Drain a cluster#
Taking a data cluster out of rotation is a five-step workflow: preview impact → drain → watch progress → wait for deregister-ready → remove from env. Strata exposes each step through the admin API and the operator console so you can drive the workflow from the browser or from a shell script.
This page is the day-2 runbook. The Drain & rebalance concept covers the lifecycle states; the Placement + rebalance best-practice covers tuning the rebalance throughput and the cluster-state machine in depth.
Lifecycle at a glance#
stateDiagram-v2
[*] --> live: in service
live --> draining_readonly: drain mode=readonly
live --> evacuating: drain mode=evacuate
draining_readonly --> evacuating: upgrade
draining_readonly --> live: undrain
evacuating --> live: undrain
evacuating --> removed: deregister (after deregister_ready)
removed --> [*]Two drain modes:
| Mode | State | Moves bytes | When to use |
|---|---|---|---|
readonly | draining_readonly | No | Short maintenance window. Stops new writes; reads + deletes + in-flight multipart keep working. |
evacuate | evacuating | Yes | Permanent decommission. Rebalance worker migrates every chunk off the cluster to its bucket’s policy targets. |
You can upgrade a readonly drain to evacuate without first
returning to live. Going back to live from either drain state
cancels the drain and re-enables the cluster.
Step 1 — Preview impact#
Before flipping the drain, ask Strata which buckets sit on the cluster:
curl -s http://strata/admin/v1/clusters/oldc/bucket-references | jq .The response lists every bucket whose Placement policy names oldc
plus its current chunk count and byte usage. Look for:
- Buckets that pin
oldcexclusively. Their PUTs will refuse with503 DrainRefusedonce the drain starts. Update the placement policy to a peer cluster first, or accept the refusal during the drain window. - Buckets in strict placement mode whose policy is
oldcalone. These will not auto-fallback to the cluster-weights wheel and the drain will mark them asstuck_single_policy. Flip to weighted mode or update the policy before draining.
The operator console exposes the same data on the cluster card under Bucket impact → Show details.
Step 2 — Drain#
Flip the cluster into the drain state you want:
# Maintenance window — stop writes, do not move data.
curl -X POST http://strata/admin/v1/clusters/oldc/drain \
-H 'Content-Type: application/json' \
-d '{"mode": "readonly"}'
# Decommission — stop writes AND migrate bytes off.
curl -X POST http://strata/admin/v1/clusters/oldc/drain \
-H 'Content-Type: application/json' \
-d '{"mode": "evacuate"}'The admin handler:
- Writes the new state into the
cluster_statetable. - Invalidates the in-process drain cache on the local replica so the flip takes effect immediately.
- Stamps an
admin:DrainClusterrow in the audit log.
Other replicas pick up the change on their next 30 s drain-cache
refresh, so the drain is fully active cluster-wide within 30 s of the
first call. For zero-window propagation, hit each replica’s
/drain endpoint directly (the calls are idempotent).
From this moment:
- PUTs that would have landed on
oldcreturn503 DrainRefusedwithRetry-After: 300. The metricstrata_putchunks_refused_total{reason="drain_refused",cluster="oldc"}ticks per refusal. - Reads, deletes, HEAD, multipart
UploadPart/Complete/Abort, andListObjectskeep working againstoldc. Drain is stop-write, not stop-read. - In-flight multipart sessions that started before the drain finish
on
oldc— the upload handle pins its initial cluster.
Step 3 — Watch progress#
For evacuate drains, the rebalance worker starts scanning the
cluster and migrating chunks the moment the state flips. Watch
progress from the console or the API.
Operator console. The cluster card flips to the evacuating state
and shows three live chips:
- Bytes moved / total — current progress against the scan’s baseline byte count.
- ETA — estimated time remaining, derived from the rolling migration rate.
- Bandwidth — current chunk-move throughput in MiB/s, capped by
STRATA_REBALANCE_RATE_MB_S(default 100).
A <DrainProgressBar> visualises bytes moved; a per-bucket breakdown
table shows which buckets are still draining.
API. GET /admin/v1/clusters/{id}/drain-progress returns a JSON
snapshot:
curl -s http://strata/admin/v1/clusters/oldc/drain-progress | jq .The payload carries migratable_chunks, stuck_single_policy_chunks,
stuck_no_policy_chunks, total bytes, baseline bytes, the last scan
timestamp, and a per-bucket breakdown. Use it for scripted drains or
custom dashboards.
Metrics. The operator-facing shortlist:
| Metric | Watch for |
|---|---|
strata_rebalance_chunks_moved_total{from="oldc"} | Counter increase = migration making progress. Plateau = drain stalled or complete. |
strata_rebalance_refused_total{reason,target} | Non-zero on target_full / target_draining = peer clusters cannot accept. Investigate before continuing. |
strata_drain_complete_total{cluster="oldc"} | Fires exactly once when chunks on cluster reach zero. |
strata_putchunks_refused_total{reason="drain_refused"} | PUT-side stop-write counter. Steady = clients still trying to write to the drained cluster; update their placement policy. |
If progress stalls (chunks_moved_total flat for ≥ 5 min while
migratable_chunks > 0), check the
Placement + rebalance troubleshooting
page for CAS-conflict storms and target-full refusals.
Step 4 — Wait for deregister_ready#
The drain is safe to deregister only when the API flips
deregister_ready=true. This requires three independent conditions
to clear:
- Chunks on cluster = 0. The rebalance scan reports zero migratable chunks remaining.
- No pending GC entries target the drained cluster. The GC queue
drains chunk-shape entries whose
clustermatches the drained id; until those drain, the chunks still exist physically. - No in-flight multipart sessions are pinned to the drained
cluster. Pre-drain uploads finish on their original cluster, so
deregister_readywaits for them.
Watch the flag in the cluster card or via:
curl -s http://strata/admin/v1/clusters/oldc | jq '.deregister_ready, .blocked_reasons'blocked_reasons enumerates which of the three preconditions is
still pending — chunks_remaining, gc_entries_remaining,
multipart_uploads_remaining. The console renders one chip per
reason next to a ✓ Ready to deregister confirmation chip.
A drain.complete audit row fires the moment chunks on cluster reach
zero; pair the alert with the strata_drain_complete_total{cluster}
counter if you want to be paged.
Step 5 — Deregister and remove from env#
Once deregister_ready=true:
- Drop the cluster id from
STRATA_RADOS_CLUSTERS(orSTRATA_S3_CLUSTERSfor S3 data backends) in the gateway env. - Roll the gateway replicas. The next boot reconcile walks the
cluster_statetable; clusters absent from env that are not alreadyremovedtransition toremoved. - Verify.
GET /admin/v1/clustersno longer listsoldc. The admin audit log has acluster_state.removedrow.
You can also tear down the cluster’s physical resources (RADOS pool, Ceph OSDs, S3 bucket) — Strata holds no more references to it.
Aborting a drain#
If you change your mind mid-drain (operator picked the wrong cluster, peer clusters are over-pressure, business priorities shifted), undrain the cluster:
curl -X POST http://strata/admin/v1/clusters/oldc/undrainThe state machine flips:
draining_readonly → livecleanly; the cluster is back in the weight wheel with its original weight.evacuating → livecancels future moves but migrated chunks stay on their new target. There is no reverse migration. Re-draining later starts from the post-undrain state, not the original layout.
The undrain handler invalidates the local drain cache and audits
admin:UndrainCluster. Other replicas pick up the flip within 30 s.
Safety rails#
The rebalance worker refuses moves into clusters that are themselves
draining or above 90 % utilisation (RADOS only — ceph df reads the
target’s free space before each move). Refusals bump
strata_rebalance_refused_total{reason,target} and surface in the
console as a banner on the cluster card. Two scenarios you may see:
reason="target_draining"— you started a second drain before the first completed and the worker tried to land bytes on the second draining cluster. Order drains strictly: drain → wait for deregister-ready → deregister → start the next drain.reason="target_full"— peer clusters are above the 90 % utilisation threshold. Add capacity (more OSDs / a new cluster) before continuing.
See also#
- Drain & rebalance concept — lifecycle states, drain refusal semantics, two drain modes.
- Placement + rebalance best practices — knob tuning, troubleshooting, the full safety-rail matrix.
- Monitoring — the rebalance and refusal metrics.
- Architecture — the drain pipeline implementation.