Pin the close before the reopen in the archive sweep (closes #103)
check / check (push) Waiting to run
check / check (push) Waiting to run
Add a test that keeps the archive's connection from before a sweep and checks that the sweep closed it. Without the close before the reopen, the reopen replaced the handle without closing it and one connection leaked per archive per sweep, yet every test passed. The sweeper's listing query now takes the sweep's context, and a comment on its cancel function says why it needs no lock. The handlers' type-filtered count of a webhook's remaining targets no longer exists: since each database target has its own archive file, deleting a target evicts that target's writer alone. Model: opus-5-5
This commit is contained in:
@@ -45,7 +45,11 @@ type ArchiveSweeper struct {
|
||||
eng *Engine
|
||||
log *slog.Logger
|
||||
interval time.Duration
|
||||
|
||||
// cancel needs no lock: fx runs start and then stop on the one
|
||||
// goroutine that runs the app, so they never overlap.
|
||||
cancel context.CancelFunc
|
||||
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
@@ -163,6 +167,7 @@ func (s *ArchiveSweeper) sweep(ctx context.Context) {
|
||||
var targets []database.Target
|
||||
|
||||
err := s.db.DB().
|
||||
WithContext(ctx).
|
||||
Model(&database.Target{}).
|
||||
Where("type = ?", database.TargetTypeDatabase).
|
||||
Find(&targets).Error
|
||||
|
||||
@@ -636,6 +636,34 @@ func TestArchiveSweep_LeavesArchiveClosed(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// TestArchiveSweep_ClosesHandleBeforeReopening proves the sweep
|
||||
// closes the handle it finds open before it reopens the file.
|
||||
// TestArchiveSweep_LeavesArchiveClosed cannot see this: without the
|
||||
// close, the reopen replaces the handle without closing it, the
|
||||
// sweep then closes only the new one, and one connection leaks per
|
||||
// archive per sweep.
|
||||
func TestArchiveSweep_ClosesHandleBeforeReopening(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
path := filepath.Join(t.TempDir(), "archive.db")
|
||||
|
||||
w := delivery.NewExportArchiveWriter(
|
||||
path, archiveTestLogger(), 0,
|
||||
)
|
||||
|
||||
require.NoError(t, w.Open(time.Hour))
|
||||
|
||||
before, err := w.DB().DB()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, w.SweepExpired(time.Hour))
|
||||
|
||||
assert.Error(
|
||||
t, before.PingContext(t.Context()),
|
||||
"the handle open before the sweep must be closed by it",
|
||||
)
|
||||
}
|
||||
|
||||
// TestArchiveSweep_ClosesHandleOfRegisteredWriter states the same
|
||||
// guarantee end to end, through the real sweeper and a writer the
|
||||
// registry keeps.
|
||||
|
||||
Reference in New Issue
Block a user