Skip to content

Commit 2ce1a2a

Browse files
committed
Merge branch 'fix/retention-lvm-nil-repo' into 'master'
fix(retention): skip pools without repo data in auto-deletion sweep Closes #742 See merge request postgres-ai/database-lab!1171
2 parents a8379bc + 41d8489 commit 2ce1a2a

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

engine/internal/srv/auto_delete.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,22 @@ func mapKeys(set map[string]struct{}) []string {
203203
return keys
204204
}
205205

206-
// pool reconciles one active pool. Snapshots and branches share a single repo read.
206+
// pool reconciles one active pool. Snapshots and branches share a single repo read; pools that
207+
// report no repo data (e.g. LVM) have no metadata to reconcile and are skipped.
207208
func (sw *sweep) pool(fsm pool.FSManager) {
208209
repo, err := fsm.GetRepo()
209210
if err != nil {
210211
log.Err(fmt.Sprintf("auto-deletion: failed to read repo for pool %s: %v", fsm.Pool().Name, err))
211212
return
212213
}
213214

215+
// thin-clone managers without repo support (LVM) return a nil repo with no error;
216+
// such pools have no snapshot or branch metadata to reconcile
217+
if repo == nil {
218+
log.Dbg(fmt.Sprintf("auto-deletion: pool %s provides no repo data; skipping", fsm.Pool().Name))
219+
return
220+
}
221+
214222
if sw.retention.UnusedSnapshotMinutes > 0 {
215223
sw.snapshots(fsm, repo)
216224
}

engine/internal/srv/auto_delete_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import (
1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/require"
1313

14+
"gitlab.com/postgres-ai/database-lab/v3/internal/provision/pool"
15+
"gitlab.com/postgres-ai/database-lab/v3/internal/provision/resources"
16+
"gitlab.com/postgres-ai/database-lab/v3/internal/provision/thinclones"
17+
srvCfg "gitlab.com/postgres-ai/database-lab/v3/internal/srv/config"
1418
"gitlab.com/postgres-ai/database-lab/v3/internal/webhooks"
1519
"gitlab.com/postgres-ai/database-lab/v3/pkg/models"
1620
)
@@ -282,3 +286,44 @@ func TestMapKeys(t *testing.T) {
282286
func timePtr(t time.Time) *time.Time {
283287
return &t
284288
}
289+
290+
// nilRepoFSM is a pool.FSManager whose GetRepo reports no repo data with no error, mirroring
291+
// thin-clone managers without repo support (LVM). It embeds the interface so unimplemented
292+
// methods are absent at compile time. ListProtection and GetProtection are the first FSManager
293+
// calls the sweep makes once it passes the nil-repo guard; they count reaches so a skipped pool
294+
// can be asserted positively, and returning empty (instead of panicking on the nil embedded
295+
// interface) keeps the without-guard failure at the same nil-repo dereference that crashes
296+
// production (branchHeadSet -> repo.Branches).
297+
type nilRepoFSM struct {
298+
pool.FSManager
299+
name string
300+
reached int
301+
}
302+
303+
func (m *nilRepoFSM) GetRepo() (*models.Repo, error) {
304+
return nil, nil
305+
}
306+
307+
func (m *nilRepoFSM) Pool() *resources.Pool {
308+
return &resources.Pool{Name: m.name}
309+
}
310+
311+
func (m *nilRepoFSM) ListProtection() (map[string]thinclones.ProtectionProperties, error) {
312+
m.reached++
313+
314+
return nil, nil
315+
}
316+
317+
func (m *nilRepoFSM) GetProtection(string) (thinclones.ProtectionProperties, error) {
318+
m.reached++
319+
320+
return thinclones.ProtectionProperties{}, nil
321+
}
322+
323+
func TestSweepPoolSkipsNilRepo(t *testing.T) {
324+
sw := &sweep{retention: srvCfg.Retention{UnusedSnapshotMinutes: 1, UnusedBranchMinutes: 1}}
325+
fsm := &nilRepoFSM{name: "lvm_pool"}
326+
327+
assert.NotPanics(t, func() { sw.pool(fsm) })
328+
assert.Zero(t, fsm.reached, "sweep must skip a pool with no repo data, not reconcile it")
329+
}

0 commit comments

Comments
 (0)