Commit Graph

171 Commits

Author SHA1 Message Date
Zhengchao An 710ae74cde perf: add S3 operations benchmark framework (#738) (#4005) 2026-06-28 18:02:41 +08:00
Zhengchao An 05d201679c fix: replace unwrap() with expect() in more files (#729 batch 13) (#3993) 2026-06-28 11:45:13 +08:00
Zhengchao An 5c94fe7dd7 fix(ecstore): improve expect() messages in replication_resyncer (#729 batch 7) (#3988)
fix(ecstore): improve expect() messages in replication_resyncer

Replace unwrap() with expect() for better error diagnostics in
replication_resyncer.rs:
- HashMap get_mut calls now have descriptive expect messages
- format() calls now use unwrap_or_else for error handling

Refs https://github.com/rustfs/backlog/issues/729
2026-06-28 11:22:26 +08:00
Zhengchao An 6e72dc3076 fix(ecstore): replace unsafe k.unwrap() in bucket_target_sys (#729) (#3982)
fix(ecstore): replace k.unwrap() with safe pattern in bucket_target_sys

Replace unsafe k.unwrap().as_str() with if let Some(key_str) pattern
in 5 locations where HeaderMap iterator yields (Option<HeaderName>, Value).

This prevents potential panics if header names are invalid.

Refs https://github.com/rustfs/backlog/issues/729
2026-06-28 10:42:56 +08:00
Zhengchao An 0a5b1b1b3a refactor: consolidate ecstore owner module layout (#3934)
* refactor: shrink ecstore root owner facades

* refactor: remove ecstore core store root shims

* refactor: move ecstore erasure owner modules

* refactor: remove ecstore root rpc facade

* refactor: move ecstore services domain modules
2026-06-27 09:03:20 +08:00
cxymds 080363f10f feat: harden site replication control plane (#3842) 2026-06-27 08:35:49 +08:00
Zhengchao An 27bb9c75dc refactor: move ecstore rpc metadata modules (#3933) 2026-06-27 07:19:45 +08:00
Zhengchao An b38976d5ee refactor: segment ECStore storage contracts by domain (#3910) 2026-06-26 17:47:36 +08:00
Zhengchao An c9614eb7cb refactor: route ecstore storage api boundaries (#3892) 2026-06-26 09:35:18 +08:00
Zhengchao An a935854b32 refactor: centralize scanner runtime source helpers (#3827)
* refactor: batch lifecycle runtime source handles

* refactor: centralize scanner runtime source helpers
2026-06-24 22:05:03 +08:00
houseme 623fc801f1 feat(replication): support custom TLS for bucket targets (#3825)
* feat(replication): support insecure https bucket targets

* feat(replication): support custom ca bucket targets

* test(replication): satisfy e2e clippy for TLS helpers

* fix(replication): avoid native root panic for custom trust stores

* test(replication): decouple private IP target test from TLS roots

* test(replication): use target TLS client in private IP unit test
2026-06-24 20:23:20 +08:00
Zhengchao An 3f624bf06c refactor: centralize ecstore bucket runtime sources (#3810) 2026-06-24 09:41:11 +08:00
Zhengchao An 1c04b86494 refactor: centralize ecstore bucket runtime sources (#3806) 2026-06-24 08:00:04 +08:00
Zhengchao An 5046f788be refactor: centralize ecstore lifecycle runtime sources (#3805) 2026-06-24 07:23:13 +08:00
Zhengchao An 7e60432588 refactor: centralize ecstore runtime owner sources (#3798) 2026-06-24 06:34:06 +08:00
唐小鸭 eff656e086 fix(storage): restore legacy SSE-S3 read compatibility (#3584)
* Update .gitignore

* Fix. fixed SSE-S3 compatibility issues in large-scale testing

* fix

* fix(ecstore): reject whitespace bucket names

* Update replication_extension_test.rs

* style(ecstore): format bucket whitespace test

---------

Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: cxymds <cxymds@gmail.com>
2026-06-23 21:35:17 +08:00
houseme 2f85ef4455 fix(security): extend outbound egress guard (#3744) 2026-06-22 19:20:31 +08:00
houseme 64aef06c60 fix(replication): harden site-repl versioning sync (#3739) 2026-06-22 19:18:42 +08:00
安正超 a0e7957e92 refactor: prune admin app compat re-exports (#3697) 2026-06-21 20:28:43 +08:00
安正超 d26d98104a refactor: prune ecstore root global facades (#3680) 2026-06-21 09:52:19 +08:00
安正超 0cf7e5cf03 refactor: extract embedded startup control helpers (#3661) 2026-06-20 21:57:03 +08:00
安正超 54ffbedbc8 refactor: remove ecstore operation compat facades (#3608) 2026-06-19 17:01:12 +08:00
houseme 8cf3c0bfbd fix(ecstore): support MinIO DARE fixture compatibility (#3590) 2026-06-19 10:13:39 +08:00
安正超 b1c6578df1 refactor: narrow test harness compatibility surfaces (#3592) 2026-06-19 07:10:52 +08:00
安正超 c28fee0013 refactor: continue storage api contract cleanup (#3580)
* refactor: move delete object contracts to storage api

* refactor: narrow store api compatibility exports

* refactor: route table catalog test through storage compat
2026-06-18 22:42:02 +08:00
安正超 7b0cb9e725 refactor: prune storage compatibility re-export allowances (#3579)
* refactor: prune storage compatibility re-export allowances

* fix: reject whitespace-padded dot path segments
2026-06-18 21:14:24 +08:00
安正超 57403525ee refactor: move heal and namespace contracts (#3560) 2026-06-18 11:00:21 +08:00
安正超 e5cad7ed20 refactor: move object operation contracts (#3559) 2026-06-18 09:48:13 +08:00
安正超 3fb4cb3d65 refactor: move list operations contract (#3550) 2026-06-18 08:48:18 +08:00
houseme 87a3d107d6 test(property): add path validator invariants (#3541)
test(property): add path validator invariants for #3525
2026-06-18 02:14:24 +08:00
安正超 53337a6f71 refactor: move HTTP range helper contracts (#3533) 2026-06-18 00:47:23 +08:00
安正超 919deeb816 refactor: move object option helper contracts (#3521) 2026-06-17 22:56:10 +08:00
houseme 8d24d9133b perf(put): comprehensive PUT performance optimization (#3514)
* perf(put): add eager path metrics and isolation tooling

* fix(decommission): persist progress adaptively (#3497)

Persist decommission progress after either the existing time interval or a migrated-item threshold, and flush progress baselines after bucket and terminal-state saves.

Also stabilize the OIDC discovery mock used by the pre-commit gate.

* refactor: move bucket operations contract (#3507)

* fix(s3): handle multipart flexible checksums (#3508)

* fix(io-core): avoid blocking on pooled buffer return

* perf(put): add slow inflight diagnostics

* perf(put): fix 16KiB regression with threshold and pool bypass

- Lower SMALL_EAGER_PUT_MAX_SIZE from 256KB to 8KB so objects >8KiB
  use the streaming BufReader path (matches baseline behavior)
- Add POOL_BYPASE_MAX_SIZE (16KiB) to bypass BytesPool for very small
  objects, avoiding Small-tier Mutex contention under high concurrency
- Add read_small_put_body_exact_direct() for direct Vec<u8> allocation
- Fix stale test assertions to match new 8KB threshold

Root cause analysis: the 16KiB regression was primarily caused by
instrumentation overhead in set_disk.rs (4x Instant::now() + metrics
per PUT), not BytesPool contention. Lowering the threshold eliminates
the eager-path overhead for 16KiB+ objects.

* perf(put): gate stage metrics behind observability flag

Add put_stage_metrics_enabled() AtomicBool switch in io-metrics crate.
When disabled (default), record_put_object_path() and
record_put_object_stage_duration() are no-ops, avoiding unnecessary
histogram/counter macro overhead in the PUT hot path.

The flag is set to true during startup when OTEL metric export is
enabled (rustfs_obs::observability_metric_enabled() == true).

This eliminates the per-request metrics overhead that contributed
to the 16KiB PUT regression when metrics collection is not active.

* perf(put): comprehensive optimization - restore eager path, cache env, remove UUID

Change 1: Restore SMALL_EAGER_PUT_MAX_SIZE from 8KB to 1MB
- The try_lock() fix (d13a189e3) eliminates the blocking that caused
  service health timeouts under 512KiB c64 load
- Eager path with BytesPool is now safe for objects up to 1MB
- Recovers the eager path benefit for 32KiB-256KiB objects

Change 2: Adjust POOL_BYPASE_MAX_SIZE from 16KB to 4KB
- With eager path restored to 1MB, objects 4KB-1MB benefit from pool reuse
- Only ≤4KB objects bypass the pool (allocation cost negligible)

Change 3: Cache RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES via OnceLock
- Eliminates per-encode std::env::var() syscall
- Env var still works (read once at first use)

Change 4: Replace Uuid::new_v4() with Uuid::nil() in Erasure construction
- _id field is unused in hot paths (documented in code)
- Eliminates CSPRNG syscall per PUT request

Change 5: Add concurrency-aware buffer sizing to PUT path
- Reuses get_concurrency_aware_buffer_size() from GET path
- Reduces buffer size under high concurrency (0.4x at >8 concurrent)
- Lowers memory pressure for >1MB streaming PUTs

* chore: add pyroscope feature flag and clean up imports

- Add pyroscope feature flag forwarding to rustfs-obs
- Remove unused allow(non_upper_case_globals) in globals.rs
- Sort imports and fix Cargo.toml formatting consistency

* style: fix import ordering and code formatting

- Sort imports alphabetically in globals.rs, encode.rs
- Fix indentation in erasure_coding encode/erasure
- Clean up HashReader formatting in object_usecase.rs

* fix(test): use tokio::test for request_logging_layer tests

The tests call tokio::spawn via RequestContextLayer, which requires a
Tokio runtime. Changed from #[test] + futures::executor::block_on to
#[tokio::test] + .await, and replaced tracing::subscriber::with_default
with tracing::subscriber::set_default to support async.

* fix(bench): normalize no-space throughput/latency parsing in to_bps/to_ms

When a benchmark tool prints throughput without a separator (e.g. 123MiB/s),
awk '{print $2}' returns empty because the whole string is one field,
causing to_bps to return N/A and losing valid measurements in CSV output.

Insert a space between number and unit via sed before awk field splitting.
Same fix applied to to_ms for latency values like '50ms'.

Also add TODO comment on PUT path noting that get_concurrency_aware_buffer_size
reads ACTIVE_GET_REQUESTS instead of PUT concurrency (PR #3514 review).

Refs: PR #3514 review comments by chatgpt-codex-connector

* fix(metrics): correct POOL_BYPASS comments and separate PUT vs generic stage metrics

- Fix 3 comment-code mismatches: POOL_BYPASS_MAX_SIZE is 4KiB, not 16KiB
- Add generic record_stage_duration() with separate histogram
  (rustfs_internal_stage_duration_ms) for non-PUT paths
- Replace record_put_object_stage_duration with record_stage_duration in
  metacache_set, store_list_objects, and bucket_lifecycle_ops to avoid
  polluting PUT-specific dashboards with listing/lifecycle timings
- Fix flaky test: serialize tests mutating PUT_STAGE_METRICS_ENABLED with
  METRICS_FLAG_LOCK mutex and explicitly set desired state at test start

Refs: PR #3514 review comments by chatgpt-codex-connector

* style: apply cargo fmt to metacache_set.rs

---------

Co-authored-by: cxymds <cxymds@gmail.com>
Co-authored-by: 安正超 <anzhengchao@gmail.com>
2026-06-17 21:19:11 +08:00
安正超 ed55857bca refactor: move bucket operations contract (#3507) 2026-06-17 09:10:38 +08:00
安正超 2ff69ae21c refactor: remove ecstore bucket DTO aliases (#3503) 2026-06-16 22:10:07 +08:00
cxymds ba0ba0d35e fix(lifecycle): persist remote tier delete journal tasks (#3493)
fix: persist remote tier delete journal tasks
2026-06-16 10:38:41 +08:00
cxymds 156e21c90e fix(lifecycle): make tier cleanup recoverable (#3491) 2026-06-16 08:45:49 +08:00
安正超 fbab160c2b refactor: narrow replication storage consumers (#3485) 2026-06-15 22:29:38 +08:00
安正超 5063524b36 ci: guard migration loss-prevention coverage (#3475) 2026-06-15 16:32:41 +08:00
Henry Guo dd6b4c35ad fix(scanner): harden lifecycle and tiering backlog (#3469)
* fix(scanner): expose lifecycle transition backlog

* fix(scanner): expose lifecycle expiry backlog

* fix(scanner): preserve lifecycle backlog states

---------

Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com>
2026-06-15 15:01:21 +08:00
abdullahnah92 a19560da41 fix(bucket-repl): persist MRF retry queue to disk and reload on startup (#3456)
* fix(bucket-repl): persist MRF retry queue to disk and reload on startup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(bucket-repl): address three blocking MRF issues from review

1. MRF replay loses delete operations — add `MrfOpKind` discriminator to
   `MrfReplicateEntry` (Object | Delete, default=Object for backward
   compat).  `DeletedObjectReplicationInfo::to_mrf_entry` now persists
   `op=Delete`, `version_id`, `delete_marker_version_id`, and
   `delete_marker`.  `start_mrf_processor` branches on `op`: delete
   entries skip `get_object_info` and replay via
   `schedule_replication_delete` with `ReplicationType::Heal`; object
   entries follow the existing heal path.

2. `flush_mrf_to_disk` cleared the in-memory batch even on encode/write
   failure — changed return type to `bool` and callers now only
   `pending.clear()` on `true`, so a transient storage error retries
   on the next tick instead of silently dropping the batch.

3. Add focused tests: encode/decode roundtrips for object, delete-marker,
   versioned-delete, and mixed-batch entries; a routing test confirming
   op-kind propagates correctly and that the default is Object for
   legacy files; a legacy-compat test verifying old entries round-trip
   cleanly through the new format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: fix clippy redundant-clone in MRF tests

Replace &[entry.clone()] with std::slice::from_ref(&entry) in two
encode_mrf_file call sites flagged by clippy's redundant_clone lint
under --all-targets --features rio-v2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(bucket-repl): strengthen legacy MRF compat test with hand-built msgpack

The previous mrf_legacy_file_without_op_field_decoded_as_object test
round-tripped through encode_mrf_file, so it exercised the new format
and never touched a truly-legacy payload.

Replace it with a hand-built msgpack payload that genuinely omits the
"op", "deleteMarker", and "deleteMarkerVersionID" keys — exactly what
the old binary would have written before MrfOpKind existed.  The test
now fails if #[serde(default)] is removed from the op field, which
proves real backward compatibility rather than round-trip stability.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: houseme <housemecn@gmail.com>
2026-06-15 11:56:50 +08:00
houseme 036741cb1c refactor(request-id): align contracts and lock field names (#3454)
* refactor(request-id): align header log and trace contracts

* test(contract): lock external request-id field names

* chore(deps): drop unused rustfs-ecstore links

Co-Authored-By: heihutu <heihutu@gmail.com>
2026-06-15 01:59:11 +08:00
houseme efaf07d323 feat: preserve request ids across async recovery logs (#3451)
* feat(obs): promote request ids in structured logs

* refactor(tracing): propagate spans into request tasks

* test(ecstore): baseline recovery monitor log chains

* fix(replication): reduce startup resync log noise

* chore(docs): stop tracking local recovery baseline

* chore(obs): polish request id logging cleanup
2026-06-14 23:16:04 +08:00
安正超 323302255c refactor: route ecstore internals through object resolver (#3441) 2026-06-14 19:55:21 +08:00
houseme e8012bd1ba refactor(logging): normalize admin telemetry and error messages (#3430) 2026-06-14 13:27:10 +08:00
abdullahnah92 22460243bf fix(bucket-repl): honor op_type in replicate_object so ExistingObject… (#3420)
fix(bucket-repl): honor op_type in replicate_object so ExistingObject resync respects DISABLED targets

replicate_object was calling filter_target_arns with hard-coded
op_type: Object and existing_object: false regardless of what was
stored in roi.op_type. This meant that a resync worker setting
roi.op_type = ExistingObject (resync_bucket, line 889) had no effect
on target filtering: all configured targets were included, even ones
whose rule had ExistingObjectReplicationStatus::DISABLED.

Fix: pass op_type: roi.op_type and derive existing_object from it
(true only for ExistingObject, not Heal — Heal intentionally bypasses
the existing-object opt-out to repair past failures).

Also add warn! logs at all four MRF channel-overflow sites that were
previously silently returning Missed with no observability.

Verified with a live two-instance test: after resync, objects reached
the ENABLED target and were correctly blocked from the DISABLED target.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: houseme <housemecn@gmail.com>
2026-06-14 07:14:11 +08:00
abdullahnah92 cb37a64a81 fix(site-replication): bidirectional sync, pre-existing data back-fill, and related fixes (#3401)
* fix(site-repl): clamp replication_cfg_mismatch to owning deployments and add resync status branch

Fix 3: replication_cfg_mismatch was set on ALL deployments for a bucket when
any replication-config discrepancy existed, even on deployments that simply have
no config. mc computes "in sync" as max_buckets minus per-deployment mismatch
entries; with N deployments all flagged for 1 bucket, the result underflows to
1-N = -1. Now only deployments that own a replication config are flagged.

Fix 4: SiteReplicationResyncOpHandler accepted "start" and "cancel" but returned
an empty body (and later a parse error on the client) for any other operation
string. Add a SITE_REPL_RESYNC_STATUS="status" arm that returns the stored
SRResyncOpStatus or an explicit {"status":"not-found"} object so mc never sees
an unexpected end of JSON input.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(site-repl): derive sync_state from reachability and replication rule completeness

PeerInfo.sync_state was set to SyncStatus::Unknown at every construction site
and never updated from real signals. build_status_info now tracks which peers
were reachable during the metainfo fetch phase and, after all bucket stats are
merged, derives sync_state as:
  - Enable  : reachable AND no replication_cfg_mismatch for any bucket
  - Disable : reachable BUT at least one bucket has incomplete/missing rules
  - Unknown : unreachable (fetch failed)

The "Sync" column in mc admin replicate status will now reflect actual state
rather than always showing Unknown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(site-repl): add SRRotateServiceAccountHandler for split-brain svc-acct repair

When site-replicator-0 gets desynced (different secret on different peers), calls
via the old service account return 403, blocking remove and replicate operations
with no in-band recovery path.

SiteReplicationRemoveHandler already purges local state unconditionally before
sending peer notifications, so force-remove works locally even when peers reject
the 403. The new POST /v3/site-replication/rotate-svc-acct endpoint provides the
missing recovery path: it generates a fresh service-account secret, applies it
locally, and pushes a peer/join to every member. Each peer's SRPeerJoinHandler
accepts the join idempotently (update if exists, create otherwise), repairing the
desynced credential without a full teardown + restart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(site-repl): back-fill pre-existing buckets and objects on replicate add

SiteReplicationAddHandler and SRPeerJoinHandler both called persist_site_replication_state
and returned success without doing anything for buckets that existed before the
sites were linked. Only buckets created after the link (via site_replication_make_bucket_hook)
ever replicated.

Introduce backfill_existing_buckets_after_add which, after persisting the new
state, iterates every local bucket and:
  1. Ensures versioning is enabled (required by replication).
  2. Reconciles bucket targets so a target entry exists for each remote peer.
  3. Reconciles the replication config so a rule pointing to each peer is present.
  4. Broadcasts a make-bucket-hook to peers (idempotent) so they create the bucket.
  5. Kicks start_site_bucket_resync toward every remote peer so pre-existing
     objects travel across.

Errors per bucket are logged but never abort the overall add — manual resync
remains available as a fallback. Both the initiator and the receiving (join) side
run the backfill so convergence happens regardless of which side held data first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(site-repl): reconcile replication config to restore bidirectional replication

Root cause: ensure_site_replication_bucket_replication_config bailed with Ok(())
the moment any replication config was found on the bucket. When bucket B was
propagated to site-2 via the make-with-versioning bucket-op, site-2's
configure-replication step loaded the freshly-written config and immediately
returned, never adding the reverse-direction rule pointing back to site-1. Result:
objects uploaded to site-2 failed with "replication head_object fallback failed
... service error" because no rule targeted the originating site.

Fix: drop the early-return. Instead load the existing rules, build the full
desired config via build_site_replication_config, and MERGE — adding only the
rules that are absent (identified by their "site-repl-<deployment_id>" id). Re-
number priorities after each merge to avoid conflicts. Existing non-site-repl
rules are preserved. The write is skipped entirely when all desired rules are
already present, so repeat calls remain cheap.

Together with Fix 1 (backfill), any file written to ANY member now replicates to
ALL members. The offline-and-recover case also benefits: when a peer returns,
the per-bucket rules are complete and the resyncer can catch up.

Also register the new rotate-svc-acct route in route_policy and
route_registration_test so the route inventory assertions stay green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(replication): remove broken resync worker-signal gate

The resync_bucket task opened a new broadcast receiver via
worker_rx.resubscribe(), which positions the receiver at the current
write-head of the ring buffer — past all 10 bootstrap signals written
in ReplicationResyncer::new().  Every spawned resync task therefore
blocked on recv() forever, making `mc admin replicate resync start`
report "started" while no objects ever moved.

Remove the dead wait entirely.  Each resync_bucket call is already
spawned on-demand (tokio::spawn in start_bucket_resync / load_resync),
so no additional gate is needed.  The per-object concurrency limit is
already enforced by the inner mpsc worker channels (line ~877).  Also
remove the now-dead worker_tx/worker_rx fields, bootstrap loop, and
signal-send in resync_bucket_mark_status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(site-repl): document rotate-svc-acct idempotency and partial-failure behavior

* style: cargo fmt and remove redundant clones

* fix(site-repl): preserve object-lock state when back-filling existing buckets

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: loverustfs <hello@rustfs.com>
Co-authored-by: houseme <housemecn@gmail.com>
2026-06-13 23:34:39 +08:00
安正超 a85cc0354c refactor(storage-api): remove namespace lock from StorageAPI (#3365)
* refactor(storage-api): remove namespace lock from StorageAPI

* test(scanner): wait for runtime budget cancellation

---------

Co-authored-by: loverustfs <hello@rustfs.com>
2026-06-11 22:42:12 +08:00
houseme 82af181dcf refactor(logging): unify governance runtime events (#3367) 2026-06-11 22:26:02 +08:00
houseme 0a987d870b refactor(logging): reduce runtime noise (#3363) 2026-06-11 19:49:01 +08:00