mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-06 21:33:14 +00:00
28f86a505e
Signed-off-by: houseme <housemecn@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
154 lines
4.7 KiB
Rust
154 lines
4.7 KiB
Rust
// 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 std::sync::atomic::{AtomicU8, Ordering};
|
|
|
|
/// Represents the various stages of system startup
|
|
#[repr(u8)]
|
|
pub enum SystemStage {
|
|
Booting = 0,
|
|
StorageReady = 1, // Disks online, Quorum met
|
|
IamReady = 2, // Users and Policies loaded into cache
|
|
FullReady = 3, // System ready to serve all traffic
|
|
}
|
|
|
|
/// Global readiness tracker for the service
|
|
/// This struct uses atomic operations to track the readiness status of various components
|
|
/// of the service in a thread-safe manner.
|
|
pub struct GlobalReadiness {
|
|
status: AtomicU8,
|
|
}
|
|
|
|
impl Default for GlobalReadiness {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl GlobalReadiness {
|
|
/// Create a new GlobalReadiness instance with initial status as Starting
|
|
/// # Returns
|
|
/// A new instance of GlobalReadiness
|
|
pub fn new() -> Self {
|
|
Self {
|
|
status: AtomicU8::new(SystemStage::Booting as u8),
|
|
}
|
|
}
|
|
|
|
/// Update the system to a new stage
|
|
///
|
|
/// # Arguments
|
|
/// * `step` - The SystemStage step to mark as ready
|
|
pub fn mark_stage(&self, step: SystemStage) {
|
|
self.status.fetch_max(step as u8, Ordering::SeqCst);
|
|
}
|
|
|
|
/// Check if the service is fully ready
|
|
/// # Returns
|
|
/// `true` if the service is fully ready, `false` otherwise
|
|
pub fn is_ready(&self) -> bool {
|
|
self.status.load(Ordering::SeqCst) == SystemStage::FullReady as u8
|
|
}
|
|
|
|
/// Get the current system stage
|
|
/// # Returns
|
|
/// The current SystemStage of the service
|
|
pub fn current_stage(&self) -> SystemStage {
|
|
match self.status.load(Ordering::SeqCst) {
|
|
0 => SystemStage::Booting,
|
|
1 => SystemStage::StorageReady,
|
|
2 => SystemStage::IamReady,
|
|
3 => SystemStage::FullReady,
|
|
invalid => {
|
|
debug_assert!(false, "GlobalReadiness::current_stage: invalid status value {}", invalid);
|
|
// Fallback to the most conservative stage on invalid values
|
|
SystemStage::Booting
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::sync::Arc;
|
|
use std::thread;
|
|
|
|
#[test]
|
|
fn test_initial_state() {
|
|
let readiness = GlobalReadiness::new();
|
|
assert!(!readiness.is_ready());
|
|
assert_eq!(readiness.status.load(Ordering::SeqCst), SystemStage::Booting as u8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_mark_stage_progression() {
|
|
let readiness = GlobalReadiness::new();
|
|
readiness.mark_stage(SystemStage::StorageReady);
|
|
assert!(!readiness.is_ready());
|
|
assert_eq!(readiness.status.load(Ordering::SeqCst), SystemStage::StorageReady as u8);
|
|
|
|
readiness.mark_stage(SystemStage::IamReady);
|
|
assert!(!readiness.is_ready());
|
|
assert_eq!(readiness.status.load(Ordering::SeqCst), SystemStage::IamReady as u8);
|
|
|
|
readiness.mark_stage(SystemStage::FullReady);
|
|
assert!(readiness.is_ready());
|
|
}
|
|
|
|
#[test]
|
|
fn test_no_regression() {
|
|
let readiness = GlobalReadiness::new();
|
|
readiness.mark_stage(SystemStage::FullReady);
|
|
readiness.mark_stage(SystemStage::IamReady); // Should not regress
|
|
assert!(readiness.is_ready());
|
|
}
|
|
|
|
#[test]
|
|
fn test_concurrent_marking() {
|
|
let readiness = Arc::new(GlobalReadiness::new());
|
|
let mut handles = vec![];
|
|
|
|
for _ in 0..10 {
|
|
let r = Arc::clone(&readiness);
|
|
handles.push(thread::spawn(move || {
|
|
r.mark_stage(SystemStage::StorageReady);
|
|
r.mark_stage(SystemStage::IamReady);
|
|
r.mark_stage(SystemStage::FullReady);
|
|
}));
|
|
}
|
|
|
|
for h in handles {
|
|
h.join().unwrap();
|
|
}
|
|
|
|
assert!(readiness.is_ready());
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_ready_only_at_full_ready() {
|
|
let readiness = GlobalReadiness::new();
|
|
assert!(!readiness.is_ready());
|
|
|
|
readiness.mark_stage(SystemStage::StorageReady);
|
|
assert!(!readiness.is_ready());
|
|
|
|
readiness.mark_stage(SystemStage::IamReady);
|
|
assert!(!readiness.is_ready());
|
|
|
|
readiness.mark_stage(SystemStage::FullReady);
|
|
assert!(readiness.is_ready());
|
|
}
|
|
}
|