mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-29 16:37:07 +00:00
fix(storage): stabilize nextest regressions (#6543)
This commit is contained in:
@@ -8112,6 +8112,20 @@ impl ECStore {
|
|||||||
Ok(decommission_durable_ilm_receipt_run_token(&pool.cmd_line, start_time))
|
Ok(decommission_durable_ilm_receipt_run_token(&pool.cmd_line, start_time))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn durable_ilm_receipt_run_token_for_generation(
|
||||||
|
&self,
|
||||||
|
source_pool_idx: usize,
|
||||||
|
generation: OffsetDateTime,
|
||||||
|
) -> Result<String> {
|
||||||
|
let pool_meta = self.pool_meta.read().await;
|
||||||
|
ensure_decommission_generation(&pool_meta, source_pool_idx, generation)?;
|
||||||
|
let pool = pool_meta
|
||||||
|
.pools
|
||||||
|
.get(source_pool_idx)
|
||||||
|
.ok_or_else(|| invalid_decommission_pool_index_error(pool_meta.pools.len(), source_pool_idx))?;
|
||||||
|
Ok(decommission_durable_ilm_receipt_run_token(&pool.cmd_line, generation))
|
||||||
|
}
|
||||||
|
|
||||||
async fn load_decommissioned_durable_ilm_target(
|
async fn load_decommissioned_durable_ilm_target(
|
||||||
&self,
|
&self,
|
||||||
source_pool_idx: usize,
|
source_pool_idx: usize,
|
||||||
@@ -8188,7 +8202,16 @@ impl ECStore {
|
|||||||
|
|
||||||
async fn list_decommission_durable_ilm_receipts(&self, source_pool_idx: usize) -> Result<Vec<(usize, String)>> {
|
async fn list_decommission_durable_ilm_receipts(&self, source_pool_idx: usize) -> Result<Vec<(usize, String)>> {
|
||||||
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
||||||
let prefix = decommission_durable_ilm_receipt_run_prefix(&run_token);
|
self.list_decommission_durable_ilm_receipts_for_run(source_pool_idx, &run_token)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_decommission_durable_ilm_receipts_for_run(
|
||||||
|
&self,
|
||||||
|
source_pool_idx: usize,
|
||||||
|
run_token: &str,
|
||||||
|
) -> Result<Vec<(usize, String)>> {
|
||||||
|
let prefix = decommission_durable_ilm_receipt_run_prefix(run_token);
|
||||||
let mut receipts = Vec::new();
|
let mut receipts = Vec::new();
|
||||||
for pool_idx in 0..self.pools.len() {
|
for pool_idx in 0..self.pools.len() {
|
||||||
if pool_idx == source_pool_idx {
|
if pool_idx == source_pool_idx {
|
||||||
@@ -8212,7 +8235,16 @@ impl ECStore {
|
|||||||
|
|
||||||
async fn list_decommission_durable_ilm_manifest_receipts(&self, source_pool_idx: usize) -> Result<Vec<String>> {
|
async fn list_decommission_durable_ilm_manifest_receipts(&self, source_pool_idx: usize) -> Result<Vec<String>> {
|
||||||
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
||||||
let prefix = decommission_durable_ilm_receipt_run_prefix(&run_token);
|
self.list_decommission_durable_ilm_manifest_receipts_for_run(source_pool_idx, &run_token)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_decommission_durable_ilm_manifest_receipts_for_run(
|
||||||
|
&self,
|
||||||
|
source_pool_idx: usize,
|
||||||
|
run_token: &str,
|
||||||
|
) -> Result<Vec<String>> {
|
||||||
|
let prefix = decommission_durable_ilm_receipt_run_prefix(run_token);
|
||||||
let receipt_paths = self
|
let receipt_paths = self
|
||||||
.list_decommission_durable_ilm_receipt_paths_in_pool(source_pool_idx, &prefix)
|
.list_decommission_durable_ilm_receipt_paths_in_pool(source_pool_idx, &prefix)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -8227,15 +8259,23 @@ impl ECStore {
|
|||||||
Ok(receipt_paths)
|
Ok(receipt_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
async fn persist_decommission_durable_ilm_manifest(&self, source_pool_idx: usize) -> Result<()> {
|
async fn persist_decommission_durable_ilm_manifest(&self, source_pool_idx: usize) -> Result<()> {
|
||||||
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
||||||
let receipt_paths = self.list_decommission_durable_ilm_manifest_receipts(source_pool_idx).await?;
|
self.persist_decommission_durable_ilm_manifest_for_run(source_pool_idx, &run_token)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn persist_decommission_durable_ilm_manifest_for_run(&self, source_pool_idx: usize, run_token: &str) -> Result<()> {
|
||||||
|
let receipt_paths = self
|
||||||
|
.list_decommission_durable_ilm_manifest_receipts_for_run(source_pool_idx, run_token)
|
||||||
|
.await?;
|
||||||
for receipt_path in &receipt_paths {
|
for receipt_path in &receipt_paths {
|
||||||
self.read_decommission_durable_ilm_receipt(source_pool_idx, receipt_path)
|
self.read_decommission_durable_ilm_receipt(source_pool_idx, receipt_path)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
let manifest = DecommissionDurableIlmManifest::new(&run_token, &receipt_paths)?;
|
let manifest = DecommissionDurableIlmManifest::new(run_token, &receipt_paths)?;
|
||||||
let manifest_path = decommission_durable_ilm_manifest_path(&run_token);
|
let manifest_path = decommission_durable_ilm_manifest_path(run_token);
|
||||||
let encoded = manifest.encode()?;
|
let encoded = manifest.encode()?;
|
||||||
let mut attempt = 1;
|
let mut attempt = 1;
|
||||||
loop {
|
loop {
|
||||||
@@ -8247,7 +8287,7 @@ impl ECStore {
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(existing) => {
|
Ok(existing) => {
|
||||||
DecommissionDurableIlmManifest::decode(&existing, &run_token, &receipt_paths).map_err(|err| {
|
DecommissionDurableIlmManifest::decode(&existing, run_token, &receipt_paths).map_err(|err| {
|
||||||
Error::other(format!(
|
Error::other(format!(
|
||||||
"durable ILM expected manifest `{manifest_path}` in source pool {source_pool_idx} is invalid: {err}"
|
"durable ILM expected manifest `{manifest_path}` in source pool {source_pool_idx} is invalid: {err}"
|
||||||
))
|
))
|
||||||
@@ -8297,13 +8337,15 @@ impl ECStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_decommission_durable_ilm_manifest(
|
async fn load_decommission_durable_ilm_manifest_for_run(
|
||||||
&self,
|
&self,
|
||||||
source_pool_idx: usize,
|
source_pool_idx: usize,
|
||||||
|
run_token: &str,
|
||||||
) -> Result<HashMap<String, DecommissionDurableIlmReceipt>> {
|
) -> Result<HashMap<String, DecommissionDurableIlmReceipt>> {
|
||||||
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
let receipt_paths = self
|
||||||
let receipt_paths = self.list_decommission_durable_ilm_manifest_receipts(source_pool_idx).await?;
|
.list_decommission_durable_ilm_manifest_receipts_for_run(source_pool_idx, run_token)
|
||||||
let manifest_path = decommission_durable_ilm_manifest_path(&run_token);
|
.await?;
|
||||||
|
let manifest_path = decommission_durable_ilm_manifest_path(run_token);
|
||||||
let data = read_config_limited_preserve_empty(
|
let data = read_config_limited_preserve_empty(
|
||||||
self.pools[source_pool_idx].clone(),
|
self.pools[source_pool_idx].clone(),
|
||||||
&manifest_path,
|
&manifest_path,
|
||||||
@@ -8315,7 +8357,7 @@ impl ECStore {
|
|||||||
"failed to read durable ILM expected manifest `{manifest_path}` from source pool {source_pool_idx}: {err}"
|
"failed to read durable ILM expected manifest `{manifest_path}` from source pool {source_pool_idx}: {err}"
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
DecommissionDurableIlmManifest::decode(&data, &run_token, &receipt_paths).map_err(|err| {
|
DecommissionDurableIlmManifest::decode(&data, run_token, &receipt_paths).map_err(|err| {
|
||||||
Error::other(format!(
|
Error::other(format!(
|
||||||
"durable ILM expected manifest `{manifest_path}` in source pool {source_pool_idx} is invalid: {err}"
|
"durable ILM expected manifest `{manifest_path}` in source pool {source_pool_idx} is invalid: {err}"
|
||||||
))
|
))
|
||||||
@@ -8335,6 +8377,7 @@ impl ECStore {
|
|||||||
Ok(receipts)
|
Ok(receipts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
async fn persist_decommission_durable_ilm_receipt(
|
async fn persist_decommission_durable_ilm_receipt(
|
||||||
&self,
|
&self,
|
||||||
source_pool_idx: usize,
|
source_pool_idx: usize,
|
||||||
@@ -8342,7 +8385,17 @@ impl ECStore {
|
|||||||
receipt: &DecommissionDurableIlmReceipt,
|
receipt: &DecommissionDurableIlmReceipt,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
||||||
let receipt_path = decommission_durable_ilm_receipt_path(&run_token, &receipt.source_path, &receipt.id_kind, &receipt.id);
|
self.persist_decommission_durable_ilm_receipt_for_run(target_pool_idx, receipt, &run_token)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn persist_decommission_durable_ilm_receipt_for_run(
|
||||||
|
&self,
|
||||||
|
target_pool_idx: usize,
|
||||||
|
receipt: &DecommissionDurableIlmReceipt,
|
||||||
|
run_token: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let receipt_path = decommission_durable_ilm_receipt_path(run_token, &receipt.source_path, &receipt.id_kind, &receipt.id);
|
||||||
let locator = parse_decommission_durable_ilm_receipt_path(&receipt_path)?;
|
let locator = parse_decommission_durable_ilm_receipt_path(&receipt_path)?;
|
||||||
let mut attempt = 1;
|
let mut attempt = 1;
|
||||||
loop {
|
loop {
|
||||||
@@ -8480,14 +8533,14 @@ impl ECStore {
|
|||||||
Ok(receipt)
|
Ok(receipt)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_decommission_durable_ilm_terminal_receipt(
|
async fn load_decommission_durable_ilm_terminal_receipt_for_run(
|
||||||
&self,
|
&self,
|
||||||
source_pool_idx: usize,
|
source_pool_idx: usize,
|
||||||
path: &str,
|
path: &str,
|
||||||
source_record: &ValidatedDurableIlmRecord,
|
source_record: &ValidatedDurableIlmRecord,
|
||||||
|
run_token: &str,
|
||||||
) -> Result<Option<DecommissionDurableIlmReceipt>> {
|
) -> Result<Option<DecommissionDurableIlmReceipt>> {
|
||||||
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
let receipt_path = decommission_durable_ilm_receipt_path(run_token, path, source_record.id_kind, &source_record.id);
|
||||||
let receipt_path = decommission_durable_ilm_receipt_path(&run_token, path, source_record.id_kind, &source_record.id);
|
|
||||||
let locator = parse_decommission_durable_ilm_receipt_path(&receipt_path)?;
|
let locator = parse_decommission_durable_ilm_receipt_path(&receipt_path)?;
|
||||||
let mut proof = None::<DecommissionDurableIlmReceipt>;
|
let mut proof = None::<DecommissionDurableIlmReceipt>;
|
||||||
for pool_idx in 0..self.pools.len() {
|
for pool_idx in 0..self.pools.len() {
|
||||||
@@ -8553,8 +8606,18 @@ impl ECStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn verify_decommission_durable_ilm_receipts(&self, source_pool_idx: usize) -> Result<()> {
|
async fn verify_decommission_durable_ilm_receipts(&self, source_pool_idx: usize) -> Result<()> {
|
||||||
let expected_receipts = self.load_decommission_durable_ilm_manifest(source_pool_idx).await?;
|
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
||||||
let receipt_paths = self.list_decommission_durable_ilm_receipts(source_pool_idx).await?;
|
self.verify_decommission_durable_ilm_receipts_for_run(source_pool_idx, &run_token)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn verify_decommission_durable_ilm_receipts_for_run(&self, source_pool_idx: usize, run_token: &str) -> Result<()> {
|
||||||
|
let expected_receipts = self
|
||||||
|
.load_decommission_durable_ilm_manifest_for_run(source_pool_idx, run_token)
|
||||||
|
.await?;
|
||||||
|
let receipt_paths = self
|
||||||
|
.list_decommission_durable_ilm_receipts_for_run(source_pool_idx, run_token)
|
||||||
|
.await?;
|
||||||
let present_receipt_paths = receipt_paths
|
let present_receipt_paths = receipt_paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, receipt_path)| receipt_path.as_str())
|
.map(|(_, receipt_path)| receipt_path.as_str())
|
||||||
@@ -8918,11 +8981,24 @@ impl ECStore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
async fn verify_and_cleanup_decommissioned_durable_ilm_record(
|
async fn verify_and_cleanup_decommissioned_durable_ilm_record(
|
||||||
&self,
|
&self,
|
||||||
source_pool_idx: usize,
|
source_pool_idx: usize,
|
||||||
source_set: Arc<SetDisks>,
|
source_set: Arc<SetDisks>,
|
||||||
path: &str,
|
path: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let run_token = self.durable_ilm_receipt_run_token(source_pool_idx).await?;
|
||||||
|
self.verify_and_cleanup_decommissioned_durable_ilm_record_for_run(source_pool_idx, source_set, path, &run_token)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn verify_and_cleanup_decommissioned_durable_ilm_record_for_run(
|
||||||
|
&self,
|
||||||
|
source_pool_idx: usize,
|
||||||
|
source_set: Arc<SetDisks>,
|
||||||
|
path: &str,
|
||||||
|
run_token: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let namespace = classify_durable_ilm_record(path)?
|
let namespace = classify_durable_ilm_record(path)?
|
||||||
.ok_or_else(|| Error::other(format!("path `{path}` is not a durable ILM record")))?;
|
.ok_or_else(|| Error::other(format!("path `{path}` is not a durable ILM record")))?;
|
||||||
@@ -8942,11 +9018,11 @@ impl ECStore {
|
|||||||
let manifest_receipt = if let Some((target_pool_idx, target)) = target {
|
let manifest_receipt = if let Some((target_pool_idx, target)) = target {
|
||||||
let target_record = validate_decommission_durable_ilm_copy(path, &source_record, &target)?;
|
let target_record = validate_decommission_durable_ilm_copy(path, &source_record, &target)?;
|
||||||
let receipt = DecommissionDurableIlmReceipt::new(path, &target_record);
|
let receipt = DecommissionDurableIlmReceipt::new(path, &target_record);
|
||||||
self.persist_decommission_durable_ilm_receipt(source_pool_idx, target_pool_idx, &receipt)
|
self.persist_decommission_durable_ilm_receipt_for_run(target_pool_idx, &receipt, run_token)
|
||||||
.await?;
|
.await?;
|
||||||
receipt
|
receipt
|
||||||
} else {
|
} else {
|
||||||
self.load_decommission_durable_ilm_terminal_receipt(source_pool_idx, path, &source_record)
|
self.load_decommission_durable_ilm_terminal_receipt_for_run(source_pool_idx, path, &source_record, run_token)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::other(format!(
|
Error::other(format!(
|
||||||
@@ -8955,7 +9031,7 @@ impl ECStore {
|
|||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
};
|
};
|
||||||
self.persist_decommission_durable_ilm_receipt(source_pool_idx, source_pool_idx, &manifest_receipt)
|
self.persist_decommission_durable_ilm_receipt_for_run(source_pool_idx, &manifest_receipt, run_token)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let cleanup_result = data_movement::cleanup_source_entry_if_unchanged(
|
let cleanup_result = data_movement::cleanup_source_entry_if_unchanged(
|
||||||
@@ -9036,16 +9112,19 @@ impl ECStore {
|
|||||||
rx: &CancellationToken,
|
rx: &CancellationToken,
|
||||||
generation: OffsetDateTime,
|
generation: OffsetDateTime,
|
||||||
) -> Result<Vec<DecommissionUnresolvedEntry>> {
|
) -> Result<Vec<DecommissionUnresolvedEntry>> {
|
||||||
self.ensure_decommission_generation_current(idx, generation).await?;
|
let run_token = self.durable_ilm_receipt_run_token_for_generation(idx, generation).await?;
|
||||||
let operation_gate = self.ctx.data_movement_operation_gate();
|
let operation_gate = self.ctx.data_movement_operation_gate();
|
||||||
self.run_guarded_decommission_side_effect(rx, &operation_gate, || self.check_after_decommission_unfenced(idx, generation))
|
self.run_guarded_decommission_side_effect(rx, &operation_gate, || {
|
||||||
.await
|
self.check_after_decommission_unfenced(idx, generation, run_token)
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_after_decommission_unfenced(
|
async fn check_after_decommission_unfenced(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
generation: OffsetDateTime,
|
generation: OffsetDateTime,
|
||||||
|
run_token: String,
|
||||||
) -> Result<Vec<DecommissionUnresolvedEntry>> {
|
) -> Result<Vec<DecommissionUnresolvedEntry>> {
|
||||||
let unresolved_entries = {
|
let unresolved_entries = {
|
||||||
let pool_meta = self.pool_meta.read().await;
|
let pool_meta = self.pool_meta.read().await;
|
||||||
@@ -9112,6 +9191,7 @@ impl ECStore {
|
|||||||
let callback_rx_cb = callback_rx.clone();
|
let callback_rx_cb = callback_rx.clone();
|
||||||
let unresolved_entries_by_identity_cb = unresolved_entries_by_identity.clone();
|
let unresolved_entries_by_identity_cb = unresolved_entries_by_identity.clone();
|
||||||
let resolved_unresolved_entries_cb = resolved_unresolved_entries.clone();
|
let resolved_unresolved_entries_cb = resolved_unresolved_entries.clone();
|
||||||
|
let run_token_cb = run_token.clone();
|
||||||
|
|
||||||
let callback: ListCallback = Arc::new(move |entry: MetaCacheEntry| {
|
let callback: ListCallback = Arc::new(move |entry: MetaCacheEntry| {
|
||||||
let versions_found = versions_found_cb.clone();
|
let versions_found = versions_found_cb.clone();
|
||||||
@@ -9126,6 +9206,7 @@ impl ECStore {
|
|||||||
let callback_rx = callback_rx_cb.clone();
|
let callback_rx = callback_rx_cb.clone();
|
||||||
let unresolved_entries_by_identity = unresolved_entries_by_identity_cb.clone();
|
let unresolved_entries_by_identity = unresolved_entries_by_identity_cb.clone();
|
||||||
let resolved_unresolved_entries = resolved_unresolved_entries_cb.clone();
|
let resolved_unresolved_entries = resolved_unresolved_entries_cb.clone();
|
||||||
|
let run_token = run_token_cb.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if callback_rx.is_cancelled() {
|
if callback_rx.is_cancelled() {
|
||||||
return;
|
return;
|
||||||
@@ -9162,7 +9243,12 @@ impl ECStore {
|
|||||||
|
|
||||||
if durable_ilm_record.is_some() {
|
if durable_ilm_record.is_some() {
|
||||||
if let Err(err) = store
|
if let Err(err) = store
|
||||||
.verify_and_cleanup_decommissioned_durable_ilm_record(idx, source_set, &entry.name)
|
.verify_and_cleanup_decommissioned_durable_ilm_record_for_run(
|
||||||
|
idx,
|
||||||
|
source_set,
|
||||||
|
&entry.name,
|
||||||
|
&run_token,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let mut first_err = entry_error.lock().await;
|
let mut first_err = entry_error.lock().await;
|
||||||
@@ -9319,8 +9405,9 @@ impl ECStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.persist_decommission_durable_ilm_manifest(idx).await?;
|
self.persist_decommission_durable_ilm_manifest_for_run(idx, &run_token)
|
||||||
self.verify_decommission_durable_ilm_receipts(idx).await?;
|
.await?;
|
||||||
|
self.verify_decommission_durable_ilm_receipts_for_run(idx, &run_token).await?;
|
||||||
|
|
||||||
Ok(verified_unresolved_entries)
|
Ok(verified_unresolved_entries)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11934,7 +11934,7 @@ mod transition_upload_integrity_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread", start_paused = true)]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn restore_finalize_rejects_acquired_lock_loss_after_commit() {
|
async fn restore_finalize_rejects_acquired_lock_loss_after_commit() {
|
||||||
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
||||||
@@ -11953,6 +11953,7 @@ mod transition_upload_integrity_tests {
|
|||||||
async { write_committed_restore(&set_disks, &disk_stores, bucket, object, operation_id).await },
|
async { write_committed_restore(&set_disks, &disk_stores, bucket, object, operation_id).await },
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
tokio::time::pause();
|
||||||
let _setup_type_guard = SetupTypeGuard::switch_to(SetupType::DistErasure).await;
|
let _setup_type_guard = SetupTypeGuard::switch_to(SetupType::DistErasure).await;
|
||||||
let barrier = RestoreFinalizeBarrier::install(bucket, object);
|
let barrier = RestoreFinalizeBarrier::install(bucket, object);
|
||||||
let finalize_set = Arc::clone(&set_disks);
|
let finalize_set = Arc::clone(&set_disks);
|
||||||
@@ -12677,7 +12678,7 @@ mod transition_upload_integrity_tests {
|
|||||||
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread", start_paused = true)]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn data_movement_cleanup_aborts_after_outer_lock_loss() {
|
async fn data_movement_cleanup_aborts_after_outer_lock_loss() {
|
||||||
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
||||||
@@ -12696,6 +12697,7 @@ mod transition_upload_integrity_tests {
|
|||||||
async { write_source(&set_disks, &disk_stores, bucket, object, &payload).await },
|
async { write_source(&set_disks, &disk_stores, bucket, object, &payload).await },
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
tokio::time::pause();
|
||||||
let expected = set_disks
|
let expected = set_disks
|
||||||
.load_file_info_versions_exact(bucket, object)
|
.load_file_info_versions_exact(bucket, object)
|
||||||
.await
|
.await
|
||||||
@@ -12737,7 +12739,7 @@ mod transition_upload_integrity_tests {
|
|||||||
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread", start_paused = true)]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn data_movement_cleanup_aborts_after_bucket_fence_loss() {
|
async fn data_movement_cleanup_aborts_after_bucket_fence_loss() {
|
||||||
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
||||||
@@ -12756,6 +12758,7 @@ mod transition_upload_integrity_tests {
|
|||||||
async { write_source(&set_disks, &disk_stores, bucket, object, &payload).await },
|
async { write_source(&set_disks, &disk_stores, bucket, object, &payload).await },
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
tokio::time::pause();
|
||||||
let expected = set_disks
|
let expected = set_disks
|
||||||
.load_file_info_versions_exact(bucket, object)
|
.load_file_info_versions_exact(bucket, object)
|
||||||
.await
|
.await
|
||||||
@@ -12893,7 +12896,7 @@ mod transition_upload_integrity_tests {
|
|||||||
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread", start_paused = true)]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn commit_lock_lost_after_upload_cleans_remote_candidate_and_preserves_source() {
|
async fn commit_lock_lost_after_upload_cleans_remote_candidate_and_preserves_source() {
|
||||||
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
||||||
@@ -12912,6 +12915,7 @@ mod transition_upload_integrity_tests {
|
|||||||
async { write_source(&set_disks, &disk_stores, bucket, object, &payload).await },
|
async { write_source(&set_disks, &disk_stores, bucket, object, &payload).await },
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
tokio::time::pause();
|
||||||
let tier_name = format!("COLDTIER{}", &Uuid::new_v4().simple().to_string()[..8]).to_uppercase();
|
let tier_name = format!("COLDTIER{}", &Uuid::new_v4().simple().to_string()[..8]).to_uppercase();
|
||||||
let backend = register_mock_tier(&runtime_sources::global_tier_config_mgr(), &tier_name).await;
|
let backend = register_mock_tier(&runtime_sources::global_tier_config_mgr(), &tier_name).await;
|
||||||
let _setup_type_guard = SetupTypeGuard::switch_to(SetupType::DistErasure).await;
|
let _setup_type_guard = SetupTypeGuard::switch_to(SetupType::DistErasure).await;
|
||||||
@@ -13344,7 +13348,7 @@ mod transition_upload_integrity_tests {
|
|||||||
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
assert_local_source_intact(&set_disks, bucket, object, &payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread", start_paused = true)]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn tagging_lock_lost_before_metadata_write_fails_closed() {
|
async fn tagging_lock_lost_before_metadata_write_fails_closed() {
|
||||||
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
let refresh_calls = Arc::new(AtomicUsize::new(0));
|
||||||
@@ -13362,6 +13366,7 @@ mod transition_upload_integrity_tests {
|
|||||||
async { write_source(&set_disks, &disk_stores, bucket, object, b"tagging source").await },
|
async { write_source(&set_disks, &disk_stores, bucket, object, b"tagging source").await },
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
tokio::time::pause();
|
||||||
|
|
||||||
let _setup_type_guard = SetupTypeGuard::switch_to(SetupType::DistErasure).await;
|
let _setup_type_guard = SetupTypeGuard::switch_to(SetupType::DistErasure).await;
|
||||||
let barrier = ObjectTaggingCommitBarrier::install(bucket, object);
|
let barrier = ObjectTaggingCommitBarrier::install(bucket, object);
|
||||||
|
|||||||
@@ -311,15 +311,33 @@ mod serial_tests {
|
|||||||
/// data_blocks disks is genuinely unrecoverable. With grace disabled it must
|
/// data_blocks disks is genuinely unrecoverable. With grace disabled it must
|
||||||
/// still be dangling-deleted (the delete path is LIVE and the guard is
|
/// still be dangling-deleted (the delete path is LIVE and the guard is
|
||||||
/// discriminating — it does not resurrect torn writes).
|
/// discriminating — it does not resurrect torn writes).
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn deep_heal_torn_minority_is_dangling_deleted_with_grace_zero() {
|
#[test]
|
||||||
|
fn deep_heal_torn_minority_is_dangling_deleted_with_grace_zero() {
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("deep-heal-torn-minority".to_string())
|
||||||
|
.stack_size(32 * 1024 * 1024)
|
||||||
|
.spawn(|| {
|
||||||
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("deep heal torn-minority test runtime should build");
|
||||||
|
|
||||||
|
runtime.block_on(deep_heal_torn_minority_is_dangling_deleted_with_grace_zero_inner());
|
||||||
|
})
|
||||||
|
.expect("deep heal torn-minority test thread should spawn")
|
||||||
|
.join()
|
||||||
|
.expect("deep heal torn-minority test thread should finish");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deep_heal_torn_minority_is_dangling_deleted_with_grace_zero_inner() {
|
||||||
let (disk_paths, ecstore, heal_storage) = heal_env_n(4).await;
|
let (disk_paths, ecstore, heal_storage) = heal_env_n(4).await;
|
||||||
let bucket = "b920-torn";
|
let bucket = "b920-torn";
|
||||||
let object = "obj.bin";
|
let object = "obj.bin";
|
||||||
create_versioned_bucket(&ecstore, bucket).await;
|
create_versioned_bucket(&ecstore, bucket).await;
|
||||||
|
|
||||||
let v1 = put_versioned(&ecstore, bucket, object, &versioned_test_data(3)).await;
|
let v1 = put_versioned(&ecstore, bucket, object, &versioned_test_data(3)).await;
|
||||||
|
wait_for_object_copies(&disk_paths, bucket, object).await;
|
||||||
|
|
||||||
// EC2+2 (4 drives, parity 2, data_blocks 2). Wipe the object ENTIRELY
|
// EC2+2 (4 drives, parity 2, data_blocks 2). Wipe the object ENTIRELY
|
||||||
// (meta + data) on 3 of 4 disks, leaving it on only 1 (< data_blocks 2):
|
// (meta + data) on 3 of 4 disks, leaving it on only 1 (< data_blocks 2):
|
||||||
|
|||||||
Reference in New Issue
Block a user