diff --git a/crates/ecstore/src/core/pools.rs b/crates/ecstore/src/core/pools.rs index bf6ab9dd2..c6bc5ab8d 100644 --- a/crates/ecstore/src/core/pools.rs +++ b/crates/ecstore/src/core/pools.rs @@ -5228,7 +5228,7 @@ where R: Into, { let replicas = replicas.into_iter().map(Into::into).collect::>(); - if let Some(err) = pool_meta_replica_read_failure(&replicas) { + if let Some(err) = pool_meta_replica_read_failure(&replicas, "pool_read") { return Err(err); } match select_pool_meta_replica_reads(replicas) { @@ -5248,13 +5248,14 @@ fn block_pool_meta_validation(write_state: &mut PoolMetaWriteState, err: Error, Error::other(context) } -fn pool_meta_replica_read_failure(replicas: &[PoolMetaReplicaRead]) -> Option { +fn pool_meta_replica_read_failure(replicas: &[PoolMetaReplicaRead], operation: &str) -> Option { replicas.iter().find_map(|read| match &read.replica { - PoolMetaReplica::Unreadable(source) => Some(Error::other(pool_metadata_error( - crate::error::PoolMetadataFailure::ReadUnavailable, - "pool_read", - Some(Arc::clone(source)), - ))), + PoolMetaReplica::Unreadable(source) => { + let mut context = + pool_metadata_error(crate::error::PoolMetadataFailure::ReadUnavailable, "pool_read", Some(Arc::clone(source))); + context.operation = operation.to_owned(); + Some(Error::other(context)) + } _ => None, }) } @@ -5268,7 +5269,7 @@ where R: Into, { let replicas = replicas.into_iter().map(Into::into).collect::>(); - if let Some(err) = pool_meta_replica_read_failure(&replicas) { + if let Some(err) = pool_meta_replica_read_failure(&replicas, operation) { return Err(err); } let selection = select_pool_meta_replica_reads(replicas)?; @@ -6349,7 +6350,7 @@ where write_state.observe_identity(&identity)?; } let reads = read_pool_meta_replicas(pools, true).await; - if let Some(err) = pool_meta_replica_read_failure(&reads) { + if let Some(err) = pool_meta_replica_read_failure(&reads, "pool metadata transaction recovery") { return Err(err); } if reads @@ -6771,7 +6772,7 @@ impl PoolMeta { write_state.observe_identity(&identity)?; } let reads = read_pool_meta_replicas(pools, true).await; - if let Some(err) = pool_meta_replica_read_failure(&reads) { + if let Some(err) = pool_meta_replica_read_failure(&reads, "pool metadata load") { write_state.block_with_context(pool_metadata_error( crate::error::PoolMetadataFailure::RecoveryRequired, "startup_read", diff --git a/crates/ecstore/src/store/heal.rs b/crates/ecstore/src/store/heal.rs index c5ea6d52f..8144e7eea 100644 --- a/crates/ecstore/src/store/heal.rs +++ b/crates/ecstore/src/store/heal.rs @@ -2353,14 +2353,19 @@ mod tests { .await .expect("quorum boundary heal should return a mapped result"); *store.pools[0].disk_set[0].disks.write().await = original_quorum_disks; - let quorum_err_text = quorum_err.as_ref().map(ToString::to_string); - assert!( - quorum_err_text.as_deref().is_some_and(|err| { - err.contains("target capacity admission failed") - && err.contains("pool metadata update cannot overwrite an unreadable replica") - }), - "heal must fail closed when capacity admission cannot verify pool metadata, got {quorum_err:?}" + let quorum_err = quorum_err + .as_ref() + .expect("heal must fail closed when capacity admission cannot verify pool metadata"); + let quorum_failure = quorum_err + .pool_metadata_failure() + .expect("capacity admission failure should preserve typed pool metadata context"); + assert_eq!( + quorum_failure.kind, + crate::error::PoolMetadataFailure::ReadUnavailable, + "read-only capacity admission failure must remain retryable" ); + assert_eq!(quorum_failure.operation, "target capacity admission failed"); + assert_eq!(quorum_failure.phase, "pool_read"); assert!( store.pool_meta_writes_ready().await, "read-only capacity admission failure must not latch the pool metadata writer" diff --git a/scripts/error-other-format-baseline.txt b/scripts/error-other-format-baseline.txt index 613b4098d..ddd906666 100644 --- a/scripts/error-other-format-baseline.txt +++ b/scripts/error-other-format-baseline.txt @@ -25,7 +25,7 @@ 10|crates/ecstore/src/cluster/rpc/remote_disk.rs 6|crates/ecstore/src/config/com.rs 14|crates/ecstore/src/config/storageclass.rs -182|crates/ecstore/src/core/pools.rs +181|crates/ecstore/src/core/pools.rs 7|crates/ecstore/src/data_movement/mod.rs 2|crates/ecstore/src/data_usage/local_snapshot.rs 12|crates/ecstore/src/data_usage/mod.rs @@ -66,7 +66,7 @@ 3|crates/ecstore/src/set_disk/read.rs 5|crates/ecstore/src/store/bucket.rs 1|crates/ecstore/src/store/heal_walk.rs -12|crates/ecstore/src/store/init.rs +10|crates/ecstore/src/store/init.rs 3|crates/ecstore/src/store/multipart.rs 6|crates/ecstore/src/store/object.rs 5|crates/ecstore/src/store/rebalance/support.rs