Commit Graph

5905 Commits

Author SHA1 Message Date
唐小鸭 f51b06f0ae perf(ecstore): enable encrypted range part-seek by default (#6598)
Range GETs on encrypted objects read the whole ciphertext from offset 0
and discarded the decrypted prefix, because the part-boundary seek
shipped behind RUSTFS_ENCRYPTED_RANGE_SEEK defaulted to false
(backlog#1316 Phase A).

Flip the default to true. Safety rests on the marker chain: MPUs created
without a candidate layout marker never become seek-eligible,
CompleteMultipartUpload promotes the candidate to the quorum marker only
after revalidating it against the object's data_dir under the uploadId
write lock, and reads seek only when the quorum marker matches the
current data_dir. Single-part, compressed and markerless objects keep
the full-read path; RUSTFS_ENCRYPTED_RANGE_SEEK=false remains the kill
switch.

The stale default-off regression test becomes
test_legacy_range_seek_defaults_enabled: the unset-env default must
match the explicit opt-in plan, seek past the leading parts, and not
span the whole ciphertext.
2026-08-26 09:35:48 +08:00
唐小鸭 a65b306fb0 perf(sse): drop the second KMS decrypt from encrypted GET responses (#6597)
perf(sse): classify GET response headers without a second KMS unwrap

An SSE-KMS GET performed two backend Decrypt calls per request: the
object layer's encryption resolver unwraps the envelope to build the
decrypted stream, and the S3 layer then called sse_decryption again
purely to derive response headers, discarding the returned key bytes.

Replace the S3-layer call with classify_sse_read_response, which
reproduces that call's behavior from stored metadata alone: SSE-C
validation errors and precedence, per-key kms:Decrypt authorization
ahead of every other failure mode, and the request's KMS audit summary
fields. The success outcome stays honest because a failed unwrap aborts
the read in the object layer before response classification is reached.

Tests cover header parity against the unwrap-based path, audit-tag
parity for allowed and denied principals, SSE-C validation parity, and
prove classification needs no DEK provider at all.
2026-08-26 09:35:42 +08:00
Sinan Eldem b93e7b2355 feat(admin): self-service account management and TOTP two-factor authentication (#6596)
* feat(madmin): add account and two-factor wire contract

Defines the self-service account and MFA API shapes in one place so the
console and the `rc` CLI decode identical payloads instead of each
carrying its own copy of the contract.

`AccountMutability` is part of the contract on purpose: a client needs to
know whether the server will accept a password change for this identity
before offering the control, rather than discovering it from a rejected
request.

* feat(s3-types): add IAM identity audit events

Adds `iam:Identity:CredentialChanged` and `iam:Identity:AuthChallenge`
so account and authentication activity reaches the audit pipeline in its
own namespace, the way the KMS events already do. Neither is reachable
from a bucket notification config.

Two variants for the whole surface rather than one per operation:
`mask()` gives every variant its own bit in a `u64`, and the budget is
nearly spent (63 of 64 used after this). The per-operation detail lives
in `AuditEntry::api.name` and the `iamOperation` tag, which is what a
SIEM filters on anyway. Splitting these further needs `mask()` widened
first.

* feat(iam): add two-factor authentication primitives

Implements the state machine behind TOTP enrollment and verification in
the IAM domain, so the admin handlers stay HTTP plumbing and the console
and CLI drive identical logic.

* `totp`: RFC 6238 over the workspace's existing hmac/sha1, pinned to the
  published Appendix B vectors. SHA-1, 6 digits, 30s: the parameters every
  mainstream authenticator app implements. Verification returns the
  matched time step so the caller can burn it.
* `recovery`: ten single-use codes, 100 bits each, in a Crockford base32
  alphabet without I/L/O/U. Stored as domain-separated SHA-256 digests —
  a password KDF would have to run once per stored code on every attempt,
  turning each guess into an attacker-controlled cost, and with uniform
  100-bit input there is no dictionary for it to defend against.
* `challenge`: stateless HMAC tokens. A TTL cache would be node-local, so
  a cluster without session affinity would issue on one node and verify
  on another; nothing here needs replicating.
* `record`: two-phase enrollment, replay high-water mark, and lockout.
  Pending enrollment never gates a login, so a mis-scanned QR cannot lock
  an operator out, and re-configuring keeps the old factor working until
  the new one is confirmed.
* `store`: one object per identity under `config/mfa/`, a sibling of
  `config/iam/` so the IAM cache loader's startup walk does not sweep it
  up. Optimistic `If-Match` writes; deliberately uncached, because a cache
  would need cluster-wide invalidation to keep the replay mark and the
  lockout counter honest.
* `qr`: server-side rendering, so neither client needs a QR encoder.

Enrollment is refused without `RUSTFS_IAM_MASTER_KEY`. A TOTP secret is
credential-equivalent, and one written in plaintext could be lifted off a
disk — worse than no second factor, because the user believes they have
one. IAM identities tolerate a missing master key for backward
compatibility; a new feature has no such history to honour.

Also adds `IamSys::revoke_sts_sessions_for_parent`, so a credential
rotation can invalidate the sessions minted under the old secret.

* feat(admin): add self-service account endpoints and the two-factor login gate

Adds the account surface (`/v3/account/*`), the second-factor endpoints,
the administrative reset (`/v3/user/mfa`), and `PUT
/v3/set-user-secret-key`, plus the gate on `AssumeRole`.

What the gate covers, and what it deliberately does not:

* `AssumeRole` is the only interactive login RustFS has, so it is where a
  second factor can be enforced. With one enrolled it requires
  `TokenCode`; without an enrollment the code path is unchanged, so
  existing deployments are untouched.
* A request signed directly with a long-term access key stays ungated.
  Gating it would break every script and CLI the moment a human enabled
  2FA on their own account, and would add no protection: whoever holds
  the secret key already has full access without presenting a code. This
  is the division AWS draws; making 2FA meaningful for API access needs an
  `aws:MultiFactorAuthPresent` policy condition, tracked separately.

`SerialNumber`/`TokenCode` are STS's own parameters, so an SDK or script
authenticates the same way the console does.

`caller_identity` resolves who a request acts as. The console signs with
a short-lived STS session, so "the caller" is almost never the key that
signed. It reports two separate capabilities: root cannot rotate its
secret (a process-wide `OnceLock` that also derives the internode RPC
secret) but *can* enroll a second factor — conflating the two would leave
the default deployment's console login unprotectable.

The self-service routes carry no admin action. Giving them one would be
wrong in both directions: it would stop an ordinary user from changing
their own password, and let any holder of that action change someone
else's. They gate on possession of the credential plus, for the
mutations, knowledge of the current secret — a signature only proves a
credential was used, so without that a hijacked tab could rewrite the
account's credentials or strip its second factor.

`set-user-secret-key` exists because the only prior way to change a
password was to re-POST the whole user through `add-user`, which rewrote
`status` and dropped the policy field — a password reset that silently
re-enabled a disabled account.

Wrong, replayed and malformed codes are indistinguishable on the wire;
the distinction survives only in the audit trail, where no submitted
value, secret or code is ever recorded.

* test(e2e): cover the two-factor lifecycle and its regressions

Unit tests cover the state machine at its edges; only an end-to-end test
proves the pieces are wired together and that the existing
authentication paths still behave.

Asserts, against a real server: enrollment is refused without a master
key; the full enroll/activate flow works with a genuine RFC 6238 code;
`AssumeRole` refuses without a factor and accepts a valid one; a recovery
code works exactly once; a direct SigV4 admin request keeps working with
a factor enrolled; `AssumeRole` for an unenrolled identity is unchanged;
and a password rotation invalidates the old secret.

The test computes TOTP codes itself rather than calling the server's
implementation — a shared helper could agree with a bug on both sides.

This suite caught a real defect during development: enrollment was
refused for root because its *password* is immutable, which would have
left the default deployment — an administrator signing into the console
as root — unable to protect the one login the feature exists for.

* docs(operations): document the two-factor authentication model

Records what the second factor protects and what it deliberately does
not, because several of the boundaries look like gaps until the
alternative is spelled out: why direct SigV4 access stays ungated, why
root credentials cannot be rotated at runtime, why secret keys cannot be
hashed in an S3 server, and why at-rest protection is mandatory for a
TOTP secret but optional for an IAM identity.

Also states the limitations plainly, including that GHSA-m77q-r63m-pj89
is unaffected: a holder of the root secret can still forge a session
token, 2FA claim included.

Placed alongside the other authentication and KMS security documents
rather than under a new `docs/security/`, which `.gitignore` excludes.

* fix(admin): route the new account handlers through the admin s3 facade

Two of the guardrails in the CI "Quick Checks" job rejected the previous
commits, so the required check would have gone red as soon as a maintainer
approved the workflow run.

`check_architecture_migration_rules.sh` requires everything under
`rustfs/src/admin` to reach `ECStore` through a domain module rather than
the root of `storage_api`. The MFA handler and the two `AssumeRole`
signatures now use `storage_api::runtime::ECStore`, which is where the
other ten admin handlers already take it from.

`check_s3s_footprint.sh` ratchets two counters that new code may not grow:
files referencing `s3s` and error-macro invocation lines. This branch added
four files and thirty-two lines to them. The ratchet is lower-only and its
header forbids raising a baseline to get green, so the construction moves
behind the facade instead: `storage_api::s3` now re-exports the request and
body types these handlers need and gains an `error` constructor over
`S3Error::with_message`. That is the same constructor the macro expands to
and the one `handlers/mod.rs`, `rebalance_internal_error` and
`invalid_object_lock_configuration` already call, so this is the existing
practice rather than a new one, and it keeps the `s3s` dependency in the
boundary file the s3gate migration replaces.

Every error code and message is carried over unchanged. In `sts.rs` only
the call site this branch added is converted; the sixteen that predate it
are left alone, because rewriting them would put unrelated churn in a
feature PR and push the counter below the baseline it is meant to hold.
2026-08-26 09:35:29 +08:00
houseme 8f196f2f20 fix(startup): avoid blocking on resync reconcile (#6593)
Run replication resync target reconcile and follow-up resync recovery in a background startup task so bucket metadata transaction lock contention cannot keep a node from joining the cluster.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-26 09:35:15 +08:00
houseme 59fd318192 perf(ecstore): optimize opts.clone() and FileInfo clone patterns (#6587)
* feat(mimalloc): add arena diagnostics and configuration

Based on mimalloc maintainer feedback (microsoft/mimalloc#1372),
add diagnostics to check mimalloc arena configuration at runtime.

Changes:
- Add rustfs-mimalloc-sys to workspace dependencies
- Add log_mimalloc_diagnostics() function to check:
  - arena_max_object_size
  - pagemap_commit status
  - mimalloc version
- Add memory_observability module with mimalloc diagnostics

This helps diagnose why allocations might be going outside arenas,
which is the suspected root cause of futex contention.

Ref: rustfs/backlog#2005
Ref: microsoft/mimalloc#1372

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

* perf(ecstore): add Vec<u8> buffer pool for EC operations

Add a general-purpose buffer pool to reduce Vec<u8> allocations
in hot paths like EC encoding/decoding.

Changes:
- Add BufferPool struct in crates/ecstore/src/erasure/codec/buffer_pool.rs
- Thread-safe pool with capacity-based bucketing (power-of-two)
- Global EC_BUFFER_POOL instance with 16 buffers per bucket
- Add buffer_pool module to codec/mod.rs

Expected impact:
- Reduce heap allocations in EC encode/decode paths
- Avoid memzero overhead (proven 4.8% CPU saving in ShardBufferPool)
- Reduce mimalloc lock contention

Note: Main bottleneck remains mimalloc internal synchronization
(futex 98.64% time). Buffer pool provides modest improvement (+2-5%).

Ref: rustfs/backlog#2005

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

* style: apply cargo fmt to buffer pool and related files

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

* fix(ecstore): add #[allow(dead_code)] to buffer pool

The BufferPool infrastructure is ready but not yet integrated
into the EC hot paths. Add #[allow(dead_code)] with clear
documentation about integration status.

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

* perf(ecstore): integrate BufferPool into bitrot verify path

Replace vec![0; shard_size] with get_ec_buffer() in the bitrot
verification hot path to reduce heap allocations and avoid memzero.

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

* style: apply cargo fmt to buffer pool and bitrot changes

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

* refactor(ecstore): clean up buffer pool code

- Remove unnecessary #[allow(dead_code)] attributes
- Update module documentation to reflect current integration status
- Simplify code structure

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

* perf(runtime): cap default worker threads at 16

Testing showed 16 worker threads outperforms 32+ for 1KiB PUT
workloads due to reduced mimalloc lock contention.

A/B test results (testing 4-node cluster, c=64):
- worker_threads=32: 740 obj/s (baseline)
- worker_threads=16: 785 obj/s (+6.1%)

The default was detect_cores() which returned 32 on our testing
nodes. Cap at 16 for optimal small-object performance.

Ref: rustfs/backlog#2005

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

* style: apply cargo fmt to buffer pool and runtime changes

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

* fix(ecstore): remove unused BufferPool::new() function

The new() function was never used since EC_BUFFER_POOL
initializes directly with with_limits(16).

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

* fix(ecstore): update buffer_pool tests to use with_limits

Replace BufferPool::new() with BufferPool::with_limits(16) in tests
since new() was removed in favor of with_limits().

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

* perf(ecstore): optimize opts.clone() and FileInfo clone patterns

## Changes

1. ObjectOptions helper methods:
   - add as_commit_opts(): creates commit options with no_lock=true,
     metadata_cache_safe=false, include_part_checksums=true
   - add as_read_opts(): creates read options with
     include_part_checksums=true
   - add with_no_lock(): creates options with modified no_lock field

2. Replace opts.clone() in hot paths:
   - commit_opts = opts.as_commit_opts() (was 4-line manual clone)
   - read_opts = opts.as_read_opts() (was 2-line manual clone)

3. Optimize FileInfo clone in rename path:
   - avoid double clone: clone once and modify erasure.index in place
   - pass &file_info reference to rename_data_borrowed_with_fence

## A/B Results (4-node cluster, c=64)

| Size | main | optimized | Change |
|------|------|-----------|--------|
| 1KiB | 892 obj/s | 920-976 obj/s | +3%~+9% |
| 4KiB | 957 obj/s | 903 obj/s | -5.7% |
| 16KiB | 922 obj/s | 855 obj/s | -7.3% |

Note: 1KiB improvement is consistent. 4KiB/16KiB variance
likely due to test noise; needs more rounds to confirm.

Ref: rustfs/backlog#2005

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

* perf(ecstore): add BytesMut buffer pool to EC encoding path

Pre-allocate a Vec<BytesMut> pool in the EC encoding loop to avoid
repeated heap allocations for ingest buffers.

Changes:
- Pre-allocate buffer pool with capacity 4
- Reuse buffers from pool after encoding
- Return buffers to pool when capacity is sufficient

Expected impact: +10-20% in EC encoding path by reducing
BytesMut allocation overhead.

Ref: rustfs/backlog#2005

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

---------

Co-authored-by: hector <hetor@rustfs.com>
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-26 09:35:09 +08:00
RJ Regenold 75a71fe6d7 fix(audit): include deleted objects in bulk audit entries (#6592) 2026-08-26 09:35:03 +08:00
Zhengchao An ba629bdae0 feat(connect): rotate device credentials at runtime (#6586)
* feat(connect): rotate credentials from heartbeat runtime

* fix(connect): make rotation safe with in-flight telemetry

* fix(connect): keep rotation retry state private

* fix(connect): preserve public rotation retries

* fix(connect): preserve heartbeat error API

* fix(connect): keep heartbeat alive during reenrollment

* fix(connect): validate pending reenrollment before skipping

* fix(connect): validate pending reenrollment token

* fix(connect): bind pending reenrollment state

* fix(connect): recover credentials before telemetry
2026-08-26 09:34:57 +08:00
Zhengchao An 5cce18fef2 test(diagnose): run binary smoke in CI (#6605) 2026-08-26 09:34:21 +08:00
Zhengchao An 54e2dce495 ci: run live target backend tests (#6603)
* ci: run live target backend tests

* test(targets): align MySQL live assertions
2026-08-26 09:34:11 +08:00
Zhengchao An 6f0a371f01 test(e2e): require exact object lock rejection oracles (#6580) 2026-08-26 09:34:05 +08:00
Zhengchao An 1bcb396752 fix(ecstore): version pool metadata transactions (#6604)
* fix(connect): adapt offline array predicate

* test(e2e): update smoke selection baseline

* test(ecstore): make slowtail oracle deterministic

* test(get): stage relocated fixture after reader opens

* ci: bound feature test link concurrency

* test: give lifecycle transition futures a larger stack

* fix(ecstore): version pool metadata transactions
2026-08-26 09:33:51 +08:00
Zhengchao An c0c5fc22f9 ci: preserve ILM timeout diagnostics (#6602)
* fix(connect): adapt offline array predicate

* test(e2e): update smoke selection baseline

* test(ecstore): make slowtail oracle deterministic

* test(get): stage relocated fixture after reader opens

* ci: bound feature test link concurrency

* test: give lifecycle transition futures a larger stack

* ci: preserve ILM timeout diagnostics
2026-08-26 09:33:42 +08:00
Zhengchao An 6886f7cac4 test(ci): give lifecycle transitions a larger stack (#6595)
* fix(connect): adapt offline array predicate

* test(e2e): update smoke selection baseline

* test(ecstore): make slowtail oracle deterministic

* test(get): stage relocated fixture after reader opens

* ci: bound feature test link concurrency

* test: give lifecycle transition futures a larger stack
2026-08-26 09:33:25 +08:00
Zhengchao An 032c5f9ac6 ci: bound feature test link concurrency (#6594)
* fix(connect): adapt offline array predicate

* test(e2e): update smoke selection baseline

* test(ecstore): make slowtail oracle deterministic

* test(get): stage relocated fixture after reader opens

* ci: bound feature test link concurrency
2026-08-26 09:33:16 +08:00
Zhengchao An 4e749d7046 test(get): stage relocated fixture after reader opens (#6584)
* fix(connect): adapt offline array predicate

* test(e2e): update smoke selection baseline

* test(ecstore): make slowtail oracle deterministic

* test(get): stage relocated fixture after reader opens
2026-08-26 09:33:07 +08:00
Zhengchao An 51449f0975 test(e2e): update smoke selection baseline (#6582)
* fix(connect): adapt offline array predicate

* test(e2e): update smoke selection baseline

* test(ecstore): make slowtail oracle deterministic (#6583)
2026-08-26 09:32:53 +08:00
Zhengchao An 90f64c60af fix(connect): adapt offline array predicate (#6581) 2026-08-26 09:32:40 +08:00
houseme 0f987714a1 fix(ecstore): handle metadata-less bucket residue (#6591)
* fix(ecstore): handle metadata-less bucket residue

Diagnose metadata-less on-disk residue before non-force DeleteBucket reaches physical deletion, and keep scanner-discovered metadata-missing objects on a non-destructive heal path.

Add explicit heal --remove cleanup for unversioned metadata-less data directories, using the existing data-dir delete primitive and fail-closed shape checks so pre-commit or unknown residue is preserved.

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

* fix(connect): adapt offline array validator

Wrap the filesystem summary validator in a closure so Option::is_some_and can pass the concrete array reference accepted by serde_json::Value::as_array.

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

* fix(connect): remove redundant offline test clones

Move the temporary path into the swap closure after deriving the output path, keeping clippy's redundant-clone lint clean for offline bundle tests.

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

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-26 09:17:19 +08:00
Zhengchao An 2bd83a5276 feat(connect): build signed offline bundles (#6579)
* feat(connect): build signed offline bundles

* fix(connect): validate offline bundle inputs

* test(connect): use the target architecture

* chore(connect): scope the unsafe allowance
2026-08-25 21:37:44 +08:00
Zhengchao An 6a99edab50 fix(s3): reject tampered multipart payloads cleanly (#6578) 2026-08-25 21:37:28 +08:00
Zhengchao An be1562e089 test(heal): wait for versioned fixture copies (#6571) 2026-08-25 21:21:52 +08:00
GatewayJ 5a0367969a fix(replication): retry startup resync lock failures (#6570) 2026-08-25 21:21:30 +08:00
GatewayJ 0c155b1656 fix(put): reap cancelled eager commit owners (#6569) 2026-08-25 21:21:05 +08:00
Henry Guo 9db29c8a6f fix(heal): reconcile dangling objects after node reconnect (#6567) 2026-08-25 21:20:47 +08:00
Zhengchao An b4a78fc907 fix(api): preserve typed upload digest errors (#6564) 2026-08-25 21:20:13 +08:00
Zhengchao An 02317dd36f test(keycloak): fix Keycloak OIDC live fixture (#6563) 2026-08-25 21:20:03 +08:00
Zhengchao An b4e6c1b081 fix(connect): use persisted inventory for offline collectors (#6560) 2026-08-25 21:19:49 +08:00
Zhengchao An bcfed065c1 test(e2e): make security boundary oracles fail closed (#6542) 2026-08-25 21:19:28 +08:00
houseme 9a89434644 fix(health): keep liveness peer independent (#6576)
Keep liveness probes local by avoiding readiness collection and omitting readiness-only fields from liveness payloads. Readiness and MinIO cluster probes continue to report dependency and quorum state.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-25 12:37:26 +00:00
Zhengchao An b15928220f test(ecstore): avoid virtual timeout for sync batch (#6551) 2026-08-25 14:30:49 +08:00
Henry Guo cf37bc418c fix(capacity): skip idle scheduled disk scans (#6541)
Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com>
2026-08-25 14:30:29 +08:00
houseme 76a861f815 fix(health): align ready with lock quorum (#6554)
Treat lock quorum as part of node readiness for both /health and /health/ready response bodies while preserving the /health liveness HTTP 200 contract.

Add focused regression coverage for lock-quorum-only degradation and make the public /health layer fixture independent from process-global readiness state.

Refs: rustfs/backlog#2011

Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-25 14:26:14 +08:00
houseme 5ce884f605 chore(deps): update s3s revision (#6545)
* chore(deps): update s3s revision

Pin the workspace s3s dependency to rustfs/s3s commit 39080d610e0560c55f068f6dd76b976e267b2f67 and refresh compatible dependencies with cargo update and cargo upgrade.

Co-authored-by: heihutu <heihutu@gmail.com>

* fix(s3): preserve SigV4 body validation errors

Map s3s upload stream body validation failures into existing RustFS client-error types before the PUT body readers consume them. This keeps tampered single-chunk payload hashes from surfacing as InternalError after the s3s revision update.

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

* chore(deps): use s3s 0.15.0 release

Switch the workspace dependency from the temporary s3s git revision to the published 0.15.0 crate and refresh the lockfile updates that come with the release.

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

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-25 13:01:53 +08:00
houseme 0c4c1caef8 perf(ecstore): add Vec<u8> buffer pool for EC operations (#6538)
* feat(mimalloc): add arena diagnostics and configuration

Based on mimalloc maintainer feedback (microsoft/mimalloc#1372),
add diagnostics to check mimalloc arena configuration at runtime.

Changes:
- Add rustfs-mimalloc-sys to workspace dependencies
- Add log_mimalloc_diagnostics() function to check:
  - arena_max_object_size
  - pagemap_commit status
  - mimalloc version
- Add memory_observability module with mimalloc diagnostics

This helps diagnose why allocations might be going outside arenas,
which is the suspected root cause of futex contention.

Ref: rustfs/backlog#2005
Ref: microsoft/mimalloc#1372

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

* perf(ecstore): add Vec<u8> buffer pool for EC operations

Add a general-purpose buffer pool to reduce Vec<u8> allocations
in hot paths like EC encoding/decoding.

Changes:
- Add BufferPool struct in crates/ecstore/src/erasure/codec/buffer_pool.rs
- Thread-safe pool with capacity-based bucketing (power-of-two)
- Global EC_BUFFER_POOL instance with 16 buffers per bucket
- Add buffer_pool module to codec/mod.rs

Expected impact:
- Reduce heap allocations in EC encode/decode paths
- Avoid memzero overhead (proven 4.8% CPU saving in ShardBufferPool)
- Reduce mimalloc lock contention

Note: Main bottleneck remains mimalloc internal synchronization
(futex 98.64% time). Buffer pool provides modest improvement (+2-5%).

Ref: rustfs/backlog#2005

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

* style: apply cargo fmt to buffer pool and related files

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

* fix(ecstore): add #[allow(dead_code)] to buffer pool

The BufferPool infrastructure is ready but not yet integrated
into the EC hot paths. Add #[allow(dead_code)] with clear
documentation about integration status.

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

* perf(ecstore): integrate BufferPool into bitrot verify path

Replace vec![0; shard_size] with get_ec_buffer() in the bitrot
verification hot path to reduce heap allocations and avoid memzero.

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

* style: apply cargo fmt to buffer pool and bitrot changes

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

* refactor(ecstore): clean up buffer pool code

- Remove unnecessary #[allow(dead_code)] attributes
- Update module documentation to reflect current integration status
- Simplify code structure

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

* perf(runtime): cap default worker threads at 16

Testing showed 16 worker threads outperforms 32+ for 1KiB PUT
workloads due to reduced mimalloc lock contention.

A/B test results (testing 4-node cluster, c=64):
- worker_threads=32: 740 obj/s (baseline)
- worker_threads=16: 785 obj/s (+6.1%)

The default was detect_cores() which returned 32 on our testing
nodes. Cap at 16 for optimal small-object performance.

Ref: rustfs/backlog#2005

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

* style: apply cargo fmt to buffer pool and runtime changes

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

* fix(ecstore): remove unused BufferPool::new() function

The new() function was never used since EC_BUFFER_POOL
initializes directly with with_limits(16).

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

* fix(ecstore): update buffer_pool tests to use with_limits

Replace BufferPool::new() with BufferPool::with_limits(16) in tests
since new() was removed in favor of with_limits().

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

---------

Co-authored-by: hector <hetor@rustfs.com>
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-25 10:45:44 +08:00
houseme d9cd04e94e fix(config): enable allocator reclaim by default (#6566)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-08-25 10:44:18 +08:00
Zhengchao An 017ffb92f7 test: add live Keycloak OIDC gate (#6562) 2026-08-25 04:34:46 +08:00
Zhengchao An 40f1356831 test(e2e): tighten control character rejection oracle (#6561) 2026-08-25 04:34:35 +08:00
Zhengchao An 619f0fd9e8 test(iam): verify JWKS rotation refresh (#6559) 2026-08-25 04:34:24 +08:00
Zhengchao An 9d68d63802 test(e2e): fail closed on tampered payloads (#6558) 2026-08-25 04:34:13 +08:00
Zhengchao An f41014ede7 test(e2e): require bucket policy denial code (#6557) 2026-08-25 04:34:02 +08:00
Zhengchao An 68dd5bfb9f test(e2e): require exact versioning oracles (#6556) 2026-08-25 04:33:50 +08:00
Zhengchao An 77d7404d77 test(e2e): add pinned direct upgrade gate (#6555) 2026-08-25 04:33:38 +08:00
Zhengchao An 97d2d344b2 test(kms): require exact fault recovery errors (#6553) 2026-08-25 04:33:26 +08:00
Zhengchao An 3da319624f ci: preserve failure verdict before early stop (#6552) 2026-08-25 04:33:16 +08:00
Zhengchao An bb9491f782 test(heal): wait for fixture writes before corruption (#6549) 2026-08-25 04:33:04 +08:00
Zhengchao An 9f12f344c9 test(e2e): require exact checksum errors (#6548) 2026-08-25 04:32:53 +08:00
Zhengchao An 82df9ec4fa test(fuzz): record reproducible run seeds (#6547) 2026-08-25 04:32:41 +08:00
Zhengchao An 40e6decc93 test(e2e): require exact SSE-C errors (#6546) 2026-08-25 04:32:30 +08:00
Zhengchao An 116119d93a test(e2e): require exact quota errors (#6544) 2026-08-25 04:32:19 +08:00
Zhengchao An 6f39765498 test(e2e): require exact bucket compatibility errors (#6540) 2026-08-25 04:32:08 +08:00