mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-01 17:58:22 +00:00
refactor: centralize startup service bootstrap (#3448)
This commit is contained in:
@@ -5,18 +5,18 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
## Current Context
|
## Current Context
|
||||||
|
|
||||||
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
|
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
|
||||||
- Branch: `overtrue/arch-startup-readiness-bootstrap`
|
- Branch: `overtrue/arch-startup-service-bootstrap`
|
||||||
- Baseline: `origin/main` at `8d23ce06c6dba11f50f656af4c30b63036cef92f`
|
- Baseline: `origin/main` at `b49057c8e3e0b1a0ed828912806ee67b49e404c4`
|
||||||
- PR type for this branch: `pure-move`
|
- PR type for this branch: `pure-move`
|
||||||
- Runtime behavior changes: no external behavior change expected; inline IAM
|
- Runtime behavior changes: no external behavior change expected; binary startup
|
||||||
bootstrap still publishes runtime readiness after runtime dependencies are
|
still treats notification initialization failure as fatal, while embedded
|
||||||
ready, and deferred IAM bootstrap still leaves readiness publication to the
|
startup still treats audit and notification failures as non-fatal warnings.
|
||||||
recovery loop.
|
- Rust code changes: centralize event notifier, audit startup, and notification
|
||||||
- Rust code changes: centralize the IAM bootstrap readiness publication decision
|
system bootstrap in `startup_services` helpers with caller-owned
|
||||||
in `startup_iam`, use it from binary and embedded startup, and add
|
logging/error policy, then use those helpers from binary and embedded
|
||||||
wrapper-level coverage for inline, deferred, and failure paths.
|
startup.
|
||||||
- CI/script changes: none.
|
- CI/script changes: none.
|
||||||
- Docs changes: record `R-009` startup readiness bootstrap wrapper progress and
|
- Docs changes: record `R-010` startup service bootstrap wrapper progress and
|
||||||
verification.
|
verification.
|
||||||
|
|
||||||
## Phase 0 Tasks
|
## Phase 0 Tasks
|
||||||
@@ -565,6 +565,19 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
- Verification: focused startup IAM tests, binary/lib compile checks,
|
- Verification: focused startup IAM tests, binary/lib compile checks,
|
||||||
formatting, migration guards, Rust risk scan, and pre-commit quality gate.
|
formatting, migration guards, Rust risk scan, and pre-commit quality gate.
|
||||||
|
|
||||||
|
- [x] `R-010` Centralize startup optional service bootstrap.
|
||||||
|
- Do: move event notifier, audit startup, and notification system startup
|
||||||
|
behind `startup_services` helpers with caller-owned logging/error policy.
|
||||||
|
- Acceptance: binary still initializes the event notifier before audit, logs
|
||||||
|
audit start/failure through the same startup target, and treats notification
|
||||||
|
init failure as fatal; embedded still treats audit and notification failures
|
||||||
|
as non-fatal warnings.
|
||||||
|
- Must preserve: startup order, audit non-fatal behavior, notification fatal
|
||||||
|
boundary in binary, embedded warn-and-continue behavior, and event notifier
|
||||||
|
initialization.
|
||||||
|
- Verification: focused startup service tests, binary/lib compile checks,
|
||||||
|
formatting, migration guards, Rust risk scan, and pre-commit quality gate.
|
||||||
|
|
||||||
## Next PRs
|
## Next PRs
|
||||||
|
|
||||||
1. `pure-move`: continue extracting startup boot wrappers in larger slices while
|
1. `pure-move`: continue extracting startup boot wrappers in larger slices while
|
||||||
@@ -576,38 +589,37 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
|
|
||||||
| Expert | Status | Notes |
|
| Expert | Status | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Quality/architecture | passed | Pure-move slice centralizes IAM bootstrap readiness publication without moving runtime readiness collection or startup dependency checks. |
|
| Quality/architecture | passed | Pure-move slice centralizes optional startup service bootstrap while keeping caller-specific logging and error policy at the binary and embedded boundaries. |
|
||||||
| Migration preservation | passed | Main and embedded keep ReadyInline publication behavior, Deferred remains recovery-loop owned, and embedded readiness errors still map through shutdown. |
|
| Migration preservation | passed | Binary keeps notification init fatal, embedded keeps audit and notification failures warn-and-continue, and event notifier still starts before audit. |
|
||||||
| Testing/verification | passed | Focused startup IAM tests, compile checks, formatting, migration/layer guards, Rust risk scan, branch freshness check, and full `make pre-commit` passed. |
|
| Testing/verification | passed | Focused startup service tests, compile checks, formatting, migration/layer guards, Rust risk scan, branch freshness check, and full `make pre-commit` passed. |
|
||||||
|
|
||||||
## Verification Notes
|
## Verification Notes
|
||||||
|
|
||||||
Passed on `8d23ce06c6dba11f50f656af4c30b63036cef92f`:
|
Passed on `b49057c8e3e0b1a0ed828912806ee67b49e404c4`:
|
||||||
|
|
||||||
- `cargo fmt --all --check`.
|
- `cargo test -p rustfs startup_services --no-fail-fast`.
|
||||||
- `cargo test -p rustfs startup_iam --no-fail-fast`.
|
|
||||||
- `cargo check -p rustfs --bin rustfs`.
|
- `cargo check -p rustfs --bin rustfs`.
|
||||||
- `cargo check -p rustfs --lib`.
|
- `cargo check -p rustfs --lib`.
|
||||||
|
- `cargo fmt --all --check`.
|
||||||
- `git diff --check`.
|
- `git diff --check`.
|
||||||
- `./scripts/check_architecture_migration_rules.sh`.
|
- `./scripts/check_architecture_migration_rules.sh`.
|
||||||
- `./scripts/check_layer_dependencies.sh`.
|
- `./scripts/check_layer_dependencies.sh`.
|
||||||
- `git rev-list --left-right --count HEAD...origin/main` returned `0 0`.
|
- `git rev-list --left-right --count HEAD...origin/main` returned `0 0`.
|
||||||
- Rust risk scan for changed Rust files: full-file matches were existing docs
|
- Rust risk scan for changed Rust files: full-file matches were existing docs
|
||||||
examples, existing startup error output, existing test expectations, and the
|
examples, existing startup error output, and existing startup imports; added
|
||||||
existing boxed recovery future; added-line scan returned no unwrap/expect,
|
lines introduced no unwrap/expect, numeric cast, string error, boxed error,
|
||||||
numeric cast, string error, boxed error, print macro, relaxed-ordering, or
|
print macro, relaxed-ordering, or unsafe match.
|
||||||
unsafe match.
|
- `make pre-commit`: all checks passed, including nextest with 5969 passed and
|
||||||
- `make pre-commit`: all checks passed, including nextest with 5966 passed and
|
|
||||||
111 skipped, plus doctests.
|
111 skipped, plus doctests.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- This slice centralizes startup IAM readiness publication without changing the
|
- This slice centralizes startup optional service bootstrap without moving
|
||||||
runtime readiness checks themselves.
|
logging or error-policy ownership out of binary and embedded startup.
|
||||||
- Deferred IAM bootstrap readiness remains owned by the recovery loop.
|
- Binary and embedded keep their existing notification failure semantics.
|
||||||
|
|
||||||
## Handoff Notes
|
## Handoff Notes
|
||||||
|
|
||||||
- R-009 is complete.
|
- R-010 is complete.
|
||||||
- Next startup slices can be larger pure moves, but must keep startup ordering
|
- Next startup slices can be larger pure moves, but must keep startup ordering,
|
||||||
and readiness ownership explicit in tests.
|
fatal/non-fatal boundaries, and readiness ownership explicit in tests.
|
||||||
|
|||||||
+3
-11
@@ -48,9 +48,7 @@
|
|||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::init::{add_bucket_notification_configuration, init_buffer_profile_system, init_kms_system};
|
use crate::init::{add_bucket_notification_configuration, init_buffer_profile_system, init_kms_system};
|
||||||
use crate::server::{
|
use crate::server::{ShutdownHandle, shutdown_event_notifier, start_http_server, stop_audit_system};
|
||||||
ShutdownHandle, init_event_notifier, shutdown_event_notifier, start_audit_system, start_http_server, stop_audit_system,
|
|
||||||
};
|
|
||||||
use crate::startup_fs_guard::enforce_unsupported_fs_policy;
|
use crate::startup_fs_guard::enforce_unsupported_fs_policy;
|
||||||
use crate::startup_iam::{bootstrap_or_defer_iam_init, publish_ready_for_iam_bootstrap};
|
use crate::startup_iam::{bootstrap_or_defer_iam_init, publish_ready_for_iam_bootstrap};
|
||||||
use rustfs_common::{GlobalReadiness, SystemStage, set_global_addr};
|
use rustfs_common::{GlobalReadiness, SystemStage, set_global_addr};
|
||||||
@@ -65,7 +63,6 @@ use rustfs_ecstore::{
|
|||||||
config as ecconfig,
|
config as ecconfig,
|
||||||
endpoints::EndpointServerPools,
|
endpoints::EndpointServerPools,
|
||||||
global::set_global_rustfs_port,
|
global::set_global_rustfs_port,
|
||||||
notification_sys::new_global_notification_sys,
|
|
||||||
set_global_endpoints,
|
set_global_endpoints,
|
||||||
store::ECStore,
|
store::ECStore,
|
||||||
store::init_local_disks,
|
store::init_local_disks,
|
||||||
@@ -435,11 +432,7 @@ impl RustFSServerBuilder {
|
|||||||
// Buffer profiles.
|
// Buffer profiles.
|
||||||
init_buffer_profile_system(&config);
|
init_buffer_profile_system(&config);
|
||||||
|
|
||||||
// Event notifier.
|
if let Err(e) = crate::startup_services::init_event_notifier_and_audit().await {
|
||||||
init_event_notifier().await;
|
|
||||||
|
|
||||||
// Audit (non-fatal).
|
|
||||||
if let Err(e) = start_audit_system().await {
|
|
||||||
warn!(
|
warn!(
|
||||||
component = LOG_COMPONENT_EMBEDDED,
|
component = LOG_COMPONENT_EMBEDDED,
|
||||||
subsystem = LOG_SUBSYSTEM_EMBEDDED,
|
subsystem = LOG_SUBSYSTEM_EMBEDDED,
|
||||||
@@ -482,8 +475,7 @@ impl RustFSServerBuilder {
|
|||||||
// Bucket notifications.
|
// Bucket notifications.
|
||||||
add_bucket_notification_configuration(buckets.clone()).await;
|
add_bucket_notification_configuration(buckets.clone()).await;
|
||||||
|
|
||||||
// Notification system.
|
if let Err(e) = crate::startup_services::init_notification_system(endpoint_pools.clone()).await {
|
||||||
if let Err(e) = new_global_notification_sys(endpoint_pools.clone()).await {
|
|
||||||
warn!(
|
warn!(
|
||||||
component = LOG_COMPONENT_EMBEDDED,
|
component = LOG_COMPONENT_EMBEDDED,
|
||||||
subsystem = LOG_SUBSYSTEM_EMBEDDED,
|
subsystem = LOG_SUBSYSTEM_EMBEDDED,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ pub mod protocols;
|
|||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod startup_fs_guard;
|
pub mod startup_fs_guard;
|
||||||
pub mod startup_iam;
|
pub mod startup_iam;
|
||||||
|
pub mod startup_services;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub(crate) mod table_catalog;
|
pub(crate) mod table_catalog;
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
|
|||||||
+16
-20
@@ -30,8 +30,8 @@ use futures_util::future::join_all;
|
|||||||
use rustfs::capacity::capacity_integration::init_capacity_management;
|
use rustfs::capacity::capacity_integration::init_capacity_management;
|
||||||
use rustfs::license::{init_license, license_status};
|
use rustfs::license::{init_license, license_status};
|
||||||
use rustfs::server::{
|
use rustfs::server::{
|
||||||
ServiceState, ServiceStateManager, ShutdownHandle, ShutdownSignal, init_event_notifier, shutdown_event_notifier,
|
ServiceState, ServiceStateManager, ShutdownHandle, ShutdownSignal, shutdown_event_notifier, start_http_server,
|
||||||
start_audit_system, start_http_server, stop_audit_system, wait_for_shutdown,
|
stop_audit_system, wait_for_shutdown,
|
||||||
};
|
};
|
||||||
use rustfs::startup_fs_guard::enforce_unsupported_fs_policy;
|
use rustfs::startup_fs_guard::enforce_unsupported_fs_policy;
|
||||||
use rustfs::startup_iam::{bootstrap_or_defer_iam_init, publish_ready_for_iam_bootstrap};
|
use rustfs::startup_iam::{bootstrap_or_defer_iam_init, publish_ready_for_iam_bootstrap};
|
||||||
@@ -45,7 +45,6 @@ use rustfs_ecstore::{
|
|||||||
config as ecconfig,
|
config as ecconfig,
|
||||||
endpoints::EndpointServerPools,
|
endpoints::EndpointServerPools,
|
||||||
global::{set_global_rustfs_port, shutdown_background_services},
|
global::{set_global_rustfs_port, shutdown_background_services},
|
||||||
notification_sys::new_global_notification_sys,
|
|
||||||
set_global_endpoints,
|
set_global_endpoints,
|
||||||
store::ECStore,
|
store::ECStore,
|
||||||
store::init_local_disks,
|
store::init_local_disks,
|
||||||
@@ -778,12 +777,8 @@ async fn run(config: rustfs::config::Config) -> Result<()> {
|
|||||||
// Initialize buffer profiling system
|
// Initialize buffer profiling system
|
||||||
init_buffer_profile_system(&config);
|
init_buffer_profile_system(&config);
|
||||||
|
|
||||||
// Initialize event notifier
|
match rustfs::startup_services::init_event_notifier_and_audit().await {
|
||||||
init_event_notifier().await;
|
Ok(()) => info!(
|
||||||
|
|
||||||
// Start the audit system
|
|
||||||
match start_audit_system().await {
|
|
||||||
Ok(_) => info!(
|
|
||||||
target: "rustfs::main::run",
|
target: "rustfs::main::run",
|
||||||
event = EVENT_AUDIT_SYSTEM_STATE,
|
event = EVENT_AUDIT_SYSTEM_STATE,
|
||||||
component = LOG_COMPONENT_MAIN,
|
component = LOG_COMPONENT_MAIN,
|
||||||
@@ -893,17 +888,18 @@ async fn run(config: rustfs::config::Config) -> Result<()> {
|
|||||||
|
|
||||||
add_bucket_notification_configuration(buckets.clone()).await;
|
add_bucket_notification_configuration(buckets.clone()).await;
|
||||||
|
|
||||||
// Initialize the global notification system
|
rustfs::startup_services::init_notification_system(endpoint_pools.clone())
|
||||||
new_global_notification_sys(endpoint_pools.clone()).await.map_err(|err| {
|
.await
|
||||||
error!(
|
.map_err(|err| {
|
||||||
event = EVENT_NOTIFICATION_SYSTEM_INITIALIZATION_FAILED,
|
error!(
|
||||||
component = LOG_COMPONENT_MAIN,
|
event = EVENT_NOTIFICATION_SYSTEM_INITIALIZATION_FAILED,
|
||||||
subsystem = LOG_SUBSYSTEM_STARTUP,
|
component = LOG_COMPONENT_MAIN,
|
||||||
error = ?err,
|
subsystem = LOG_SUBSYSTEM_STARTUP,
|
||||||
"Failed to initialize notification system"
|
error = ?err,
|
||||||
);
|
"Failed to initialize notification system"
|
||||||
Error::other(err)
|
);
|
||||||
})?;
|
Error::other(err)
|
||||||
|
})?;
|
||||||
|
|
||||||
// Create a cancellation token for AHM services
|
// Create a cancellation token for AHM services
|
||||||
let _ = create_ahm_services_cancel_token();
|
let _ = create_ahm_services_cancel_token();
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// 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 crate::server::{init_event_notifier, start_audit_system};
|
||||||
|
use rustfs_audit::AuditResult;
|
||||||
|
use rustfs_ecstore::endpoints::EndpointServerPools;
|
||||||
|
use rustfs_ecstore::notification_sys::new_global_notification_sys;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pub async fn init_event_notifier_and_audit() -> AuditResult<()> {
|
||||||
|
init_event_notifier_and_audit_with(init_event_notifier, start_audit_system).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_event_notifier_and_audit_with<NotifyFn, NotifyFuture, AuditFn, AuditFuture>(
|
||||||
|
notify: NotifyFn,
|
||||||
|
start_audit: AuditFn,
|
||||||
|
) -> AuditResult<()>
|
||||||
|
where
|
||||||
|
NotifyFn: FnOnce() -> NotifyFuture,
|
||||||
|
NotifyFuture: Future<Output = ()>,
|
||||||
|
AuditFn: FnOnce() -> AuditFuture,
|
||||||
|
AuditFuture: Future<Output = AuditResult<()>>,
|
||||||
|
{
|
||||||
|
notify().await;
|
||||||
|
start_audit().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_notification_system(endpoint_pools: EndpointServerPools) -> rustfs_ecstore::error::Result<()> {
|
||||||
|
init_notification_system_with(|| new_global_notification_sys(endpoint_pools)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_notification_system_with<InitFn, InitFuture>(init_notification: InitFn) -> rustfs_ecstore::error::Result<()>
|
||||||
|
where
|
||||||
|
InitFn: FnOnce() -> InitFuture,
|
||||||
|
InitFuture: Future<Output = rustfs_ecstore::error::Result<()>>,
|
||||||
|
{
|
||||||
|
init_notification().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{init_event_notifier_and_audit_with, init_notification_system_with};
|
||||||
|
use rustfs_audit::AuditError;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn event_notifier_runs_before_successful_audit_start() {
|
||||||
|
let events = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let notify_events = events.clone();
|
||||||
|
let audit_events = events.clone();
|
||||||
|
let result = init_event_notifier_and_audit_with(
|
||||||
|
move || async move {
|
||||||
|
notify_events.lock().unwrap_or_else(|err| err.into_inner()).push("notify");
|
||||||
|
},
|
||||||
|
move || async move {
|
||||||
|
audit_events.lock().unwrap_or_else(|err| err.into_inner()).push("audit");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let events = events.lock().unwrap_or_else(|err| err.into_inner()).clone();
|
||||||
|
assert_eq!(events, ["notify", "audit"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn event_notifier_runs_before_failed_audit_result() {
|
||||||
|
let events = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let notify_events = events.clone();
|
||||||
|
let audit_events = events.clone();
|
||||||
|
|
||||||
|
let result = init_event_notifier_and_audit_with(
|
||||||
|
move || async move {
|
||||||
|
notify_events.lock().unwrap_or_else(|err| err.into_inner()).push("notify");
|
||||||
|
},
|
||||||
|
move || async move {
|
||||||
|
audit_events.lock().unwrap_or_else(|err| err.into_inner()).push("audit");
|
||||||
|
Err(AuditError::ConfigNotLoaded)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
let events = events.lock().unwrap_or_else(|err| err.into_inner()).clone();
|
||||||
|
assert_eq!(events, ["notify", "audit"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn notification_system_returns_source_error() {
|
||||||
|
let result = init_notification_system_with(|| async { Err(rustfs_ecstore::error::Error::FaultyDisk) }).await;
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user