Qirava Cloud docs
Vertical scaling: live capacity growth
Vertical scaling grows or shrinks a single tenant's per-instance capacity — its CPU threads, memory, and storage — without recreating the tenant or taking it offline. In Qirava Cloud this is the cloud.scale_vertical orchestration function: it writes the new cap onto the tenant's _cp_tenants row, adjusts the host node's allocation counters by the delta, and returns the intended live effect. The control plane persists the new state durably (real); the infra step of signalling the running tenant DMS to re-read its cap is SIMULATED today and surfaced to the operator as a PLANNED note, never faked as done.
What vertical scaling changes#
A tenant has a per-instance resource cap: how many CPU threads it may use, how much memory, and how much storage. Vertical scaling sets a NEW absolute cap (not a delta) for that single instance. It is the right tool when one tenant simply needs a bigger or smaller box — more concurrency, more working memory, or more disk — and you are not changing how many copies of the tenant run. (Changing the number of copies is horizontal scaling, and only applies in cluster mode.)
- threads
- CPU thread budget for the instance. The executor (qexec) re-reads this to expand or shrink its concurrency budget live.
- mem_gb
- Memory cap in whole gigabytes.
- storage_gb
- Storage cap in whole gigabytes.
- per-instance
- The cap is per running copy. For a cluster tenant the node allocation is charged once per replica (cap x replicas), but the cap value itself is the size of ONE instance.
Who may call it#
cloud.scale_vertical requires cloud role admin or higher. Cloud governance is its own authority domain (cloud custodian > admin > user), entirely separate from any tenant's own governance. The check is deny-by-default: a missing or unknown role ranks 0 and is denied with AccessDenied. The role is read from ctx.role, which the Cloud Console session injects; there is no top-level role field a caller could spoof.
| Role | Rank | May scale vertically? |
|---|---|---|
| custodian | 3 | Yes |
| admin | 2 | Yes |
| user | 1 | No (AccessDenied) |
| (none / unknown) | 0 | No (AccessDenied) |
Inputs and clamps#
The function takes the tenant id plus the new absolute cap. Any of the three cap fields may be omitted; an omitted field keeps the tenant's current value as its default. Each numeric field is parsed (integers, or numeric strings from a form POST) and CLAMPED to a safe range so junk input can never become a wild allocation.
| Field | Required | Default if omitted | Clamp (min..max) |
|---|---|---|---|
| id | yes | — | must name an existing, non-terminated tenant |
| threads | no | current threads | 1 .. 256 |
| mem_gb | no | current mem_gb | 1 .. 1024 |
| storage_gb | no | current storage_gb | 1 .. 100000 |
Validation rejects two cases before any write: the tenant id must exist (InvalidInput: no tenant '<id>'), and the tenant must not be terminated (InvalidInput: tenant is terminated). A terminated tenant has had its allocation released and cannot be resized.
What it does, step by step#
- Decode the request and re-check
ctx.role>= admin (deny-by-default). - Load the tenant's
_cp_tenantsrow; reject if absent or terminated. - Read the OLD cap (threads, mem_gb, storage_gb) and the replica count (at least 1).
- Compute the NEW cap from the input fields, applying defaults + clamps.
- UPDATE the tenant row to the new cap via the write funnel.
- Apply the node allocation delta: (new - old) x replicas for each of threads, mem, storage, against the tenant's placed node. Node alloc counters are clamped at 0.
- Write a
_cp_auditrow (operator, action=scale_vertical, tenant, node, result=ok). - Return the new tenant state plus the PLANNED note describing the live re-read.
console form (admin)
POST /cloud/tenants (id, threads, mem_gb, storage_gb, _csrf)
│ session -> ctx.{user,role}; CSRF verify
▼
cloud.scale_vertical (re-checks ctx.role >= admin)
│ REAL: UPDATE _cp_tenants SET cap...
│ REAL: node alloc_* += (new - old) x replicas (clamped >= 0)
│ REAL: INSERT _cp_audit { action: scale_vertical }
▼
SIMULATED (PLANNED): signal the running DMS to re-read its cap LIVE
(expand qexec budget / storage; no rebuild, no downtime)Worked example#
A standalone tenant acme was provisioned at 2 threads / 4 GB / 20 GB and placed on a node. We grow it to 8 threads / 16 GB / 50 GB. The call below is exactly how the Cloud Console drives it: a cloud.* function invoked through the runtime with the operator's role in ctx.
Request
// request record passed to cloud.scale_vertical
{
ctx: { user: "op", role: "admin" },
id: "acme",
threads: 8,
mem_gb: 16,
storage_gb: 50
}Response
// FunctionResponse: code = Ok, data = encoded record
{
tenant_id: "acme",
simulated: "PLANNED: signal the running DMS to re-read its cap LIVE (2->8 thr, 4->16 GB mem, 20->50 GB storage) — expand qexec budget / storage, no rebuild or downtime",
status: "active",
mode: "standalone",
threads: 8,
mem_gb: 16,
storage_gb: 50,
replicas: 1,
node_id: "node-eu-1"
}Output
After the call the persisted rows reflect the new cap:
_cp_tenants(acme): threads=8, mem_gb=16, storage_gb=50
_cp_nodes(node-eu-1): alloc_threads bumped by (8-2)x1 = +6 (now 8 for this tenant)
The Console renders the `simulated` string as a PLANNED badge in the success flash.Console usage#
In the Cloud Console, vertical scaling is a form on the Tenants screen (/cloud/tenants, admin+). The form POSTs id + the three cap fields (as strings) to the cloud.action.scale_vertical handler. That handler resolves the session, verifies the CSRF token, does a cheap client-side rank check, then calls cloud.scale_vertical WITH the operator's role in ctx — the function re-checks the role server-side regardless. On success it redirects back with a ?flash= carrying the success message and the PLANNED note; on failure, a ?error= with the function's message.
Vertical vs migration#
Vertical scaling here means resizing the cap on the SAME node — the node already has headroom and the live instance simply re-reads a larger budget. When a tenant needs to move to a bigger server entirely (the node lacks headroom), that is a migration: a new node is brought up, synced live, and cut over via the FIFO-hold cutover. Both are governed by the one replication mechanism described in Scaling, migration & upgrades (the mechanism page). The control-plane function modeled here covers the same-node cap change; migration placement is the placer's job when headroom runs out.