mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-05 12:57:42 +00:00
fix(storage): expose truthful storage class capabilities (#5172)
This commit is contained in:
@@ -75,6 +75,9 @@ Default parity by drive count — `default_parity_count(N)` ([storageclass.rs](.
|
||||
|
||||
Two storage classes: `STANDARD` (SC) and `REDUCED_REDUNDANCY` (RRS) ([storageclass.rs](../../crates/ecstore/src/config/storageclass.rs)), configured as `"EC:<parity>"` via the `standard` / `rrs` config keys or the `RUSTFS_STORAGE_CLASS_STANDARD` / `RUSTFS_STORAGE_CLASS_RRS` env overrides. Absent config falls back to `default_parity_count` (SC) and `1`, or `0` on a single drive (RRS).
|
||||
|
||||
- **INVARIANT — truthful client write classes.** S3 PUT, CopyObject, and CreateMultipartUpload accept only `STANDARD` and `REDUCED_REDUNDANCY`. AWS labels such as `STANDARD_IA`, `ONEZONE_IA`, `INTELLIGENT_TIERING`, `GLACIER`, and `DEEP_ARCHIVE` are rejected with `InvalidStorageClass` because RustFS does not implement their advertised access, retrieval, or archival semantics. The supported allowlist and stable error identifier are owned by [storageclass.rs](../../crates/ecstore/src/config/storageclass.rs), not duplicated by individual handlers.
|
||||
- **INVARIANT — historical label normalization.** Non-transitioned objects written by older RustFS versions may contain an AWS storage-class label without distinct physical semantics. Read and listing responses report the effective local layout (`STANDARD`, or `REDUCED_REDUNDANCY` when that layout was selected) instead of repeating a label-only promise. A completed lifecycle transition is different: its real transition tier name is preserved. Persistent metadata-fast list snapshots use the `rustfs-listobjects-key-only-v2` header; older or unknown snapshot formats are rebuilt from normalized `ObjectInfo` values instead of being served, because their stored label lacks enough information to distinguish historical metadata from a real transition tier.
|
||||
|
||||
- **INVARIANT — parity bounds.** Parity must satisfy `parity ≤ N/2` for both classes, and `SC parity ≥ RRS parity` when both are non-zero ([storageclass.rs](../../crates/ecstore/src/config/storageclass.rs), `validate_parity` / `validate_parity_inner`). Enforcement nuance to be aware of: `validate_parity_inner` (the path a user-configured `EC:<parity>` storage class flows through) only applies the `parity ≤ N/2` check for `N > 2`, so degenerate small-set values (e.g. `EC:2` on `N = 2`, giving `data_blocks = 0`) are not caught there; the standalone `validate_parity` enforces the bound unconditionally but is applied only to the resolved default parity. A change that lets user-configured parity reach a write path must not assume the `≤ N/2` bound was enforced for `N ≤ 2`. Parity `0` is permitted (single-drive / capacity setups); there is no non-zero minimum.
|
||||
- **INVARIANT — per-pool validity.** Each pool's resolved parity must be valid for **that pool's own drive count**. A heterogeneous deployment (pools of different widths) must resolve parity per pool; applying one pool's parity to a narrower pool can drive `data_blocks = N − parity` to `0` and make encoding impossible.
|
||||
- Baseline defect: `main` computes `common_parity_drives` from the **first** pool only and applies it to every pool ([store/init.rs](../../crates/ecstore/src/store/init.rs), `ec_drives_no_config` at [store/init_format.rs](../../crates/ecstore/src/store/init_format.rs)); this is issue #4801 (a smaller later pool panics with `TooFewDataShards`). The correct rule is per-pool resolution.
|
||||
|
||||
@@ -59,3 +59,33 @@ owners through read-only providers:
|
||||
|
||||
Unsupported or unavailable runtime capabilities are reported as `unsupported`
|
||||
or `unknown` contract states instead of activating fallback behavior.
|
||||
|
||||
## Storage-Class Write Contract
|
||||
|
||||
Authenticated clients discover the storage-class write contract from
|
||||
`GET /rustfs/admin/v4/runtime/capabilities`. The additive
|
||||
`storage_classes` object is versioned independently from the route:
|
||||
|
||||
```json
|
||||
{
|
||||
"storage_classes": {
|
||||
"contract_version": 1,
|
||||
"supported_write_classes": ["STANDARD", "REDUCED_REDUNDANCY"],
|
||||
"unsupported_write_error": "InvalidStorageClass",
|
||||
"legacy_label_behavior": "normalized_to_effective_class"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`supported_write_classes` is the complete client-selectable write allowlist.
|
||||
Any other value fails before object or multipart mutation with the stable S3
|
||||
error named by `unsupported_write_error`. `legacy_label_behavior` means
|
||||
non-transitioned historical label-only metadata is reported as its effective
|
||||
local class; actual lifecycle transition tier names remain unchanged.
|
||||
|
||||
The values are sourced from
|
||||
[`crates/ecstore/src/config/storageclass.rs`](../../crates/ecstore/src/config/storageclass.rs),
|
||||
which also owns write validation and response normalization. Consumers must
|
||||
branch on `contract_version` before assigning meaning to future fields. The
|
||||
admin route continues to require `ServerInfoAdminAction`; capability discovery
|
||||
does not weaken authentication or authorization.
|
||||
|
||||
@@ -79,10 +79,11 @@
|
||||
| snowball_auto_extract_test | 6 | |
|
||||
| special_chars_test | 14 | ✅ |
|
||||
| stale_multipart_cleanup_cluster_test | 1 | |
|
||||
| storage_class_capability_test | 4 | ✅ |
|
||||
| tls_gen | 3 | |
|
||||
| tls_hot_reload_test | 1 | ✅ |
|
||||
| version_id_regression_test | 10 | ✅ |
|
||||
|
||||
`notification_webhook_test` also has 1 ignored store-and-forward regression tracked by rustfs#4852; ignored tests are excluded from the active counts above.
|
||||
|
||||
**Total listed: 475 tests across 65 modules · PR smoke subset: 122 tests / 30 modules** (28 full modules + 4 `reliant` tests + 20 of `replication_extension_test`) **· nightly `e2e-repl-nightly`: 27 tests** · generated 2026-07-24.
|
||||
**Total listed: 479 tests across 66 modules · PR smoke subset: 126 tests / 31 modules** (29 full modules + 4 `reliant` tests + 20 of `replication_extension_test`) **· nightly `e2e-repl-nightly`: 27 tests** · generated 2026-07-24.
|
||||
|
||||
Reference in New Issue
Block a user