Files
rustfs/docs/architecture/config-model-boundary-adr.md
T

6.4 KiB

Config Model Boundary ADR

Related issue: rustfs/backlog#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:

crates/config/src/server_config.rs

The exported path should be:

rustfs_config::server_config::{Config, KV, KVS}

The extraction kept the existing path available through a temporary compatibility re-export:

rustfs_ecstore::config::{Config, KV, KVS}

That re-export included RUSTFS_COMPAT_TODO(CFG-004) and a matching entry in 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
  • ./scripts/check_metrics_migration_refs.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.