From f1a37d7ac1e36815a907bf1c8a7038451e632d2a Mon Sep 17 00:00:00 2001 From: dawn Date: Wed, 21 Oct 2026 22:06:44 +0300 Subject: [PATCH] spindle/db: give back the quota of a cache upload a restart interrupted Signed-off-by: dawn --- spindle/db/quota.go | 13 +++---------- spindle/db/quota_test.go | 11 ++++++----- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/spindle/db/quota.go b/spindle/db/quota.go index 0630375c2..aec7ebcbd 100644 --- a/spindle/db/quota.go +++ b/spindle/db/quota.go @@ -841,19 +841,12 @@ func classifyStatus(used, limit int64) string { return "under_limit" } +// a publishing reservation is released with the reserved ones. nothing at boot can tell whether +// its object landed, and counting one that never did is permanent where forgetting one that did is +// one object's bytes func (d *QuotaStore) Recover(ctx context.Context, liveIDs []string) error { return d.withTx(ctx, func(conn *sql.Conn) error { _, err := conn.ExecContext(ctx, ` - INSERT INTO quota_allocations (repo_did, resource, kind, key, amount) - SELECT repo_did, resource, kind, key, amount FROM quota_reservations WHERE phase = 'publishing' - ON CONFLICT(repo_did, resource, kind, key) - DO UPDATE SET amount = excluded.amount - `) - if err != nil { - return err - } - - _, err = conn.ExecContext(ctx, ` DELETE FROM quota_reservations WHERE phase IN ('reserved', 'publishing') `) if err != nil { diff --git a/spindle/db/quota_test.go b/spindle/db/quota_test.go index 2edffc1e7..19a29d079 100644 --- a/spindle/db/quota_test.go +++ b/spindle/db/quota_test.go @@ -478,7 +478,8 @@ func TestCommitUpdatesChangedAmountForSameKey(t *testing.T) { } } -func TestRecoverUpdatesChangedAmountForPublishingKey(t *testing.T) { +// the interrupted 80 may never have landed, so recovery keeps the committed 60 +func TestRecoverKeepsTheCommittedAmountOverAnInterruptedRepublish(t *testing.T) { ctx := context.Background() database, err := Make(ctx, filepath.Join(t.TempDir(), "spindle.db")) if err != nil { @@ -523,8 +524,8 @@ func TestRecoverUpdatesChangedAmountForPublishingKey(t *testing.T) { `, identity.RepoDID, quota.ResourceCacheStorageBytes, quota.KindNixCache, "nar/object.nar").Scan(&amount); err != nil { t.Fatal(err) } - if amount != 80 { - t.Fatalf("recovered amount = %d, want 80", amount) + if amount != 60 { + t.Fatalf("recovered amount = %d, want the committed 60", amount) } } @@ -608,8 +609,8 @@ func TestRecoveryPhases(t *testing.T) { if err != nil { t.Fatal(err) } - if count != 1 { - t.Fatal("expected publishing object to be committed") + if count != 0 { + t.Fatal("expected the publishing reservation to be released, not counted as an object") } err = database.QueryRowContext(ctx, ` -- 2.54.0