mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-28 16:07:05 +00:00
refactor: move startup app context owner (#3670)
This commit is contained in:
@@ -2460,6 +2460,30 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
migration/layer guards, formatting, diff hygiene, Rust risk scan, branch
|
migration/layer guards, formatting, diff hygiene, Rust risk scan, branch
|
||||||
freshness check, pre-commit quality gate, and three-expert review.
|
freshness check, pre-commit quality gate, and three-expert review.
|
||||||
|
|
||||||
|
- [x] `R-054` Move startup AppContext bootstrap owner into app context.
|
||||||
|
- Do: move the post-IAM AppContext bootstrap helper out of IAM startup and
|
||||||
|
into the app context owner while keeping IAM startup on the existing
|
||||||
|
context boundary.
|
||||||
|
- Acceptance: inline startup and deferred IAM recovery still initialize or
|
||||||
|
reuse the global AppContext through one owner.
|
||||||
|
- Must preserve: global AppContext singleton behavior, IAM handle lookup,
|
||||||
|
KMS handle wiring, startup error mapping, and readiness ordering.
|
||||||
|
- Verification: focused startup checks, RustFS lib check, migration/layer
|
||||||
|
guards, formatting, diff hygiene, Rust risk scan, branch freshness check,
|
||||||
|
pre-commit quality gate, and three-expert review.
|
||||||
|
|
||||||
|
- [x] `R-055` Retire stale startup IAM layer baseline entries.
|
||||||
|
- Do: remove the old direct `get_global_app_context` and
|
||||||
|
`init_global_app_context` startup IAM baseline entries after the app
|
||||||
|
context owner absorbs those calls.
|
||||||
|
- Acceptance: layer dependency guard reports no new reverse dependencies and
|
||||||
|
no stale baseline entries.
|
||||||
|
- Must preserve: the existing accepted startup-to-AppContext boundary,
|
||||||
|
AppContext initialization semantics, and no new layer cycles.
|
||||||
|
- Verification: focused startup checks, RustFS lib check, migration/layer
|
||||||
|
guards, formatting, diff hygiene, Rust risk scan, branch freshness check,
|
||||||
|
pre-commit quality gate, and three-expert review.
|
||||||
|
|
||||||
- [x] `E-001/E-SET-001` Add ECStore layout skeleton and set-layout boundary.
|
- [x] `E-001/E-SET-001` Add ECStore layout skeleton and set-layout boundary.
|
||||||
- Do: create the ECStore internal layout ownership buckets and pin static set
|
- Do: create the ECStore internal layout ownership buckets and pin static set
|
||||||
layout versus runtime `Sets`/`SetDisks` orchestration boundaries before any
|
layout versus runtime `Sets`/`SetDisks` orchestration boundaries before any
|
||||||
@@ -2702,21 +2726,34 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
|
|
||||||
## Next PRs
|
## Next PRs
|
||||||
|
|
||||||
1. `pure-move`: continue the context-first runtime startup path after the
|
1. `pure-move`: continue the context-first runtime startup path by narrowing
|
||||||
embedded startup ownership slices land.
|
remaining startup service ownership boundaries.
|
||||||
|
|
||||||
## Pre-Push Review Log
|
## Pre-Push Review Log
|
||||||
|
|
||||||
| Expert | Status | Notes |
|
| Expert | Status | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Quality/architecture | passed | R-052 and R-053 make AppContext bootstrap outcome explicit and share inline/recovery finalization without widening public API. |
|
| Quality/architecture | passed | R-054 and R-055 move AppContext bootstrap ownership into app context and shrink stale layer baseline entries without widening public API. |
|
||||||
| Migration preservation | passed | IAM init order, singleton reuse, KMS/IAM handles, degraded recovery, backoff/shutdown token, readiness stages, and log behavior stay unchanged. |
|
| Migration preservation | passed | IAM init order, singleton reuse, KMS handle wiring, degraded recovery, readiness stages, and log behavior stay unchanged. |
|
||||||
| Testing/verification | passed | Focused startup IAM checks, RustFS lib check, architecture/layer/unsafe guards, formatting, diff hygiene, Rust risk scan, and pre-commit gate passed. |
|
| Testing/verification | passed | Focused startup checks, RustFS lib check, architecture/layer/unsafe guards, formatting, diff hygiene, Rust risk scan, and pre-commit gate passed. |
|
||||||
|
|
||||||
## Verification Notes
|
## Verification Notes
|
||||||
|
|
||||||
Passed before push:
|
Passed before push:
|
||||||
|
|
||||||
|
- Issue #660 R-054/R-055 current slice:
|
||||||
|
- `cargo test -p rustfs --lib startup_ -- --nocapture`: passed; 51 tests.
|
||||||
|
- `cargo check -p rustfs --lib`: passed.
|
||||||
|
- `cargo fmt --all --check`: passed.
|
||||||
|
- `git diff --check`: passed.
|
||||||
|
- `./scripts/check_architecture_migration_rules.sh`: passed.
|
||||||
|
- `./scripts/check_layer_dependencies.sh`: passed.
|
||||||
|
- `./scripts/check_unsafe_code_allowances.sh`: passed.
|
||||||
|
- Rust risk scan on changed Rust files: passed; only a test-only `expect`
|
||||||
|
call was present.
|
||||||
|
- `make pre-commit`: passed; nextest ran 6339 tests with 6339 passed, 111
|
||||||
|
skipped, and doctests passed.
|
||||||
|
|
||||||
- Issue #660 R-052/R-053 current slice:
|
- Issue #660 R-052/R-053 current slice:
|
||||||
- `cargo test -p rustfs --lib startup_iam -- --nocapture`: passed.
|
- `cargo test -p rustfs --lib startup_iam -- --nocapture`: passed.
|
||||||
- `cargo check -p rustfs --lib`: passed.
|
- `cargo check -p rustfs --lib`: passed.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ mod compat;
|
|||||||
mod global;
|
mod global;
|
||||||
mod handles;
|
mod handles;
|
||||||
mod interfaces;
|
mod interfaces;
|
||||||
|
mod startup;
|
||||||
|
|
||||||
pub use compat::*;
|
pub use compat::*;
|
||||||
pub use global::*;
|
pub use global::*;
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use super::global::{AppContext, get_global_app_context, init_global_app_context};
|
||||||
|
use crate::app::storage_compat::ECStore;
|
||||||
|
use rustfs_kms::KmsServiceManager;
|
||||||
|
use std::io::{Error, Result};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum StartupAppContextBootstrap {
|
||||||
|
AlreadyAvailable,
|
||||||
|
Initialized,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppContext {
|
||||||
|
pub(crate) fn ensure_startup_after_iam(store: Arc<ECStore>, kms_interface: Arc<KmsServiceManager>) -> Result<()> {
|
||||||
|
ensure_startup_app_context_after_iam_with(
|
||||||
|
|| get_global_app_context().is_some(),
|
||||||
|
|| {
|
||||||
|
let iam_interface = rustfs_iam::get().map_err(|_| Error::other("IAM is initialized but unavailable"))?;
|
||||||
|
init_global_app_context(AppContext::with_default_interfaces(store, iam_interface, kms_interface));
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_startup_app_context_after_iam_with<IsAvailable, InitContext>(
|
||||||
|
is_available: IsAvailable,
|
||||||
|
init_context: InitContext,
|
||||||
|
) -> Result<StartupAppContextBootstrap>
|
||||||
|
where
|
||||||
|
IsAvailable: FnOnce() -> bool,
|
||||||
|
InitContext: FnOnce() -> Result<()>,
|
||||||
|
{
|
||||||
|
if is_available() {
|
||||||
|
return Ok(StartupAppContextBootstrap::AlreadyAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_context()?;
|
||||||
|
Ok(StartupAppContextBootstrap::Initialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{StartupAppContextBootstrap, ensure_startup_app_context_after_iam_with};
|
||||||
|
use std::io::Error;
|
||||||
|
use std::sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn startup_app_context_bootstrap_reuses_existing_context() {
|
||||||
|
let init_calls = Arc::new(AtomicUsize::new(0));
|
||||||
|
let init_calls_for_assert = init_calls.clone();
|
||||||
|
|
||||||
|
let disposition = ensure_startup_app_context_after_iam_with(
|
||||||
|
|| true,
|
||||||
|
move || {
|
||||||
|
init_calls.fetch_add(1, Ordering::SeqCst);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("existing app context should be reused");
|
||||||
|
|
||||||
|
assert_eq!(disposition, StartupAppContextBootstrap::AlreadyAvailable);
|
||||||
|
assert_eq!(init_calls_for_assert.load(Ordering::SeqCst), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn startup_app_context_bootstrap_initializes_missing_context() {
|
||||||
|
let init_calls = Arc::new(AtomicUsize::new(0));
|
||||||
|
let init_calls_for_assert = init_calls.clone();
|
||||||
|
|
||||||
|
let disposition = ensure_startup_app_context_after_iam_with(
|
||||||
|
|| false,
|
||||||
|
move || {
|
||||||
|
init_calls.fetch_add(1, Ordering::SeqCst);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("missing app context should initialize");
|
||||||
|
|
||||||
|
assert_eq!(disposition, StartupAppContextBootstrap::Initialized);
|
||||||
|
assert_eq!(init_calls_for_assert.load(Ordering::SeqCst), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn startup_app_context_bootstrap_returns_init_error() {
|
||||||
|
let err = ensure_startup_app_context_after_iam_with(|| false, || Err(Error::other("iam unavailable")))
|
||||||
|
.expect_err("init failure should be returned");
|
||||||
|
|
||||||
|
assert_eq!(err.to_string(), "iam unavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::app::context::{AppContext, get_global_app_context, init_global_app_context};
|
use crate::app::context::AppContext;
|
||||||
use crate::server::{ServiceStateManager, publish_ready_when_runtime_ready};
|
use crate::server::{ServiceStateManager, publish_ready_when_runtime_ready};
|
||||||
use crate::storage_compat::ECStore;
|
use crate::storage_compat::ECStore;
|
||||||
use rustfs_common::{GlobalReadiness, SystemStage};
|
use rustfs_common::{GlobalReadiness, SystemStage};
|
||||||
@@ -44,12 +44,6 @@ pub enum IamBootstrapDisposition {
|
|||||||
Deferred,
|
Deferred,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum AppContextBootstrapDisposition {
|
|
||||||
AlreadyAvailable,
|
|
||||||
Initialized,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn publish_ready_for_iam_bootstrap(
|
pub async fn publish_ready_for_iam_bootstrap(
|
||||||
disposition: IamBootstrapDisposition,
|
disposition: IamBootstrapDisposition,
|
||||||
readiness: &GlobalReadiness,
|
readiness: &GlobalReadiness,
|
||||||
@@ -77,27 +71,13 @@ where
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_app_context_after_iam(
|
|
||||||
store: Arc<ECStore>,
|
|
||||||
kms_interface: Arc<KmsServiceManager>,
|
|
||||||
) -> Result<AppContextBootstrapDisposition> {
|
|
||||||
if get_global_app_context().is_some() {
|
|
||||||
return Ok(AppContextBootstrapDisposition::AlreadyAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
let iam_interface = rustfs_iam::get().map_err(|_| std::io::Error::other("IAM is initialized but unavailable"))?;
|
|
||||||
|
|
||||||
init_global_app_context(AppContext::with_default_interfaces(store, iam_interface, kms_interface));
|
|
||||||
Ok(AppContextBootstrapDisposition::Initialized)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn finalize_iam_recovery(
|
async fn finalize_iam_recovery(
|
||||||
store: Arc<ECStore>,
|
store: Arc<ECStore>,
|
||||||
kms_interface: Arc<KmsServiceManager>,
|
kms_interface: Arc<KmsServiceManager>,
|
||||||
readiness: Arc<GlobalReadiness>,
|
readiness: Arc<GlobalReadiness>,
|
||||||
state_manager: Option<Arc<ServiceStateManager>>,
|
state_manager: Option<Arc<ServiceStateManager>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
ensure_app_context_after_iam(store, kms_interface)?;
|
AppContext::ensure_startup_after_iam(store, kms_interface)?;
|
||||||
|
|
||||||
readiness.mark_stage(SystemStage::IamReady);
|
readiness.mark_stage(SystemStage::IamReady);
|
||||||
publish_ready_when_runtime_ready(readiness.as_ref(), state_manager.as_deref()).await
|
publish_ready_when_runtime_ready(readiness.as_ref(), state_manager.as_deref()).await
|
||||||
@@ -335,7 +315,7 @@ pub async fn bootstrap_or_defer_iam_init(
|
|||||||
) -> Result<IamBootstrapDisposition> {
|
) -> Result<IamBootstrapDisposition> {
|
||||||
match attempt_init_iam_sys(store.clone()).await {
|
match attempt_init_iam_sys(store.clone()).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
ensure_app_context_after_iam(store, kms_interface)?;
|
AppContext::ensure_startup_after_iam(store, kms_interface)?;
|
||||||
readiness.mark_stage(SystemStage::IamReady);
|
readiness.mark_stage(SystemStage::IamReady);
|
||||||
return Ok(IamBootstrapDisposition::ReadyInline);
|
return Ok(IamBootstrapDisposition::ReadyInline);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ accepted|rustfs/src/server/layer.rs|infra->interface|crate::admin::console::is_c
|
|||||||
accepted|rustfs/src/server/layer.rs|infra->interface|crate::admin::handlers::health::HealthProbe|HTTP layer builds health responses
|
accepted|rustfs/src/server/layer.rs|infra->interface|crate::admin::handlers::health::HealthProbe|HTTP layer builds health responses
|
||||||
accepted|rustfs/src/server/layer.rs|infra->interface|crate::admin::handlers::health::build_health_response_parts|HTTP layer builds health responses
|
accepted|rustfs/src/server/layer.rs|infra->interface|crate::admin::handlers::health::build_health_response_parts|HTTP layer builds health responses
|
||||||
accepted|rustfs/src/startup_iam.rs|infra->app|crate::app::context::AppContext|startup wires IAM bootstrap through AppContext
|
accepted|rustfs/src/startup_iam.rs|infra->app|crate::app::context::AppContext|startup wires IAM bootstrap through AppContext
|
||||||
accepted|rustfs/src/startup_iam.rs|infra->app|crate::app::context::get_global_app_context|startup wires IAM bootstrap through AppContext
|
|
||||||
accepted|rustfs/src/startup_iam.rs|infra->app|crate::app::context::init_global_app_context|startup wires IAM bootstrap through AppContext
|
|
||||||
accepted|rustfs/src/storage/ecfs_extend.rs|infra->interface|crate::storage::ecfs::ListObjectUnorderedQuery|storage extension uses current ECFS interface query type
|
accepted|rustfs/src/storage/ecfs_extend.rs|infra->interface|crate::storage::ecfs::ListObjectUnorderedQuery|storage extension uses current ECFS interface query type
|
||||||
accepted|rustfs/src/storage/ecfs_test.rs|infra->interface|crate::storage::ecfs::FS|storage tests exercise current ECFS interface path
|
accepted|rustfs/src/storage/ecfs_test.rs|infra->interface|crate::storage::ecfs::FS|storage tests exercise current ECFS interface path
|
||||||
accepted|rustfs/src/storage/ecfs_test.rs|infra->interface|crate::storage::ecfs::validate_object_lock_configuration_input|storage tests exercise current ECFS object-lock validator
|
accepted|rustfs/src/storage/ecfs_test.rs|infra->interface|crate::storage::ecfs::validate_object_lock_configuration_input|storage tests exercise current ECFS object-lock validator
|
||||||
|
|||||||
Reference in New Issue
Block a user