Files
rustfs/docs/architecture/config-model-boundary-adr.md
T
Zhengchao An f63af3df63 chore: retire completed-migration scaffolding, wire orphaned boundary check (#4719)
The ecstore/global-state migrations are done (backlog#815, #939, #1052 all
closed). Review of every migration-era test/gate measure found three things
actually retirable or broken — everything else is a live anti-regression
guard and stays.

Remove:
- scripts/check_metrics_migration_refs.sh — guards a migration that
  finished: rustfs_metrics:: has zero hits, the metrics crate no longer
  exists, and the script was never wired into CI or make (only reference
  was one line in config-model-boundary-adr.md, also removed).
- crates/obs init_metrics_collectors — the "backward-compatible alias kept
  during migration" the removed script was guarding. Zero callers; pure
  delegate to init_metrics_runtime.

Archive (docs/superpowers/plans/, continuing the 2026-07 convention,
with the standard archived banner):
- startup-timeline.md, scheduler-baseline.md,
  profiling-numa-capability-inventory.md,
  kms-development-defaults-inventory.md — one-shot snapshots whose only
  consumer is the already-archived migration-progress ledger (their
  same-dir links there start resolving again after the move); zero script
  pins; fed the closed backlog#660/#665 architecture-review ledger.
  Fixed the one outbound link (startup-timeline -> readiness-matrix) that
  the move would have broken — check_doc_paths.sh deliberately does not
  scan plans/, so nothing else would have caught it.

Wire (found orphaned by the same review):
- scripts/check_extension_schema_boundaries.sh guards a live contract
  crate but was never invoked anywhere. Add lint-fmt.mak target, include
  in pre-commit/pre-pr/dev-check, add ci.yml Quick Checks step (job
  already installs ripgrep), sync the CONTRIBUTING.md enumerated list,
  and harden the script against a silently-passing rg probe when src/
  is missing.

Keep (verified live, documented so the next cleanup pass does not repeat
this analysis):
- scripts/check_architecture_migration_rules.sh — added a header stating
  it is a permanent boundary guard, not retirable migration scaffolding;
  'migration' in the name is historical.
- check_migration_gate_count.sh + floor, delete-marker e2e proof, all
  pinned docs, compat-cleanup-register sync, remaining inventories
  (referenced by live docs).

Verification: all 7 guard scripts pass, actionlint clean,
cargo check --workspace (excl e2e) clean, cargo fmt --check clean.
Adversarially reviewed by two independent skeptic passes; their 7
findings (alias left behind, broken outbound link, missing banners,
wrong backlog attribution, CONTRIBUTING drift, rg exit-2 hole, missing
header rationale) are all folded in.
2026-07-11 13:42:56 +08:00

186 lines
6.3 KiB
Markdown

# Config Model Boundary ADR
Related issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
Task: `CFG-002`
## Decision
Use the existing `crates/config` package (`rustfs-config`) as the target owner
for the pure server-config model. Do not create a new config-model crate for
the first extraction.
The next model extraction PR should introduce the model under:
```text
crates/config/src/server_config.rs
```
The exported path should be:
```rust
rustfs_config::server_config::{Config, KV, KVS}
```
The extraction kept the existing path available through a temporary
compatibility re-export:
```rust
rustfs_ecstore::config::{Config, KV, KVS}
```
That re-export included `RUSTFS_COMPAT_TODO(CFG-004)` and a matching entry in
[`compat-cleanup-register.md`](compat-cleanup-register.md) until the model
consumers were migrated. The CFG-004 cleanup removed this old model path after
code scans showed consumers import the model directly from `rustfs-config`.
Follow-up `CFG-008` moved the process-global server-config snapshot accessors
to `rustfs_config::server_config` after the model path stabilized. Its temporary
`rustfs_ecstore::config::{get_global_server_config, set_global_server_config}`
compatibility re-export was removed after in-repo runtime consumers migrated to
the `rustfs-config` owner.
## Why `rustfs-config`
`rustfs-config` is already the lowest RustFS crate for configuration constants
and subsystem identifiers used by ECStore, notify, audit, targets, scanner, IAM,
and admin code. The current `ecstore::config::{Config, KV, KVS}` model already
uses `rustfs-config` constants, so moving the pure model upward to
`rustfs-config` cuts the wrong dependency direction without adding another crate.
Creating a new crate now would add a second config namespace before consumers
are migrated. That would increase re-export and compatibility surface while not
removing any storage or runtime dependency by itself.
## Allowed Dependencies
The server-config model module may use only:
- `std::collections::HashMap`
- `std::sync::{LazyLock, OnceLock, RwLock}` for the default `KVS` registration
surface and process-global server-config snapshot
- `serde` for `KV` and `KVS` serialization compatibility
- `serde_json` for `Config::marshal` and `Config::unmarshal`
- existing `rustfs-config` constants and subsystem modules
If `serde` and `serde_json` are added to `rustfs-config`, they should be attached
only to a model feature such as `server-config-model` unless the implementation
PR proves that making them non-optional is simpler and harmless for downstream
builds.
## Forbidden Dependencies
The model module must not depend on:
- `rustfs-ecstore`
- `rustfs`
- `StorageAPI` or object persistence helpers
- notify, audit, targets, IAM, scanner, KMS, or admin handler crates
- async runtimes, HTTP/router crates, object-store crates, or runtime lifecycle
state
- unrelated runtime global state outside the process-global server-config
snapshot
- `ConfigSys`, `read_config_without_migrate`, `save_server_config`, or any
`com.rs` persistence helper
## Boundary Split
Move in the first extraction:
- `KV`
- `KVS`
- `Config`
- `DEFAULT_KVS`
- `register_default_kvs`
- `Config::new`
- `Config::get_value`
- `Config::set_defaults`
- `Config::marshal`
- `Config::unmarshal`
- `Config::merge`
Keep in `ecstore`:
- `ConfigSys`
- `init_global_config_sys`
- `try_migrate_server_config`
- `read_config_without_migrate`
- `save_server_config`
- generic `com.rs` config-object helpers
- storage-class runtime global state
Keep default registration wiring in `ecstore::config::init` until a later PR
extracts a dedicated default-registration contract. The values may be registered
through the moved `rustfs_config::server_config::register_default_kvs`, but the
startup order and caller remain unchanged.
Move in `CFG-008`:
- `GLOBAL_SERVER_CONFIG`
- `get_global_server_config`
- `set_global_server_config`
The temporary ECStore compatibility re-export for these accessors was removed
after code scans showed in-repo consumers use `rustfs_config::server_config`
directly.
## Required Shape Preservation
The extraction PR must preserve:
- `KV { key, value, hidden_if_empty }`
- `#[serde(default, alias = "hiddenIfEmpty")]` on `KV::hidden_if_empty`
- `KVS(pub Vec<KV>)`
- `Config(pub HashMap<String, HashMap<String, KVS>>)`
- `KVS::new`, `get`, `lookup`, `is_empty`, `keys`, `insert`, and `extend`
- `Config::new`, `get_value`, `set_defaults`, `marshal`, `unmarshal`, and
`merge`
- `Config::new()` default application after `ecstore::config::init()`
- existing persisted server-config JSON shape
- existing target, notify, audit, scanner, OIDC, and admin interpretation of
`Config` and `KVS`
## Next PR Requirements
`CFG-003` should be a pure model extraction or narrow `api-extraction` PR. It
must not migrate consumers, change persistence helpers, or alter runtime
behavior.
`CFG-004` kept the old `rustfs_ecstore::config::*` path as a temporary
compatibility shim, registered its removal condition, and removed the shim after
all in-repo consumers migrated.
`CFG-005` should migrate external consumers one group at a time after the model
and compatibility path are stable.
`CFG-008` moves only the global server-config snapshot accessors to
`rustfs-config` and migrates in-repo direct consumers. It must not move
`ConfigSys`, storage-class global state, persistence helpers, default
registration wiring, startup order, or storage behavior.
## Verification Gate
Before pushing an extraction PR, run:
- serde roundtrip tests for old and new paths
- tests for `hiddenIfEmpty` alias compatibility
- tests for `KVS` insertion, lookup, extension, and keys behavior
- tests for `Config::new`, `set_defaults`, `marshal`, `unmarshal`, and `merge`
- a cleanup scan proving in-repo consumers no longer use the old
`rustfs_ecstore::config::{Config, KV, KVS}` model path before removing the
compatibility shim
- `cargo tree -p rustfs-config --edges normal`
- `cargo tree -p rustfs-ecstore --edges normal`
- `./scripts/check_layer_dependencies.sh`
- `./scripts/check_architecture_migration_rules.sh`
- `cargo fmt --all --check`
- `make pre-commit`
## Non-Goals
- No consumer migration in `CFG-002`.
- No code movement in `CFG-002`.
- No new crate in `CFG-002`.
- No `com.rs` or `StorageAPI` movement in the first model extraction.
- No global server-config state migration until the model path is stable.