mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-30 08:49:26 +00:00
133 lines
8.2 KiB
Markdown
133 lines
8.2 KiB
Markdown
# Rebalance and Decommission Post-Remediation Review
|
|
|
|
> **Status:** Updated for branch `cxymds/rebalance-decommission-remediation`.
|
|
>
|
|
> **Scope:** Rebalance, decommission, shared data-movement helpers, source cleanup,
|
|
> delete-marker handling, lifecycle expiry, restart/cancel recovery, and CI coverage.
|
|
>
|
|
> **Conclusion:** The main code-level P1/P2 remediation set has been implemented on
|
|
> this branch. The remaining release gate is verification, not another known
|
|
> correctness rewrite: full CI must pass, `make pre-commit` must pass before the
|
|
> final PR state, and the data-movement e2e proof added to CI must complete in the
|
|
> GitHub runner environment.
|
|
|
|
## Review Corrections
|
|
|
|
The previous report needed two important corrections.
|
|
|
|
First, the R28 decommission permit issue is real but was overstated. The worker
|
|
permit was released before entry migration work, so `Workers` did not account for
|
|
entry lifetime and `wk.wait()` could not prove entry work had drained. However,
|
|
the current listing path awaits entry callbacks inline and bucket processing is
|
|
bounded, so the old "unbounded object movement" wording was not supported by the
|
|
code. The fix remains correct because permit lifetime now matches entry lifetime
|
|
and prevents future spawn-based regressions.
|
|
|
|
Second, SSE-C migration is related to raw data movement but has a distinct
|
|
failure mode. Before the raw migration path, SSE-C objects did not silently
|
|
corrupt through an empty `HeaderMap`; migration reads failed while resolving
|
|
SSE-C material because the customer SSE-C headers were missing. The raw internal
|
|
read path avoids requiring request customer headers for data movement.
|
|
|
|
## Implemented Remediation
|
|
|
|
| Area | Current Branch State | Representative Guards |
|
|
| --- | --- | --- |
|
|
| Raw migration reads | Rebalance and decommission use raw data-movement read options instead of normal transformed GET semantics. | `test_rebalance_object_migration_read_opts_are_raw_data_movement`, `test_decommission_object_migration_read_opts_are_raw_data_movement` |
|
|
| Raw write invariants | Data-movement writes preserve source version, ETag, tags, expires, part metadata, and checksums consistently. Single-part raw ETag validation no longer rejects preserved source ETags. | `test_data_movement_put_object_opts_preserves_version_and_etag`, `test_data_movement_opts_preserve_tags_and_expires`, `test_data_movement_single_part_raw_reader_does_not_validate_source_etag` |
|
|
| Terminal decommission recovery | Cancel/failed/complete paths cancel in-memory workers before terminal persistence, and terminal-save failure no longer leaves ghost cancelers. | decommission canceler unit tests in `crates/ecstore/src/pools.rs` |
|
|
| Rebalance operation identity | Rebalance merge/save/stop paths are id-aware and do not let stale operation state clobber a newer operation. | rebalance id-gate and merge tests |
|
|
| Start coordination | Rebalance and decommission starts cross-check persisted peer operation state before starting conflicting work. | start cross-check tests |
|
|
| Decommission worker accounting | Entry permits are held through entry completion. R28 is no longer a pending worker-accounting bug. | decommission worker permit tests |
|
|
| Rebalance rollback | Failed start rollback finalizes metadata instead of leaving `Started` plus `stopping=true` without a worker. | rollback tests |
|
|
| Decommission progress save | Periodic progress-save errors are best-effort; terminal saves remain strict. | progress-save tests |
|
|
| Queue semantics | Multi-pool decommission is queued, local-leader prefix scheduling is supported, completed restart is rejected, completed queue prefixes can advance, failed/canceled entries block automatic promotion, failed/canceled retry preserves bucket progress, and promoted queued pools are canceled if cancellation arrives before work starts. | queue/promotion/retry tests in `pools.rs` |
|
|
| Admin status and query hardening | Pool admin status exposes queued/progress state, dangerous pool/rebalance mutation queries reject unknown or ambiguous parameters, and pool status `by-id` parsing no longer falls back to pool 0. | `admin_pool_list_item`, `pools_handler_tests`, `rebalance_handler_tests` |
|
|
| Cancel/clear operations | Non-leader cancel intent is accepted and failed/canceled terminal decommission can be explicitly cleared. | remote cancel and clear tests |
|
|
| Resume equivalence | Delete-marker replication state, tiered-object metadata, multipart part numbers, tags, expires, and version counts are covered more strictly. | data-movement equivalence tests |
|
|
| Lifecycle expiry | Data movement no longer treats failed lifecycle expiry application as a successful skip. | `resolve_data_movement_lifecycle_expiry_result_rejects_apply_failure` |
|
|
| CI coverage | Existing RustFS e2e delete-marker migration proof is wired into the `e2e-tests` CI job with `--test-threads=1`, reusing the downloaded debug binary. | `.github/workflows/ci.yml` |
|
|
|
|
## Compatibility Decisions
|
|
|
|
The current RustFS decommission contract is no longer single-pool only. Multi-pool
|
|
requests are supported as queued operations on multi-pool deployments. The
|
|
current behavior is documented in
|
|
`docs/architecture/decommission-compatibility.md`.
|
|
|
|
Delete-marker behavior is intentionally characterized rather than guessed:
|
|
|
|
- a lone delete marker without replication is cleanup-only metadata and can be
|
|
skipped;
|
|
- delete markers with replication metadata remain eligible for movement;
|
|
- rebalance and decommission share the same predicate.
|
|
|
|
Lifecycle-expired versions are also explicit:
|
|
|
|
- decommission can count safely expired versions toward source cleanup only when
|
|
lifecycle expiry application succeeds;
|
|
- rebalance remains stricter and requires actual data-movement completion for
|
|
cleanup.
|
|
|
|
## Remaining Risks and Gates
|
|
|
|
There are no remaining known P1/P2 code changes in this remediation set, but the
|
|
branch is not final-release-ready until verification completes:
|
|
|
|
1. `make pre-commit` must pass before the PR is marked ready.
|
|
2. GitHub CI must pass, including the newly wired delete-marker migration e2e
|
|
proof.
|
|
3. Local full e2e verification was attempted but the machine ran out of disk
|
|
space while building `rustfs`; this is an environment blocker, not a test
|
|
assertion failure. The `target/` directory was cleaned afterward.
|
|
4. If product requires stronger end-to-end proof for encrypted and compressed
|
|
migration, add those scenarios to `crates/e2e_test` before release. Current
|
|
branch coverage is strongest at the internal option/invariant layer, with CI
|
|
e2e coverage focused on versioning/delete-marker semantics.
|
|
|
|
## Focused Verification Run During Implementation
|
|
|
|
Focused checks were run per task, including:
|
|
|
|
```bash
|
|
cargo test -p rustfs-ecstore data_movement_single_part_raw_reader --lib
|
|
cargo test -p rustfs-ecstore test_merge_rebalance_meta_preserves_stopping_stop_snapshot --lib
|
|
cargo test -p rustfs-ecstore test_merge_rebalance_pool_stats_clears_stopping_for_terminal_status --lib
|
|
cargo test -p rustfs-ecstore first_resumable_decommission_queue_indices --lib
|
|
cargo test -p rustfs-ecstore test_return_resumable_pools_skips_failed_decommission --lib
|
|
cargo test -p rustfs-ecstore resolve_data_movement_lifecycle_expiry_result --lib
|
|
cargo test -p rustfs-ecstore lifecycle_action_removes_data_movement_version --lib
|
|
cargo test -p rustfs-ecstore test_pool_meta_promoted_queued_decommission_can_be_canceled --lib
|
|
cargo test -p rustfs admin_pool_list_item --lib
|
|
cargo test -p rustfs pools_handler_tests --lib
|
|
cargo test -p rustfs rebalance_handler_tests --lib
|
|
cargo test -p rustfs admin_query_pool_status_by_id --lib
|
|
cargo fmt --all --check
|
|
```
|
|
|
|
The attempted local e2e command was:
|
|
|
|
```bash
|
|
cargo test -p e2e_test delete_marker_migration_semantics -- --nocapture
|
|
```
|
|
|
|
It failed because the local filesystem reached `No space left on device` while
|
|
building the RustFS debug binary. CI now runs the proof in the e2e job after the
|
|
debug binary artifact has been downloaded, and uses `--test-threads=1` to avoid
|
|
parallel test-server startup for that proof.
|
|
|
|
## Final PR Gate
|
|
|
|
Before marking the draft PR ready:
|
|
|
|
```bash
|
|
cargo fmt --all
|
|
cargo fmt --all --check
|
|
make pre-commit
|
|
```
|
|
|
|
If CI reports failures after the branch is pushed, inspect the failing job logs
|
|
instead of weakening the e2e gate. The delete-marker migration proof was added
|
|
because skipping it would leave the exact versioning regression class invisible
|
|
to CI.
|