Commit Graph

2562 Commits

Author SHA1 Message Date
cxymds 1be636b914 fix(replication): make resync recovery resilient (#5883)
Co-authored-by: houseme <housemecn@gmail.com>
2026-08-09 19:42:06 +08:00
唐小鸭 73e4ef4dd4 feat(replication): replicate managed-SSE objects via target re-encryption (#5885)
Open the managed-SSE replication gate (backlog#1783, PR-B of 3, after
#5872): the replication reader already decrypts through the injected
object-encryption resolver, so the source sends plaintext plus an
encryption intent header (AES256 / aws:kms, never the source key id) and
the target re-encrypts on its normal PUT path with its own KMS. No DEK
crosses sites.

- replication_put_object_options: fail closed only on Unsupported;
  insert the SSE intent after the strip loop.
- TargetClient::create_multipart_upload sends the full opts.header()
  set, fixing multipart replicas losing content-type/user metadata
  (plaintext included).
- Preserve source ETag and mtime on replicas (authorized replication
  only): receiver wires x-rustfs-source-etag into preserve_etag for PUT
  and CompleteMultipartUpload, resolve_complete_etag consumes it, and
  complete options carry source_etag/source_mtime (absent mtime
  degrades to epoch, not now_utc). Without this every replication HEAD
  comparison re-drives re-encrypted objects forever.
- e2e: managed SSE contracts flip to success on an independent-KMS
  dual-process pair (byte-identical plain GET proves target-owned
  envelopes; ETag/mtime preserved; version stable across scanner
  cycles; resync converges; multipart keeps structure and metadata);
  new target-without-KMS fail-closed contract; SSE-C stays FAILED.

Co-authored-by: houseme <housemecn@gmail.com>
2026-08-09 10:21:35 +00:00
houseme a71726ef49 perf(get): reduce response write allocations (#5890)
Avoid cloning cache-served GET bodies, preserve downstream vectored writes through the GET close-detection wrapper, and remove per-stripe EC decode sidecar allocations.

Co-authored-by: heihutu <heihutu@gmail.com>
Co-authored-by: zhi22915 <qiuzgang@gmail.com>
2026-08-09 08:58:41 +00:00
houseme 27ecdb88b1 fix(admin): allow owner service account updates (#5889)
* fix(admin): allow owner service account updates

* test(admin): cover console admin update scope

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: ccccpj <ccccpj@outlook.com>
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-09 08:46:27 +00:00
houseme 2c7d0fb2ce feat: add hotpath observability for S3 data paths (#5860) 2026-08-09 08:36:58 +00:00
Zhengchao An 255f3395bc fix(ecstore): rename stale two_set_test_sets references to make_local_two_set_sets (#5886) 2026-08-09 07:56:51 +00:00
Zhengchao An a07ad4a9ff test(replication): cover rule id byte limit (#5873) 2026-08-09 14:48:44 +08:00
唐小鸭 c619d8f2d6 fix(replication): persist REPLICA status on inbound replication writes (#5878) 2026-08-09 14:10:34 +08:00
Zhengchao An 6ce0961780 fix(policy): accept object lock mode condition (#5874) 2026-08-09 14:10:25 +08:00
terem42 578d02977e fix(heal): log the number of drives actually healed, not the drives consulted (#5871) 2026-08-09 14:10:11 +08:00
terem42 9c1c44807d fix(admin): answer background-heal/status partially when peers are unreachable (#5862) 2026-08-09 14:09:34 +08:00
GatewayJ 70deb3284b fix(select): pin object snapshot for query lifetime (#5835) 2026-08-09 14:08:53 +08:00
cxymds 47369ff027 fix(heal): defer scoped repair on suspended pools (#5876) 2026-08-09 11:50:17 +08:00
唐小鸭 10c7476883 fix(replication): rebuild SSE metadata boundary for encrypted objects (#5872)
Groundwork for encrypted-object replication (backlog#1783, PR-A of 3):

- classify_replication_source_encryption: accept the AES256 marker that
  every stored SSE-C object carries; the SseC arm was unreachable.
- Fail closed on sealed material without an SSE marker (MinIO-written
  objects) instead of replicating ciphertext as plaintext.
- Replace the dead VALID_SSE_REPLICATION_HEADERS table with a transport
  map keyed by the metadata keys the SSE writer actually persists, shared
  via the new rustfs_utils::http::object_encryption_keys module.
- Structurally strip all encryption metadata from outbound replication
  (x-rustfs-encryption-* envelopes previously passed the filters).
- Skip decrypt_checksums for encrypted objects at the boundary so its
  is_multipart=false (a response-path contract) cannot misroute
  encrypted multipart objects once managed replication opens.
- Redact X-Rustfs-Replication-* SSE transport values in FileInfo Debug.

A reconciliation test pins that every key encryption_material_to_metadata
produces is either transport-mapped or stripped. All four SSE replication
e2e contracts still assert FAILED unchanged.
2026-08-09 03:05:11 +00:00
Heracles 9996d567d9 fix(build): support non-Linux Unix targets (illumos/Solaris/*BSD) (#5853)
* fix(build): support non-Linux Unix targets (illumos/Solaris/*BSD)

Two independent build-infrastructure blockers kept RustFS from building on
non-Linux Unix platforms. Neither touches runtime logic.

1. pulsar regenerates its protobuf bindings in build.rs on every build, which
   needs `protoc`. Platforms without a packaged protoc (illumos/Solaris/*BSD)
   now enable pulsar's `protobuf-src` feature via a cfg-gated dependency, which
   builds a vendored protoc from C++ sources. Mainstream targets keep the lean
   dependency and their existing system/CI protoc.

2. clocksource 0.8.3 (pulled in transitively by ratelimit 0.10) used the
   Linux-only `CLOCK_MONOTONIC_COARSE`. ratelimit 2.0 dropped the clocksource
   dependency entirely, so upgrading removes the portability problem at the
   root rather than patching clocksource. The bandwidth throttle's bulk
   `consume()` is rewritten onto ratelimit 2.0's `try_wait_n`, preserving the
   best-effort partial-consumption semantics.

Verified: cargo check + bandwidth monitor unit tests pass; cargo tree confirms
protobuf-src is enabled only for illumos/Solaris/*BSD and clocksource is gone
from the graph. The final illumos build must be confirmed on-platform.

Closes #3195

* fix(ecstore): guard ratelimit v2 capacity overflow

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ecstore): avoid slow bandwidth reader timeout

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(targets): drop vendored pulsar protobuf build

Co-Authored-By: heihutu <heihutu@gmail.com>

---------
Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-09 10:07:00 +08:00
cxymds 3b9c67e79b fix(rpc): authenticate internode put file bodies (#5868) 2026-08-09 08:05:16 +08:00
cxymds d36166ffb5 fix(ecstore): bound decommission listing retries (#5861) 2026-08-09 08:05:12 +08:00
cxymds 963a107b33 fix(ecstore): fence bucket memo on live lock loss (#5852) 2026-08-09 08:00:46 +08:00
cxymds b4133d69e6 fix(heal): respect scoped object repair limits (#5855) 2026-08-09 07:23:16 +08:00
houseme 134081b27b chore(deps): fix cargo shear dependency metadata (#5854)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-08 15:52:00 +00:00
cxymds 65e55c0f8e fix(rebalance): fence batch delete source pools (#5846) 2026-08-08 21:14:26 +08:00
cxymds 8c75a3834a fix(rebalance): fence writer pool lookups (#5845) 2026-08-08 21:14:11 +08:00
houseme f96346124e fix(multipart): recover part transactions by write quorum (#5844)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-08 20:22:51 +08:00
cxymds a7de957eb8 fix(rebalance): fence peers before activation (#5842) 2026-08-08 11:55:50 +00:00
houseme c2e23411e8 test(filemeta): cover crc heal classification (#5841)
* fix(filemeta): classify xl.meta CRC mismatch as FileCorrupt so heal repairs it

A failed CRC means the metadata bytes on disk are not the bytes that were
written — bitrot. Raising it as Error::other() surfaces a generic Io error,
which should_heal_object_on_disk does not recognise as heal-worthy: the drive
is skipped, disks_to_heal_count stays 0, heal_object returns ok, and the
corrupted xl.meta is never rewritten — while the scanner re-submits the same
no-op heal every deep-scan cycle. An explicit admin deep heal fails the same
way, so no heal path repairs metadata bitrot, and every one of them reports
success.

check_xl2_v1 already classifies a short or wrong-magic header as FileCorrupt
for exactly this reason (#5716); this completes the pattern for the two CRC
sites. The existing From<rustfs_filemeta::Error> for DiskError conversion maps
the variant to DiskError::FileCorrupt, which the heal path already handles.
The previously silent is_indexed_meta site now logs the mismatch (structured
event shape) like unmarshal_msg does.

Regression test: corrupt one byte of a marshalled FileMeta and assert
unmarshal_msg reports FileCorrupt; fails on the previous code, which returned
Io(Other).

Verified end-to-end on a 3-node / 12-drive EC:4 cluster: xl.meta corrupted on
2 of 12 drives via dd, admin deep heal — before this change the heal returns
ok with the corruption intact and the scanner loops forever; with it, both
copies are rewritten (decode-identical to the healthy quorum), the object
reads back byte-correct, and a follow-up heal reports all twelve drives
clean.

* test(filemeta): cover crc heal classification

Add regression coverage for the indexed xl.meta CRC path and the metadata-heal decision that consumes FileCorrupt.

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: terem42 <9478806+terem42@users.noreply.github.com>
Co-authored-by: heihutu <heihutu@gmail.com>
Co-authored-by: zhi22915 <qiuzgang@gmail.com>
2026-08-08 11:36:44 +00:00
GatewayJ 4a234c0fe3 fix(iam): stabilize OIDC provider ordering (#5832) 2026-08-08 19:29:04 +08:00
GatewayJ 1b1b217826 fix(iam): preserve OIDC outbound policy errors (#5762) 2026-08-08 19:28:47 +08:00
terem42 7e8b500420 fix(filemeta): classify xl.meta CRC mismatch as FileCorrupt so heal repairs it (#5838)
A failed CRC means the metadata bytes on disk are not the bytes that were
written — bitrot. Raising it as Error::other() surfaces a generic Io error,
which should_heal_object_on_disk does not recognise as heal-worthy: the drive
is skipped, disks_to_heal_count stays 0, heal_object returns ok, and the
corrupted xl.meta is never rewritten — while the scanner re-submits the same
no-op heal every deep-scan cycle. An explicit admin deep heal fails the same
way, so no heal path repairs metadata bitrot, and every one of them reports
success.

check_xl2_v1 already classifies a short or wrong-magic header as FileCorrupt
for exactly this reason (#5716); this completes the pattern for the two CRC
sites. The existing From<rustfs_filemeta::Error> for DiskError conversion maps
the variant to DiskError::FileCorrupt, which the heal path already handles.
The previously silent is_indexed_meta site now logs the mismatch (structured
event shape) like unmarshal_msg does.

Regression test: corrupt one byte of a marshalled FileMeta and assert
unmarshal_msg reports FileCorrupt; fails on the previous code, which returned
Io(Other).

Verified end-to-end on a 3-node / 12-drive EC:4 cluster: xl.meta corrupted on
2 of 12 drives via dd, admin deep heal — before this change the heal returns
ok with the corruption intact and the scanner loops forever; with it, both
copies are rewritten (decode-identical to the healthy quorum), the object
reads back byte-correct, and a follow-up heal reports all twelve drives
clean.
2026-08-08 18:48:18 +08:00
houseme e342457830 perf(filemeta): reduce meta object key allocations (#5836)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-08 17:23:08 +08:00
houseme 7e9e4b67e5 fix(ecstore): raise replay cache auto capacity (#5833)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-08 14:13:34 +08:00
cxymds f5463f4aa8 fix(rebalance): defer changed source cleanup (#5829) 2026-08-08 10:45:34 +08:00
terem42 cb93ac5df1 fix(ecstore): purge the stale destination data dir on healing rename_data commits (#5822)
* fix(ecstore): purge the stale destination data dir on healing rename_data commits

Heal commits reuse the version's existing data_dir, so when repairing
in-place corruption (bitrot) the destination directory still exists and
holds the corrupt shard files. rename(2) cannot replace a non-empty
directory (EEXIST on XFS, ENOTEMPTY on ext4), so the commit failed on
every attempt — including all scheduler retries — and in-place bitrot was
detected and reconstructed but never repaired.

Purge the stale destination data dir (move_to_trash) before the commit
rename, for healing commits only: fresh PUTs mint a new data_dir and can
never collide, and a non-healing collision keeps failing loudly. Adds the
FileInfo::is_healing() reader for the marker set_healing() already writes.

* style(ecstore): emit the heal purge failure as a structured event

The new warning was the only sentence-style log in `rename_data`'s commit
path — it sat ten lines above `info!(event = EVENT_DISK_LOCAL_RENAME_REJECTED,
component = ..., subsystem = ...)` and interpolated its values into the
message instead of carrying them as fields, so it is invisible to any operator
query keyed on `event`.

Give it the shape the rest of the file uses: a named
`EVENT_DISK_LOCAL_HEAL_PURGE_FAILED`, `component`/`subsystem`, `dst_path` and
`error` as fields, and a short label as the message. Level stays `warn` — the
purge is best effort and the rename below fails closed — and the condition,
the branch, and the control flow are unchanged.

---------

Co-authored-by: Zhengchao An <anzhengchao@gmail.com>
2026-08-07 23:10:53 +00:00
Zhengchao An 96d24bc006 docs(agents): make the structured-logging rule reachable and enforceable (#5828)
The RustFS event shape (`event`/`component`/`subsystem`/`result` + context,
message last) is specified only in
`.agents/skills/rustfs-logging-governance/SKILL.md`, and nothing routes a
change to it:

- `AGENTS.md`, which is what an agent actually loads by default, never
  mentions logging. Its only related line is "log unknown fields at `warn`"
  under Serde Safety, which is about level, not shape.
- The skill's `description` says "use when editing or reviewing RustFS logs",
  so a bugfix that adds one log line in passing — how most new log sites enter
  this repo — never matches it.
- `scripts/check_logging_guardrails.sh` is a blocklist: 500+ `rg -F` literals
  that retire log lines which already shipped. It cannot see a newly written
  one. For `crates/ecstore/src/disk/local.rs` the only check is that
  `#[tracing::instrument]` is TRACE-only; `warn!`/`info!` shape is unchecked.

PR #5822 landed `warn!("heal rename_data: purging ... {:?} failed: {}", ...)`
in `disk/local.rs` — sentence-style, no fields, directly beside `info!(event =
EVENT_DISK_LOCAL_RENAME_REJECTED, component = ..., subsystem = ...)` — with
every check green. That is the gap, not an authoring mistake.

Close all three:

- `AGENTS.md`: a Logging section stating the field shape, the level policy,
  the reuse-the-file's-constants rule, and that it applies to any `tracing`
  macro added in passing, not only to log-focused changes.
- Skill `description`: trigger on adding or editing any `tracing` macro,
  naming the single-line-added-in-passing case explicitly.
- Guardrail: assert the event shape positively on the already-governed disk
  files — `error!`/`warn!`/`info!` must open with fields or a `target:`, never
  a bare string. Commented-out macros are excluded; `debug!`/`trace!` stay out
  of scope as targeted diagnostics. Self-test fixtures cover both directions.

`crates/ecstore/src/disk/mod.rs` carried the one live violation in that file
set (`conv_part_err_to_int`), so it is converted here; the guardrail would
otherwise fail on an untouched file.

Verification:
- `./scripts/check_logging_guardrails.sh` — passes
- Negative control: re-inserting PR #5822's exact `warn!` line into
  `disk/local.rs` makes it exit 1 pointing at that line
- `cargo fmt -p rustfs-ecstore -- --check`, `cargo check -p rustfs-ecstore`
2026-08-07 23:05:57 +00:00
houseme 74c6c114b1 fix(rpc): skip batch read-version JSON for msgpack peers (#5825)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-07 23:03:22 +00:00
cxymds b301588248 fix(rebalance): drain entry tasks before listing retry (#5820)
* fix(rebalance): drain entry tasks before listing retry

* test(rebalance): satisfy clippy in retry regression
2026-08-08 05:50:47 +08:00
cxymds 41e262cdab test(e2e): wait for authoritative quota usage (#5816) 2026-08-08 05:44:40 +08:00
cxymds 23fef384ce test(e2e): align backpressure assertion with recovery (#5815) 2026-08-08 05:44:25 +08:00
cxymds d7f1ba9ae7 test(ecstore): stabilize free-version enqueue retry (#5813) 2026-08-08 05:44:10 +08:00
cxymds a4712fae81 test(e2e): make scanner snapshot tests deterministic (#5812)
* test(e2e): configure scanner snapshot timing

* style(e2e): format scanner snapshot tests
2026-08-08 05:43:56 +08:00
cxymds 8201a74f7f feat(rpc): advertise cross-pool fence capability (#5809)
* feat(rpc): advertise cross-pool fence capability

* refactor(rpc): narrow fence capability surface

* fix(rpc): state fence compatibility removal condition
2026-08-08 05:43:26 +08:00
cxymds ce7ca4cbb8 fix(policy): support version ID condition keys (#5810) 2026-08-08 05:42:52 +08:00
唐小鸭 6633c80151 refactor(kms): close the low-severity follow-ups from the #5668 adversarial re-review (#5817)
* refactor(kms): share the DEK spec mapping and stop re-parsing opened envelopes

- generate_key_material is now the single spec->length mapping for every
  backend that mints DEKs itself; the inline copies in the Static and Local
  backends are gone, and ChaCha20 (32 bytes, same as AES_256) is accepted
  uniformly instead of only by Static.
- The pub(crate) client decrypt of the Local, Vault KV2 and Vault Transit
  backends returns (plaintext, master_key_id), so KmsBackend::decrypt no
  longer re-parses the envelope it just opened (one JSON parse per SSE GET
  instead of two, and unknown-field observability is no longer double-counted).
- Malformed-envelope parse failures now report CryptographicError("parse")
  on all backends; Local was the last one mapping them to SerializationError.
- The four KmsBackend::generate_data_key adapters take fields out of
  DataKeyInfo instead of cloning, dropping a redundant un-zeroized plaintext
  DEK copy and a full ciphertext clone per call; a missing plaintext now
  fails closed everywhere instead of returning an empty key on three of four
  backends.

* test(kms): pin legacy header fallback, stored-AAD, and decrypt key-id contracts

- a_legacy_aws_kms_object_without_the_cipher_header_still_opens rebuilds the
  true pre-internal-header shape (aws:kms mode + S3 key-id header, no
  x-rustfs-* headers) and asserts the fallback normalizes the cipher and
  re-projects it.
- a_rewritten_sse_c_context_header_fails_authentication is the SSE-C flank of
  the stored-AAD tamper check; metadata_without_stored_context_bytes_still_opens
  covers the derived-AAD path for both flavours and pins the seal side to the
  canonical bytes (mutation-verified).
- data_key_spec_controls_the_length_of_the_generated_key requires every
  backend in the matrix to honour all three specs, asserts the envelope
  records the requested spec, and round-trips each blob.
- corrupt_ciphertext_fails_cleanly pins unparseable ciphertext to
  CryptographicError instead of merely not-InternalError.
- Deleted the never-called assert_validation_error / assert_cryptographic_error
  helpers.
2026-08-08 05:41:50 +08:00
Zhengchao An 027456032f fix(ecstore): enforce the NAME_MAX segment budget on the write path too (#5826)
#5804 added the on-disk segment budget to check_bucket_and_object_names, but PUT validates through check_put_object_args, which has its own checks and never calls it. An over-NAME_MAX key therefore still reached the disk layer and came back to the client as ENAMETOOLONG → InternalError 500, exactly the behavior #5785 reported.

Caught by re-running the acceptance suite against the locked build 4b2d79f5d, which contains #5804: S3-003 still failed with a 512-byte key.

Multipart is unaffected — check_new_multipart_args and check_multipart_object_args both route through check_object_args → check_bucket_and_object_names, which already carries the budget.

Verification: new test pins the same boundaries on check_put_object_args (255 ok / 256 rejected, byte-based via CJK, multi-segment long keys ok, __XLDIR__ budget for directory keys); cargo test -p rustfs-ecstore --lib -- bucket::utils 17 passed; cargo clippy -p rustfs-ecstore --all-targets clean; make pre-commit green.
2026-08-08 05:30:15 +08:00
houseme 58c49672ca perf(ecstore): cache modern erasure codec construction (#5824)
Co-authored-by: heihutu <heihutu@gmail.com>
Co-authored-by: zhi22915 <qiuzgang@gmail.com>
2026-08-07 18:52:31 +00:00
houseme aa4de7b9d6 perf(utils): avoid metadata key lowercase allocation (#5823)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-07 18:39:09 +00:00
Zhengchao An 05a5be51ce fix(scanner): retry a superseded usage snapshot in seconds, not a full cycle (#5814)
A superseded cycle is the expected outcome of the dirty-usage fast path, not a signal of pathological load: a write burst marks buckets dirty, the scanner wakes within milliseconds, and the still-landing writes then supersede the snapshot it just took. Charging that first race SUPERSEDED_RETRY_BASE_INTERVAL = 60s meant the burst surfaced in usage and quota accounting roughly two cycles late.

Measured on an idle single-node instance (fresh data dir, 10 PUTs, polling /rustfs/admin/v3/datausageinfo every 5s): the dirty-usage wake fires 0.3s after the PUTs, its cycle is superseded 0.2s later, and the retry was then scheduled 55.6s out; usage first became visible at t+120s. With the base at 5s the retry is scheduled 4.7s out and usage becomes visible at t+70s.

The exponential growth in retry_interval is what protects against a persistently hot bucket driving an unbroken full-scan loop, so the base does not need to be a whole cycle: 5s, 10s, 20s, 40s ... still reaches minute-scale backoff within a handful of consecutive supersedes and keeps the SUPERSEDED_RETRY_MAX_INTERVAL cap. A configured cycle shorter than the base still wins, since retrying faster than the operator's own cadence buys nothing.

Verification: cargo test -p rustfs-scanner --lib (444 passed) with the three superseded-backoff tests updated to the new schedule; make pre-commit green; end-to-end probe above.
2026-08-07 16:09:26 +00:00
houseme 5187f91997 fix(s3select): replace deprecated parquet reader (#5811)
Implement a local AsyncFileReader over DataFusion's object store re-export so Parquet metadata loading no longer uses the deprecated ParquetObjectReader adapter.

Co-authored-by: heihutu <heihutu@gmail.com>
Co-authored-by: zhi22915 <qiuzgang@gmail.com>
2026-08-07 16:00:58 +00:00
houseme 9d996b82a8 perf(storage): skip read-version JSON for bin peers (#5808)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-07 15:24:17 +00:00
Zhengchao An ab35681928 perf(ecstore): memoize bucket-incarnation fence validation under lifecycle read-lock coverage (#5782)
* perf(ecstore): memoize bucket-incarnation fence validation under lifecycle read-lock coverage

The PUT commit fence from #5648 validated the bucket incarnation with an uncached read (a distributed metadata-transaction read lock plus an EC quorum read of bucket metadata) on every PUT commit. Under 64-concurrency 4KiB PUT load this adds two quorum round-trips per PUT and the resulting lock-manager pressure produced ~1,000 client-visible 'Lock acquisition timeout' failures per 5-minute window (see rustfs/backlog#1776).

Memoize the validation per node while lifecycle read-lock coverage is continuous: bucket deletion/recreation requires the lifecycle WRITE lock, so while at least one read guard on this node has been held continuously the incarnation cannot have changed. The first fenced PUT in a coverage window performs the exact authoritative disk validation as before; overlapping PUTs reuse its result. The memo clears when the node's last guard drops or any guard observes a lost lock, so the next PUT revalidates from disk. Fence semantics are unchanged; only the redundant re-validations under continuous coverage are elided.

Also right-size the s3s footprint ratchet baselines: -1 s3_error! line from this change's error-path consolidation, and +1 s3s-importing file inherited from #5763 (crates/obs/src/telemetry/filter.rs) which landed on main without the baseline bump.

* fix(ecstore): carry the bucket fence registry through the rebalance test store

The rebalance entry test constructor landed on main after this branch was cut and needs the new field.
2026-08-07 15:05:48 +00:00
cxymds ba5641237c ci(e2e): stabilize full-gate tooling (#5805) 2026-08-07 15:04:33 +00:00