mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
refactor(storage): resolve staged dead code in store module (#730)
Drop the store module's blanket #![allow(dead_code)] and resolve every unit it was masking, instead of keeping them staged: - write_persistent_key_only_index: the keys-only writer wrapper only had test callers; the in-process rebuild flow already exists and uses the _with_metadata variant (prepare -> rebuild, triggered lazily from the opt-in listing path). Tests call _with_metadata directly now. - ListIndexLifecycle::recover_after_restart / mark_corrupt: production derives index health per request from the persisted artifacts (index file + namespace mutation journal), and restart recovery already lives in load_persistent_key_only_index's journal restore. The persisted- lifecycle design these transitions served was superseded, and Corrupt had no detector, so the never-constructed Corrupt state/reason variants go with them. - record_list_objects_index_opt_in_fallback (+ helpers): superseded by the inline per-site fallback recording in the opt-in listing path; the recorder recomputed health with a hardcoded None provider, so it could only ever report a disabled-based reason. - Provider-contract traits (Generation/Page/PageIterator/KeyLookup): provider dispatch went the ListObjectsIndexProviderKind enum route; only ListMetadataIndexHealth is live (dyn in source-mode selection) and stays, minus its unused is_healthy default. - Delete the unused list_quorum_from_env/RUSTFS_API_LIST_QUORUM pair, fold should_resume_local_decommission and should_auto_start_rebalance_after_recovered_meta into their surviving primitives, drop get_disk_via_endpoint, and cfg(test) the remaining test-only conveniences.
This commit is contained in:
@@ -49,21 +49,6 @@ fn resolve_startup_pool_defaults_with(
|
||||
drive_counts.into_iter().map(ec_drives_no_config).collect()
|
||||
}
|
||||
|
||||
fn should_resume_local_decommission(endpoints: &EndpointServerPools, idx: usize) -> Result<bool> {
|
||||
let pool = endpoints.as_ref().get(idx).ok_or_else(|| {
|
||||
Error::other(format!(
|
||||
"store init failed to resolve decommission resume pool index {idx} from current endpoints"
|
||||
))
|
||||
})?;
|
||||
let endpoint = pool.endpoints.as_ref().first().ok_or_else(|| {
|
||||
Error::other(format!(
|
||||
"store init failed to resolve decommission resume pool index {idx}: no endpoints available"
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(endpoint.is_local)
|
||||
}
|
||||
|
||||
const LOCAL_DECOMMISSION_RESUME_MAX_CONFIG_RETRIES: usize = 6;
|
||||
const LOCAL_DECOMMISSION_INITIAL_RESUME_DELAY: Duration = Duration::from_secs(60 * 3);
|
||||
const LOCAL_DECOMMISSION_RESUME_RETRY_DELAY: Duration = Duration::from_secs(30);
|
||||
@@ -84,10 +69,6 @@ fn pool_meta_has_active_decommission(meta: &PoolMeta) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
fn should_auto_start_rebalance_after_recovered_meta(pool_meta: &PoolMeta, rebalance_meta_loaded: bool) -> bool {
|
||||
should_auto_start_rebalance_after_init(pool_meta_has_active_decommission(pool_meta), rebalance_meta_loaded)
|
||||
}
|
||||
|
||||
async fn wait_for_local_decommission_resume_delay(rx: &CancellationToken, delay: Duration) -> bool {
|
||||
tokio::select! {
|
||||
_ = rx.cancelled() => false,
|
||||
@@ -522,9 +503,9 @@ impl ECStore {
|
||||
mod tests {
|
||||
use super::{
|
||||
LOCAL_DECOMMISSION_RESUME_MAX_CONFIG_RETRIES, load_pool_meta_for_startup, pool_first_endpoint_is_local,
|
||||
resolve_startup_pool_defaults_with, resolve_store_init_stage_result, save_validated_pool_meta_for_startup,
|
||||
should_auto_start_rebalance_after_init, should_auto_start_rebalance_after_recovered_meta,
|
||||
should_resume_local_decommission, should_retry_local_decommission_resume, wait_for_local_decommission_resume_delay,
|
||||
pool_meta_has_active_decommission, resolve_startup_pool_defaults_with, resolve_store_init_stage_result,
|
||||
save_validated_pool_meta_for_startup, should_auto_start_rebalance_after_init, should_retry_local_decommission_resume,
|
||||
wait_for_local_decommission_resume_delay,
|
||||
};
|
||||
#[cfg(feature = "test-util")]
|
||||
use crate::{
|
||||
@@ -696,46 +677,33 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_resume_local_decommission_respects_local_flag() {
|
||||
fn test_pool_first_endpoint_is_local_respects_local_flag() {
|
||||
let mut local_endpoint = Endpoint::try_from("http://127.0.0.1:9000/data").expect("endpoint should parse");
|
||||
local_endpoint.is_local = true;
|
||||
let endpoints = EndpointServerPools::from(vec![PoolEndpoints {
|
||||
let pool = PoolEndpoints {
|
||||
legacy: false,
|
||||
set_count: 1,
|
||||
drives_per_set: 1,
|
||||
endpoints: Endpoints::from(vec![local_endpoint]),
|
||||
cmd_line: "pool-0".to_string(),
|
||||
platform: String::new(),
|
||||
}]);
|
||||
};
|
||||
|
||||
assert!(should_resume_local_decommission(&endpoints, 0).expect("local endpoint should resume"));
|
||||
assert!(pool_first_endpoint_is_local(&pool));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_resume_local_decommission_rejects_unresolvable_pool() {
|
||||
let endpoints = EndpointServerPools::default();
|
||||
let err = should_resume_local_decommission(&endpoints, 0).expect_err("missing pool should error");
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"Io error: store init failed to resolve decommission resume pool index 0 from current endpoints"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_resume_local_decommission_rejects_missing_endpoint() {
|
||||
let endpoints = EndpointServerPools::from(vec![PoolEndpoints {
|
||||
fn test_pool_first_endpoint_is_local_rejects_missing_endpoint() {
|
||||
let pool = PoolEndpoints {
|
||||
legacy: false,
|
||||
set_count: 1,
|
||||
drives_per_set: 1,
|
||||
endpoints: Endpoints::from(Vec::<Endpoint>::new()),
|
||||
cmd_line: "pool-0".to_string(),
|
||||
platform: String::new(),
|
||||
}]);
|
||||
let err = should_resume_local_decommission(&endpoints, 0).expect_err("missing endpoint should error");
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"Io error: store init failed to resolve decommission resume pool index 0: no endpoints available"
|
||||
);
|
||||
};
|
||||
|
||||
assert!(!pool_first_endpoint_is_local(&pool));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -782,7 +750,10 @@ mod tests {
|
||||
}));
|
||||
let rebalance_meta = Some(RebalanceMeta::default());
|
||||
|
||||
assert!(!should_auto_start_rebalance_after_recovered_meta(&pool_meta, rebalance_meta.is_some()));
|
||||
assert!(!should_auto_start_rebalance_after_init(
|
||||
pool_meta_has_active_decommission(&pool_meta),
|
||||
rebalance_meta.is_some()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -790,7 +761,10 @@ mod tests {
|
||||
let pool_meta = init_test_pool_meta(None);
|
||||
let rebalance_meta = Some(RebalanceMeta::default());
|
||||
|
||||
assert!(should_auto_start_rebalance_after_recovered_meta(&pool_meta, rebalance_meta.is_some()));
|
||||
assert!(should_auto_start_rebalance_after_init(
|
||||
pool_meta_has_active_decommission(&pool_meta),
|
||||
rebalance_meta.is_some()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -301,7 +301,6 @@ const LIST_CURSOR_SOURCE_INDEX_KEY_ONLY: &str = "index_key_only";
|
||||
const LIST_CURSOR_SOURCE_INDEX_VERIFIED_PAGE: &str = "index_verified_page";
|
||||
const LIST_CURSOR_SOURCE_INDEX_METADATA_FAST: &str = "index_metadata_fast";
|
||||
const LIST_CURSOR_GENERATION_LIVE: &str = "live";
|
||||
const ENV_API_LIST_QUORUM: &str = "RUSTFS_API_LIST_QUORUM";
|
||||
const DEFAULT_API_LIST_QUORUM: &str = "strict";
|
||||
const ENV_API_LIST_OBJECTS_QUORUM: &str = "RUSTFS_LIST_OBJECTS_QUORUM";
|
||||
const DEFAULT_API_LIST_OBJECTS_QUORUM: &str = "optimal";
|
||||
@@ -406,7 +405,6 @@ enum ListIndexFallbackReason {
|
||||
Unhealthy,
|
||||
Lagging,
|
||||
Degraded,
|
||||
Corrupt,
|
||||
MissingGeneration,
|
||||
GenerationMismatch,
|
||||
UnsupportedRequest,
|
||||
@@ -421,7 +419,6 @@ impl ListIndexFallbackReason {
|
||||
Self::Unhealthy => "unhealthy",
|
||||
Self::Lagging => "lagging",
|
||||
Self::Degraded => "degraded",
|
||||
Self::Corrupt => "corrupt",
|
||||
Self::MissingGeneration => "missing_generation",
|
||||
Self::GenerationMismatch => "generation_mismatch",
|
||||
Self::UnsupportedRequest => "unsupported_request",
|
||||
@@ -492,13 +489,6 @@ impl ListObjectsIndexProviderState {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_kind(kind: ListObjectsIndexProviderKind) -> Self {
|
||||
match kind {
|
||||
ListObjectsIndexProviderKind::WalkerKeyOnly => Self::walker_key_only(),
|
||||
ListObjectsIndexProviderKind::PersistentKeyOnly => Self::persistent_key_only(None, None),
|
||||
}
|
||||
}
|
||||
|
||||
fn health_snapshot(&self) -> ListIndexHealthSnapshot {
|
||||
self.lifecycle.health_snapshot()
|
||||
}
|
||||
@@ -667,6 +657,9 @@ async fn observe_list_objects_mutations_with_store(store: Option<&ECStore>, buck
|
||||
Some(next)
|
||||
}
|
||||
|
||||
// Test-only observer over the live mutation snapshot; production reads go
|
||||
// through `current_list_objects_mutation_snapshot` directly.
|
||||
#[cfg(test)]
|
||||
async fn current_list_objects_mutation_sequence(bucket: &str) -> u64 {
|
||||
current_list_objects_mutation_snapshot(None, bucket).await.high_water_mark
|
||||
}
|
||||
@@ -1539,17 +1532,6 @@ fn generated_persistent_key_only_generation(configured: Option<&str>) -> String
|
||||
format!("{LIST_OBJECTS_INDEX_PROVIDER_PERSISTENT_KEY_ONLY_DEFAULT_GENERATION}-{millis}")
|
||||
}
|
||||
|
||||
async fn write_persistent_key_only_index(
|
||||
store: Option<&ECStore>,
|
||||
path: &Path,
|
||||
bucket: &str,
|
||||
generation: &str,
|
||||
checkpoint_high_water_mark: u64,
|
||||
keys: &[String],
|
||||
) -> Result<PersistentKeyOnlyIndex> {
|
||||
write_persistent_key_only_index_with_metadata(store, path, bucket, generation, checkpoint_high_water_mark, keys, &[]).await
|
||||
}
|
||||
|
||||
async fn write_persistent_key_only_index_with_metadata(
|
||||
store: Option<&ECStore>,
|
||||
path: &Path,
|
||||
@@ -1656,38 +1638,8 @@ enum ListIndexSourceDecision {
|
||||
FallbackToWalker(ListIndexFallbackReason),
|
||||
}
|
||||
|
||||
trait ListMetadataIndexGeneration {
|
||||
fn generation_id(&self) -> &str;
|
||||
}
|
||||
|
||||
trait ListMetadataIndexHealth {
|
||||
fn fallback_reason(&self) -> Option<ListIndexFallbackReason>;
|
||||
|
||||
fn is_healthy(&self) -> bool {
|
||||
self.fallback_reason().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
trait ListMetadataIndexPage {
|
||||
fn source_mode(&self) -> ListSourceMode;
|
||||
fn generation(&self) -> &dyn ListMetadataIndexGeneration;
|
||||
fn keys(&self) -> &[String];
|
||||
|
||||
fn metadata_authority(&self) -> ListMetadataAuthority {
|
||||
self.source_mode().metadata_authority()
|
||||
}
|
||||
}
|
||||
|
||||
trait ListMetadataIndexPageIterator {
|
||||
type Page: ListMetadataIndexPage;
|
||||
|
||||
fn next_page(&mut self) -> Option<Self::Page>;
|
||||
}
|
||||
|
||||
trait ListMetadataIndexKeyLookup {
|
||||
type Page: ListMetadataIndexPage;
|
||||
|
||||
fn lookup_page(&self, opts: &ListPathOptions) -> ListIndexSourceDecision;
|
||||
}
|
||||
|
||||
fn select_list_index_provider_source_mode(
|
||||
@@ -1748,7 +1700,6 @@ enum ListIndexLifecycleState {
|
||||
Healthy,
|
||||
Lagging,
|
||||
Degraded,
|
||||
Corrupt,
|
||||
}
|
||||
|
||||
impl ListIndexLifecycleState {
|
||||
@@ -1759,7 +1710,6 @@ impl ListIndexLifecycleState {
|
||||
Self::Healthy => None,
|
||||
Self::Lagging => Some(ListIndexFallbackReason::Lagging),
|
||||
Self::Degraded => Some(ListIndexFallbackReason::Degraded),
|
||||
Self::Corrupt => Some(ListIndexFallbackReason::Corrupt),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1826,16 +1776,6 @@ impl ListIndexLifecycle {
|
||||
true
|
||||
}
|
||||
|
||||
fn recover_after_restart(mut self) -> Self {
|
||||
self.staging_generation = None;
|
||||
self.state = if self.active_generation.is_some() {
|
||||
ListIndexLifecycleState::Healthy
|
||||
} else {
|
||||
ListIndexLifecycleState::Disabled
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
fn observe_mutation_high_water_mark(&mut self, mutation_high_water_mark: u64) {
|
||||
self.mutation_high_water_mark = self.mutation_high_water_mark.max(mutation_high_water_mark);
|
||||
if matches!(self.state, ListIndexLifecycleState::Healthy | ListIndexLifecycleState::Lagging)
|
||||
@@ -1849,10 +1789,6 @@ impl ListIndexLifecycle {
|
||||
self.state = ListIndexLifecycleState::Degraded;
|
||||
}
|
||||
|
||||
fn mark_corrupt(&mut self) {
|
||||
self.state = ListIndexLifecycleState::Corrupt;
|
||||
}
|
||||
|
||||
fn mutation_lag(&self) -> u64 {
|
||||
self.mutation_high_water_mark.saturating_sub(self.checkpoint_high_water_mark)
|
||||
}
|
||||
@@ -2003,11 +1939,6 @@ fn normalize_list_quorum(value: &str) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn list_quorum_from_env() -> String {
|
||||
let value = rustfs_utils::get_env_str(ENV_API_LIST_QUORUM, DEFAULT_API_LIST_QUORUM);
|
||||
normalize_list_quorum(&value).to_owned()
|
||||
}
|
||||
|
||||
fn list_objects_quorum_from_env() -> String {
|
||||
let value = rustfs_utils::get_env_str(ENV_API_LIST_OBJECTS_QUORUM, DEFAULT_API_LIST_OBJECTS_QUORUM);
|
||||
normalize_list_quorum(&value).to_owned()
|
||||
@@ -2086,38 +2017,10 @@ fn list_objects_index_provider_state_from_env() -> Option<ListObjectsIndexProvid
|
||||
})
|
||||
}
|
||||
|
||||
fn list_objects_key_only_provider_health(provider: Option<ListObjectsIndexProviderKind>) -> ListIndexHealthSnapshot {
|
||||
provider
|
||||
.map(ListObjectsIndexProviderState::from_kind)
|
||||
.map(|state| state.health_snapshot())
|
||||
.unwrap_or_else(|| ListIndexLifecycle::disabled(0).health_snapshot())
|
||||
}
|
||||
|
||||
fn record_list_objects_index_fallback(mode: ListSourceMode, reason: ListIndexFallbackReason) {
|
||||
rustfs_io_metrics::record_list_objects_index_fallback(mode.cursor_value(), reason.metric_label());
|
||||
}
|
||||
|
||||
fn record_list_objects_index_opt_in_fallback(opts: &ListPathOptions, mode: ListSourceMode) {
|
||||
if !rustfs_io_metrics::get_stage_metrics_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let health = list_objects_key_only_provider_health(None);
|
||||
let reason = match select_list_index_provider_source_mode(opts, mode, &health) {
|
||||
ListIndexSourceDecision::FallbackToWalker(reason) => reason,
|
||||
ListIndexSourceDecision::UseIndex(_) => ListIndexFallbackReason::UnsupportedRequest,
|
||||
};
|
||||
|
||||
record_list_objects_index_fallback(mode, reason);
|
||||
debug!(
|
||||
bucket = %opts.bucket,
|
||||
prefix = %opts.prefix,
|
||||
source = mode.cursor_value(),
|
||||
reason = reason.metric_label(),
|
||||
"list_objects opt-in index path fell back to walker"
|
||||
);
|
||||
}
|
||||
|
||||
fn append_list_cache_id_to_marker(marker: String, cache_id: Option<&str>) -> String {
|
||||
let Some(id) = cache_id else {
|
||||
return marker;
|
||||
@@ -2284,6 +2187,8 @@ struct VerifiedIndexCandidateResult {
|
||||
stats: VerifiedIndexCandidateStats,
|
||||
}
|
||||
|
||||
// Test-only convenience over the live `_with_optional_stats` implementation.
|
||||
#[cfg(test)]
|
||||
async fn list_objects_from_verified_index_candidates<F, Fut>(
|
||||
prefix: &str,
|
||||
marker: Option<&str>,
|
||||
@@ -2309,6 +2214,8 @@ where
|
||||
.info)
|
||||
}
|
||||
|
||||
// Test-only convenience over the live `_with_optional_stats` implementation.
|
||||
#[cfg(test)]
|
||||
async fn list_objects_from_verified_index_candidates_with_stats<F, Fut>(
|
||||
prefix: &str,
|
||||
marker: Option<&str>,
|
||||
@@ -6753,11 +6660,10 @@ mod test {
|
||||
ENV_API_LIST_OBJECTS_METADATA_FAST_STALENESS_MS, ENV_API_LIST_OBJECTS_NAMESPACE_JOURNAL_CHAOS_BUCKET,
|
||||
ENV_API_LIST_OBJECTS_NAMESPACE_JOURNAL_CHAOS_ENABLED, ENV_API_LIST_OBJECTS_NAMESPACE_JOURNAL_CHAOS_SEQUENCE,
|
||||
ENV_API_LIST_OBJECTS_NAMESPACE_JOURNAL_CHAOS_STATUS, ENV_API_LIST_OBJECTS_NAMESPACE_JOURNAL_PATH,
|
||||
ENV_API_LIST_OBJECTS_QUORUM, ENV_API_LIST_QUORUM, FallbackListingEntries, FallbackListingEntry, GatherResultsState,
|
||||
ENV_API_LIST_OBJECTS_QUORUM, FallbackListingEntries, FallbackListingEntry, GatherResultsState,
|
||||
LIST_CURSOR_GENERATION_LIVE, LIST_OBJECTS_INDEX_PROVIDER_PERSISTENT_KEY_ONLY,
|
||||
LIST_OBJECTS_INDEX_PROVIDER_WALKER_KEY_ONLY, ListIndexFallbackReason, ListIndexLifecycle, ListIndexLifecycleState,
|
||||
ListIndexSourceDecision, ListMetadataAuthority, ListMetadataIndexGeneration, ListMetadataIndexHealth,
|
||||
ListMetadataIndexKeyLookup, ListMetadataIndexPage, ListMetadataIndexPageIterator, ListObjectsIndexProviderKind,
|
||||
ListIndexSourceDecision, ListMetadataAuthority, ListMetadataIndexHealth, ListObjectsIndexProviderKind,
|
||||
ListObjectsIndexProviderState, ListPathOptions, ListPathRawOptions, ListSourceMode, ListingEntryResolution,
|
||||
ListingSupplement, ListingSupplementOptions, MAX_OBJECT_LIST, NamespaceMutationJournalBackend,
|
||||
NamespaceMutationJournalSnapshot, NamespaceMutationJournalStatus, PERSISTENT_KEY_ONLY_INDEX_BUCKET_HEADER,
|
||||
@@ -6770,19 +6676,19 @@ mod test {
|
||||
list_merged_entry_channel, list_metadata_resolution_params, list_objects_from_metadata_snapshot_candidates,
|
||||
list_objects_from_verified_index_candidates, list_objects_from_verified_index_candidates_with_optional_stats,
|
||||
list_objects_from_verified_index_candidates_with_stats, list_objects_index_mode_from_env,
|
||||
list_objects_index_provider_from_env, list_objects_index_provider_state_from_env, list_objects_key_only_provider_health,
|
||||
list_objects_index_provider_from_env, list_objects_index_provider_state_from_env,
|
||||
list_objects_metadata_fast_guardrails_from_env, list_objects_paginate, list_objects_quorum_from_env,
|
||||
list_quorum_from_env, load_namespace_mutation_journal_state, load_persistent_key_only_index, max_keys_plus_one,
|
||||
merge_entry_channels, namespace_mutation_journal_chaos_bucket_from_env, namespace_mutation_journal_chaos_config_from_env,
|
||||
load_namespace_mutation_journal_state, load_persistent_key_only_index, max_keys_plus_one, merge_entry_channels,
|
||||
namespace_mutation_journal_chaos_bucket_from_env, namespace_mutation_journal_chaos_config_from_env,
|
||||
namespace_mutation_journal_chaos_enabled_from_env, namespace_mutation_journal_chaos_sequence_from_env,
|
||||
namespace_mutation_journal_chaos_status_from_env, normalize_list_quorum, observe_list_objects_mutations_with_store,
|
||||
parse_namespace_mutation_journal_state, parse_persistent_key_only_index, parse_persistent_list_metadata_object,
|
||||
parse_version_marker, persist_observed_list_objects_mutation, persistent_key_only_index_has_complete_metadata_snapshot,
|
||||
persistent_key_only_index_health, persistent_key_only_index_matches_provider, record_list_objects_index_opt_in_fallback,
|
||||
persistent_key_only_index_health, persistent_key_only_index_matches_provider,
|
||||
reset_list_objects_mutation_sequences_for_test, resolve_agreed_listing_entry, resolve_listing_entries,
|
||||
scanner_namespace_mutation_generation, select_list_index_provider_source_mode, select_list_index_source_mode,
|
||||
send_or_cancel, version_marker_for_entries, walk_result_from_set_errors, write_namespace_mutation_journal_state,
|
||||
write_persistent_key_only_index,
|
||||
write_persistent_key_only_index_with_metadata,
|
||||
};
|
||||
use crate::cache_value::metacache_set::{FallbackClaimTracker, TestReaderBehavior, list_path_raw};
|
||||
use crate::disk::{DiskAPI, DiskOption, endpoint::Endpoint, error::DiskError, new_disk};
|
||||
@@ -6846,16 +6752,6 @@ mod test {
|
||||
}
|
||||
use uuid::Uuid;
|
||||
|
||||
struct TestIndexGeneration {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl ListMetadataIndexGeneration for TestIndexGeneration {
|
||||
fn generation_id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
struct TestIndexHealth {
|
||||
reason: Option<ListIndexFallbackReason>,
|
||||
}
|
||||
@@ -6866,50 +6762,6 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
struct TestIndexPage {
|
||||
mode: ListSourceMode,
|
||||
generation: TestIndexGeneration,
|
||||
keys: Vec<String>,
|
||||
}
|
||||
|
||||
impl ListMetadataIndexPage for TestIndexPage {
|
||||
fn source_mode(&self) -> ListSourceMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
fn generation(&self) -> &dyn ListMetadataIndexGeneration {
|
||||
&self.generation
|
||||
}
|
||||
|
||||
fn keys(&self) -> &[String] {
|
||||
&self.keys
|
||||
}
|
||||
}
|
||||
|
||||
struct TestIndexIterator {
|
||||
pages: std::vec::IntoIter<TestIndexPage>,
|
||||
}
|
||||
|
||||
impl ListMetadataIndexPageIterator for TestIndexIterator {
|
||||
type Page = TestIndexPage;
|
||||
|
||||
fn next_page(&mut self) -> Option<Self::Page> {
|
||||
self.pages.next()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestIndexLookup {
|
||||
decision: ListIndexSourceDecision,
|
||||
}
|
||||
|
||||
impl ListMetadataIndexKeyLookup for TestIndexLookup {
|
||||
type Page = TestIndexPage;
|
||||
|
||||
fn lookup_page(&self, _opts: &ListPathOptions) -> ListIndexSourceDecision {
|
||||
self.decision
|
||||
}
|
||||
}
|
||||
|
||||
fn test_meta_entry(name: &str) -> MetaCacheEntry {
|
||||
MetaCacheEntry {
|
||||
name: name.to_owned(),
|
||||
@@ -7894,30 +7746,6 @@ mod test {
|
||||
assert_eq!(normalize_list_quorum("unknown"), "strict");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn list_quorum_from_env_defaults_to_strict() {
|
||||
temp_env::with_var_unset(ENV_API_LIST_QUORUM, || {
|
||||
assert_eq!(list_quorum_from_env(), "strict");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn list_quorum_from_env_honors_supported_value() {
|
||||
temp_env::with_var(ENV_API_LIST_QUORUM, Some("auto"), || {
|
||||
assert_eq!(list_quorum_from_env(), "auto");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn list_quorum_from_env_rejects_unknown_value() {
|
||||
temp_env::with_var(ENV_API_LIST_QUORUM, Some("unsafe"), || {
|
||||
assert_eq!(list_quorum_from_env(), "strict");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn list_objects_quorum_from_env_defaults_to_optimal() {
|
||||
@@ -8154,9 +7982,7 @@ mod test {
|
||||
fn list_objects_index_provider_from_env_is_default_off() {
|
||||
temp_env::with_var_unset(ENV_API_LIST_OBJECTS_INDEX_PROVIDER, || {
|
||||
assert_eq!(list_objects_index_provider_from_env(), None);
|
||||
let health = list_objects_key_only_provider_health(list_objects_index_provider_from_env());
|
||||
assert_eq!(health.state, ListIndexLifecycleState::Disabled);
|
||||
assert_eq!(health.fallback_reason, Some(ListIndexFallbackReason::Disabled));
|
||||
assert_eq!(list_objects_index_provider_state_from_env(), None);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8168,7 +7994,9 @@ mod test {
|
||||
Some(LIST_OBJECTS_INDEX_PROVIDER_WALKER_KEY_ONLY),
|
||||
|| {
|
||||
assert_eq!(list_objects_index_provider_from_env(), Some(ListObjectsIndexProviderKind::WalkerKeyOnly));
|
||||
let health = list_objects_key_only_provider_health(list_objects_index_provider_from_env());
|
||||
let health = list_objects_index_provider_state_from_env()
|
||||
.expect("walker provider should be configured")
|
||||
.health_snapshot();
|
||||
assert_eq!(health.state, ListIndexLifecycleState::Healthy);
|
||||
assert_eq!(health.active_generation.as_deref(), Some(LIST_CURSOR_GENERATION_LIVE));
|
||||
assert_eq!(health.fallback_reason, None);
|
||||
@@ -8459,7 +8287,7 @@ mod test {
|
||||
let journal_root = tempdir.path().join("namespace-mutation-journal");
|
||||
let keys = vec!["object-a".to_string(), "object-b".to_string()];
|
||||
|
||||
write_persistent_key_only_index(None, &index_path, "bucket", "generation-42", 5, &keys)
|
||||
write_persistent_key_only_index_with_metadata(None, &index_path, "bucket", "generation-42", 5, &keys, &[])
|
||||
.await
|
||||
.expect("index should be written");
|
||||
write_namespace_mutation_journal_state(NamespaceMutationJournalBackend::LocalPath(journal_root), "bucket", 9, false)
|
||||
@@ -8644,7 +8472,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn list_objects_index_provider_state_uses_lifecycle_active_generation() {
|
||||
let provider = ListObjectsIndexProviderState::from_kind(ListObjectsIndexProviderKind::WalkerKeyOnly);
|
||||
let provider = ListObjectsIndexProviderState::walker_key_only();
|
||||
let health = provider.health_snapshot();
|
||||
|
||||
assert_eq!(provider.kind, ListObjectsIndexProviderKind::WalkerKeyOnly);
|
||||
@@ -8657,21 +8485,6 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_objects_index_opt_in_fallback_accepts_incompatible_cursor_without_panicking() {
|
||||
record_list_objects_index_opt_in_fallback(
|
||||
&ListPathOptions {
|
||||
bucket: "bucket".to_string(),
|
||||
prefix: "photos/".to_string(),
|
||||
marker: Some(
|
||||
"photos/2026/[rustfs_cache:v2,id:list-cache-id,src:index_verified_page,gen:generation-1]".to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
ListSourceMode::IndexKeyOnly,
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_objects_verified_index_candidates_use_live_metadata_only() {
|
||||
let candidates = vec![
|
||||
@@ -9392,38 +9205,6 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_traits_keep_page_iteration_and_metadata_authority_explicit() {
|
||||
let page = TestIndexPage {
|
||||
mode: ListSourceMode::IndexMetadataFast,
|
||||
generation: TestIndexGeneration {
|
||||
id: "snapshot-7".to_string(),
|
||||
},
|
||||
keys: vec!["photos/2026/image.jpg".to_string()],
|
||||
};
|
||||
let mut iterator = TestIndexIterator {
|
||||
pages: vec![page].into_iter(),
|
||||
};
|
||||
let page = iterator.next_page().expect("test iterator should produce one page");
|
||||
|
||||
assert_eq!(page.keys(), &["photos/2026/image.jpg".to_string()]);
|
||||
assert_eq!(page.generation().generation_id(), "snapshot-7");
|
||||
assert_eq!(page.metadata_authority(), ListMetadataAuthority::IndexSnapshotEventuallyConsistent);
|
||||
assert!(!page.source_mode().can_satisfy_strong_listing());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_key_lookup_trait_returns_explicit_fallback_decision() {
|
||||
let lookup = TestIndexLookup {
|
||||
decision: ListIndexSourceDecision::FallbackToWalker(ListIndexFallbackReason::UnsupportedRequest),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
lookup.lookup_page(&ListPathOptions::default()),
|
||||
ListIndexSourceDecision::FallbackToWalker(ListIndexFallbackReason::UnsupportedRequest)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_lifecycle_rebuild_requires_publish_before_health() {
|
||||
let mut lifecycle = ListIndexLifecycle::disabled(10);
|
||||
@@ -9446,40 +9227,6 @@ mod test {
|
||||
assert_eq!(snapshot.fallback_reason, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_lifecycle_restart_never_trusts_unpublished_staging_generation() {
|
||||
let mut lifecycle = ListIndexLifecycle::disabled(10);
|
||||
lifecycle.begin_rebuild("generation-1", 100);
|
||||
lifecycle.checkpoint_rebuild(90);
|
||||
|
||||
let recovered = lifecycle.recover_after_restart();
|
||||
let snapshot = recovered.health_snapshot();
|
||||
|
||||
assert_eq!(snapshot.state, ListIndexLifecycleState::Disabled);
|
||||
assert_eq!(snapshot.active_generation, None);
|
||||
assert_eq!(snapshot.staging_generation, None);
|
||||
assert_eq!(snapshot.fallback_reason, Some(ListIndexFallbackReason::Disabled));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_lifecycle_restart_keeps_only_last_published_generation() {
|
||||
let mut lifecycle = ListIndexLifecycle::disabled(10);
|
||||
lifecycle.begin_rebuild("generation-1", 100);
|
||||
lifecycle.checkpoint_rebuild(100);
|
||||
assert!(lifecycle.publish_rebuild());
|
||||
|
||||
lifecycle.begin_rebuild("generation-2", 125);
|
||||
lifecycle.checkpoint_rebuild(120);
|
||||
|
||||
let recovered = lifecycle.recover_after_restart();
|
||||
let snapshot = recovered.health_snapshot();
|
||||
|
||||
assert_eq!(snapshot.state, ListIndexLifecycleState::Healthy);
|
||||
assert_eq!(snapshot.active_generation.as_deref(), Some("generation-1"));
|
||||
assert_eq!(snapshot.staging_generation, None);
|
||||
assert_eq!(snapshot.fallback_reason, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_lifecycle_reports_lagging_and_fallback_reason() {
|
||||
let mut lifecycle = ListIndexLifecycle::disabled(10);
|
||||
@@ -9493,11 +9240,10 @@ mod test {
|
||||
assert_eq!(snapshot.state, ListIndexLifecycleState::Lagging);
|
||||
assert_eq!(snapshot.mutation_lag, 15);
|
||||
assert_eq!(snapshot.fallback_reason, Some(ListIndexFallbackReason::Lagging));
|
||||
assert!(!snapshot.is_healthy());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_index_lifecycle_reports_degraded_and_corrupt_as_unhealthy() {
|
||||
fn list_index_lifecycle_reports_degraded_as_fallback() {
|
||||
let mut lifecycle = ListIndexLifecycle::disabled(10);
|
||||
lifecycle.begin_rebuild("generation-1", 100);
|
||||
lifecycle.checkpoint_rebuild(100);
|
||||
@@ -9507,13 +9253,6 @@ mod test {
|
||||
let degraded = lifecycle.health_snapshot();
|
||||
assert_eq!(degraded.state, ListIndexLifecycleState::Degraded);
|
||||
assert_eq!(degraded.fallback_reason, Some(ListIndexFallbackReason::Degraded));
|
||||
assert!(!degraded.is_healthy());
|
||||
|
||||
lifecycle.mark_corrupt();
|
||||
let corrupt = lifecycle.health_snapshot();
|
||||
assert_eq!(corrupt.state, ListIndexLifecycleState::Corrupt);
|
||||
assert_eq!(corrupt.fallback_reason, Some(ListIndexFallbackReason::Corrupt));
|
||||
assert!(!corrupt.is_healthy());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// #730: store startup/listing helpers are staged while global state is moved to owners.
|
||||
#![allow(dead_code)]
|
||||
#![allow(clippy::map_entry)]
|
||||
|
||||
use crate::bucket::lifecycle::bucket_lifecycle_audit::LcEventSrc;
|
||||
|
||||
@@ -61,10 +61,6 @@ pub async fn find_local_disk_by_ref(disk_ref: &str) -> Option<DiskStore> {
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn get_disk_via_endpoint(endpoint: &Endpoint) -> Option<DiskStore> {
|
||||
runtime_sources::local_disk_for_endpoint(endpoint).await
|
||||
}
|
||||
|
||||
pub async fn all_local_disk_path() -> Vec<String> {
|
||||
runtime_sources::local_disk_paths().await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user