Reference · 09

Staged deployment.

Prepare a new version in the background, switch to it, then clean up.

A plain deploy rebuilds all derived values at once. A staged deploy builds the new version in the background while the old one keeps serving, then switches when you say so. Nothing activates automatically.

Staged deployment API

APIContract
StagedDeploymentState{requestId, phase, baseRevision, baseBundleHash: string|null, bundleHash, cursor: string|null, scannedRows, builtEntries, generation: string|null, graphCursor: string|null, rebuiltRoots, error?: string|null, cleanupCursor: string|null}. Survives restarts and partition moves. Counters show work done, not a percentage.
StagedDeploymentAction{operation: "advance"|"collect", requestId, maxBytes?: number} or {operation: "activate"|"cancel", requestId}. Use the job's original request ID. maxBytes caps the work per call; raise it if a single row doesn't fit.
client.stageDeployment(bundle, options?)Start a staged job (admin token). Returns QueryResult<StagedDeploymentState>. One unfinished job per database. Keep the requestId for retries.
client.stagedDeploymentStatus(options?)Current job state, or null if none. Check it after a restart or an uncertain response before your next step.
client.controlStagedDeployment(action, options?)advance does the next page of work. activate switches bundle, indexes, aliases, authorization, keys and derived values at once. cancel abandons the job. collect deletes leftover state in pages. Aborting the HTTP request doesn't undo committed work.

Phases, resume and failure

  1. backfill: advance fills new indexes page by page. Code-only changes skip to rebuilding.
  2. rebuilding: advance recomputes derived values in a hidden copy. Public calls still use the old version.
  3. ready: the new version is complete and kept up to date by normal writes.
  4. active: activate switched everything at once.
  5. canceled / collected: cancel keeps the old version. collect cleans up. Collect before starting another job. To undo an activation, deploy again.
  • Only advance backfill/rebuilding, activate ready, and collect active/canceled.
  • After an uncertain response, check status and confirm requestId and bundleHash before resuming. A repeated advance may do the next page rather than repeat the last one.
  • A failed page or activation keeps earlier progress. Fix the budget and retry, or cancel and collect.
  • If the new code fails while handling a normal write, the job becomes failed with an error; the write itself still commits. Cancel and collect, then stage fixed code.
  • While a cross-group transaction is prepared, controls return TRANSACTION_PREPARED.
  • After cleanup, status shows the last collected job instead of null.
  • Don't retire the job's retry epoch or session before activating it; see retry retention.

Example: restart-aware lifecycle.

Page size and limits

  • FLOWER_DEPLOYMENT_PAGE_MS: target time per page. Default 200, at most FLOWER_EVALUATION_TIMEOUT_MS. Set at startup. Smaller pages mean shorter write stalls; larger pages finish sooner.
  • Each page blocks writes to that database while it runs; reads continue. A page can overrun the target by up to one full evaluation.
  • maxBytes separately caps the work per call.
  • Each derived value, with its dependencies, must fit in one evaluation.
  • The staged copy uses extra storage and adds work to each write until you collect.

Measure rebuild time and write latency on your own workload. See the tuning guide.

Migrate stored records

Deploys never rewrite stored records. To change their shape, write migration mutations:

  1. Deploy code that reads both old and new shapes, and make writers emit the new shape.
  2. Migrate in batches with ctx.scan(rows, {gt: afterKey, limit: 100}) (omit gt at first). Save the converted rows and the next cursor in the same mutation.
  3. Reuse the batch's request ID if a response is lost. Record completion when a page is empty.
  4. Check that no old records remain before deploying code that drops the old shape.
  • Avoid unbounded scans and growing offsets. Index ranges (ctx.range) can skip or repeat rows that change during the migration.
  • Renaming a collection or derived value doesn't move its data. Copy it explicitly.
  • Protect migration methods with application authorization. The admin token doesn't protect public mutations.
  • Moving a partition doesn't change record shapes.

See the migration workflow, range contracts and mutation context.