From db0c0ffedd4ba1459fc8f9001475667ce370d409 Mon Sep 17 00:00:00 2001 From: Feng Ruohang Date: Fri, 6 Nov 2026 20:28:55 +0800 Subject: [PATCH] test(ilm): check the purge's history error end to end The implementation review of the delete-all reporting found that no committed test checked the error a purge returns on a drive whose metadata does not decode: restoring the old read quorum error passed. TestPurgeAllVersionsRefusesCorruptDrive now sends the purge through DeleteObject and checks errDeleteAllHistoryUnavailable and its SlowDownWrite mapping; with the old error it fails. The HELP text of expiry_incomplete_total now says that the tier_unsupported_skipped reason counts the scanner's per-drive observations, not expiry attempts. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Feng Ruohang --- cmd/erasure-server-pool-delete-all-reasons_test.go | 5 +++++ cmd/metrics-v2.go | 2 +- cmd/metrics-v3-ilm.go | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/erasure-server-pool-delete-all-reasons_test.go b/cmd/erasure-server-pool-delete-all-reasons_test.go index 9224a1c8a..88d8da173 100644 --- a/cmd/erasure-server-pool-delete-all-reasons_test.go +++ b/cmd/erasure-server-pool-delete-all-reasons_test.go @@ -170,6 +170,11 @@ func TestPurgeAllVersionsRefusesCorruptDrive(t *testing.T) { if delta[ilmIncompleteHistoryUnavailable] != 1 { t.Errorf("counted %v, want one %s", delta, ilmIncompleteHistoryUnavailable) } + // The purge names the cause, and callers see a write quorum error. + _, err = z.DeleteObject(ctx, p.bucket, key, ObjectOptions{DeleteAllVersions: true, Expiration: ExpirationOptions{Expire: true}}) + if !errors.Is(err, errDeleteAllHistoryUnavailable) || toAPIErrorCode(ctx, err) != ErrSlowDownWrite { + t.Errorf("purge on a corrupt drive: %v, want %v as SlowDownWrite", err, errDeleteAllHistoryUnavailable) + } if got := p.get(key); got != "v2" { t.Fatalf("the refused purge left %s readable", got) } diff --git a/cmd/metrics-v2.go b/cmd/metrics-v2.go index 6932bbf69..186047e76 100644 --- a/cmd/metrics-v2.go +++ b/cmd/metrics-v2.go @@ -2143,7 +2143,7 @@ func getILMNodeMetrics() *MetricsGroupV2 { Namespace: nodeMetricNamespace, Subsystem: ilmSubsystem, Name: expiryIncompleteTotal, - Help: "Total number of ILM expiry attempts since server start that ended without removing what they addressed, by reason", + Help: "Total number of ILM expiry attempts since server start that ended without removing what they addressed, by reason; tier_unsupported_skipped counts the scanner's per-drive observations of an expiry of all versions it did not queue", Type: counterMetric, }, VariableLabels: map[string]string{"reason": reason}, diff --git a/cmd/metrics-v3-ilm.go b/cmd/metrics-v3-ilm.go index 0d8738e9f..847295552 100644 --- a/cmd/metrics-v3-ilm.go +++ b/cmd/metrics-v3-ilm.go @@ -36,7 +36,7 @@ var ( ilmTransitionPendingTasksMD = NewGaugeMD(transitionPendingTasks, "Number of pending ILM transition tasks in the queue") ilmTransitionMissedImmediateTasksMD = NewCounterMD(transitionMissedImmediateTasks, "Number of missed immediate ILM transition tasks") ilmVersionsScannedMD = NewCounterMD(versionsScanned, "Total number of object versions checked for ILM actions since server start") - ilmExpiryIncompleteTotalMD = NewCounterMD(expiryIncompleteTotal, "Total number of ILM expiry attempts since server start that ended without removing what they addressed, by reason", "reason") + ilmExpiryIncompleteTotalMD = NewCounterMD(expiryIncompleteTotal, "Total number of ILM expiry attempts since server start that ended without removing what they addressed, by reason; tier_unsupported_skipped counts the scanner's per-drive observations of an expiry of all versions it did not queue", "reason") ) // loadILMMetrics - `MetricsLoaderFn` for ILM metrics.