Skip to content

Commit 59904f7

Browse files
authored
fix: Skip non-tenant directories during block cleanup (#2916)
1 parent b4d686b commit 59904f7

File tree

2 files changed

+109
-4
lines changed

2 files changed

+109
-4
lines changed

pkg/ingester/retention.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,17 +361,18 @@ func (bm *realFSBlockManager) GetTenantIDs(ctx context.Context) ([]string, error
361361
return nil, ctx.Err()
362362
}
363363

364-
tenantDirs, err := fs.ReadDir(bm.FS, bm.Root)
364+
dirs, err := fs.ReadDir(bm.FS, bm.Root)
365365
if err != nil {
366366
return nil, err
367367
}
368368

369369
tenantIDs := make([]string, 0)
370-
for _, tenantDir := range tenantDirs {
371-
if !tenantDir.IsDir() {
370+
for _, dir := range dirs {
371+
if !bm.isTenantDir(bm.Root, dir) {
372372
continue
373373
}
374-
tenantIDs = append(tenantIDs, tenantDir.Name())
374+
375+
tenantIDs = append(tenantIDs, dir.Name())
375376
}
376377
return tenantIDs, nil
377378
}
@@ -446,3 +447,28 @@ func (bm *realFSBlockManager) DeleteBlock(ctx context.Context, block *tenantBloc
446447
return nil
447448
})
448449
}
450+
451+
// isTenantDir checks if a directory is a tenant directory.
452+
func (bm *realFSBlockManager) isTenantDir(path string, entry fs.DirEntry) bool {
453+
if !entry.IsDir() {
454+
return false
455+
}
456+
457+
subEntries, err := bm.FS.ReadDir(filepath.Join(path, entry.Name()))
458+
if err != nil {
459+
return false
460+
}
461+
462+
foundLocalDir := false
463+
for _, subEntry := range subEntries {
464+
if !subEntry.IsDir() {
465+
continue
466+
}
467+
468+
if subEntry.Name() == phlareDBLocalPath {
469+
foundLocalDir = true
470+
break
471+
}
472+
}
473+
return foundLocalDir
474+
}

pkg/ingester/retention_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/spf13/afero"
2020
"github.com/stretchr/testify/mock"
2121
"github.com/stretchr/testify/require"
22+
"golang.org/x/exp/slices"
2223

2324
"github.com/grafana/pyroscope/pkg/phlaredb"
2425
"github.com/grafana/pyroscope/pkg/phlaredb/shipper"
@@ -427,11 +428,16 @@ func TestFSBlockManager(t *testing.T) {
427428
fs.markBlocksShippedForTenant(t, tenantID, uploadedBlockIDs...)
428429
}
429430

431+
// Create a lost+found directory.
432+
fs.createDirectories(t, "lost+found")
433+
430434
t.Run("GetTenantIDs", func(t *testing.T) {
431435
bm := newFSBlockManager(root, e, fs)
432436
tenantIDs, err := bm.GetTenantIDs(context.Background())
433437
require.NoError(t, err)
434438
require.Equal(t, []string{"1218", "anonymous"}, tenantIDs)
439+
// Explicitly check lost+found isn't in tenant id list.
440+
require.NotContains(t, tenantIDs, "lost+found")
435441
})
436442

437443
t.Run("GetBlocksForTenant", func(t *testing.T) {
@@ -461,6 +467,55 @@ func TestFSBlockManager(t *testing.T) {
461467
})
462468
}
463469

470+
func TestFSBlockManager_isTenantDir(t *testing.T) {
471+
const root = "/data"
472+
dirPaths := []string{
473+
// Skip, not tenant ids
474+
"lost+found",
475+
".DS_Store",
476+
477+
// Skip, no local dir
478+
"1234/head/01HKWWF79V1STKXBNYW7WCMDGM",
479+
"1234/head/01HKWWF8939QM6E7BS69X0RASG",
480+
481+
// Tenant dirs
482+
"anonymous/local/01HKWWF3CTFC5EJN6JJ96TY4W9",
483+
"anonymous/local/01HKWWF4C298KVTEEQ3RW6TVHZ",
484+
"1218/local/01HKWWF5BB2DJVDP0DTMT9MDMN",
485+
"1218/local/01HKWWF6AKVZDCWQB12MHWG7FN",
486+
"9876/local",
487+
}
488+
filePaths := []string{
489+
// Skip all files
490+
"somefile.txt",
491+
}
492+
493+
fs := &mockFS{
494+
Fs: afero.NewMemMapFs(),
495+
Root: root,
496+
}
497+
fs.createDirectories(t, dirPaths...)
498+
fs.createFiles(t, filePaths...)
499+
500+
gotTenantIDs := []string{}
501+
entries, err := fs.ReadDir(fs.Root)
502+
require.NoError(t, err)
503+
504+
bm := &realFSBlockManager{
505+
Root: fs.Root,
506+
FS: fs,
507+
}
508+
for _, entry := range entries {
509+
if bm.isTenantDir(fs.Root, entry) {
510+
gotTenantIDs = append(gotTenantIDs, entry.Name())
511+
}
512+
}
513+
slices.Sort(gotTenantIDs)
514+
515+
wantTenantIDs := []string{"1218", "9876", "anonymous"}
516+
require.Equal(t, wantTenantIDs, gotTenantIDs)
517+
}
518+
464519
func TestSortBlocks(t *testing.T) {
465520
createAnonymousBlock := func(t *testing.T, blockID string, uploaded bool) *tenantBlock {
466521
t.Helper()
@@ -591,6 +646,30 @@ func (mfs *mockFS) markBlocksShippedForTenant(t *testing.T, tenantID string, blo
591646
}
592647
}
593648

649+
func (mfs *mockFS) createDirectories(t *testing.T, paths ...string) {
650+
t.Helper()
651+
for _, path := range paths {
652+
path = filepath.Join(mfs.Root, path)
653+
err := mfs.MkdirAll(path, 0755)
654+
if err != nil {
655+
t.Fatalf("failed to create directory: %s: %v", path, err)
656+
return
657+
}
658+
}
659+
}
660+
661+
func (mfs *mockFS) createFiles(t *testing.T, paths ...string) {
662+
t.Helper()
663+
for _, path := range paths {
664+
path = filepath.Join(mfs.Root, path)
665+
_, err := mfs.Create(path)
666+
if err != nil {
667+
t.Fatalf("failed to create file: %s: %v", path, err)
668+
return
669+
}
670+
}
671+
}
672+
594673
type mockBlockManager struct {
595674
mock.Mock
596675
}

0 commit comments

Comments
 (0)