Skip to content

Commit ff0eb69

Browse files
committed
db: test to demonstrate excise and EFOS race bug
Informs #5390
1 parent ae3bfad commit ff0eb69

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

ingest.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,6 +1715,9 @@ func (d *DB) ingest(ctx context.Context, args ingestArgs) (IngestOperationStats,
17151715
waitFlushDuration = waitFlushStart.Elapsed()
17161716
}
17171717

1718+
if d.opts.private.testingBeforeIngestApplyFunc != nil {
1719+
d.opts.private.testingBeforeIngestApplyFunc()
1720+
}
17181721
// Assign the sstables to the correct level in the LSM and apply the
17191722
// version edit.
17201723
ve, manifestUpdateDuration, err = d.ingestApply(ctx, jobID, loadResult, mut, args.ExciseSpan, args.ExciseBoundsPolicy, seqNum)

options.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,10 @@ type Options struct {
11871187
// obsolete file deletion (to make events deterministic).
11881188
testingAlwaysWaitForCleanup bool
11891189

1190+
// testingBeforeIngestApplyFunc when non-nil, is called when ingesting,
1191+
// before calling DB.ingestApply.
1192+
testingBeforeIngestApplyFunc func()
1193+
11901194
// fsCloser holds a closer that should be invoked after a DB using these
11911195
// Options is closed. This is used to automatically stop the
11921196
// long-running goroutine associated with the disk-health-checking FS.

snapshot_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,56 @@ func TestNewSnapshotRace(t *testing.T) {
383383
wg.Wait()
384384
require.NoError(t, d.Close())
385385
}
386+
387+
// Test for https://github.com/cockroachdb/pebble/issues/5390.
388+
func TestEFOSAndExciseRace(t *testing.T) {
389+
// Skipping since: panic: unexpected excise of an
390+
// EventuallyFileOnlySnapshot's bounds.
391+
t.Skip("#5390")
392+
o := &Options{
393+
FS: vfs.NewMem(),
394+
L0CompactionThreshold: 10,
395+
DisableAutomaticCompactions: true,
396+
FormatMajorVersion: FormatNewest,
397+
}
398+
beforeIngestApply := make(chan struct{}, 1)
399+
waitForCh := make(chan struct{}, 1)
400+
o.private.testingBeforeIngestApplyFunc = func() {
401+
beforeIngestApply <- struct{}{}
402+
<-waitForCh
403+
}
404+
d, err := Open("", o)
405+
require.NoError(t, err)
406+
defer d.Close()
407+
require.NoError(t, d.Set([]byte("c"), nil, nil))
408+
require.NoError(t, d.Flush())
409+
require.NoError(t, d.Set([]byte("a"), nil, nil))
410+
go func() {
411+
_, err = d.IngestAndExcise(context.Background(), nil, nil, nil,
412+
KeyRange{Start: []byte("b"), End: []byte("d")})
413+
}()
414+
<-beforeIngestApply
415+
// IngestAndExcise is blocked, after waiting for memtable flush.
416+
//
417+
// Create a EFOS that overlaps with the excise.
418+
snap := d.NewEventuallyFileOnlySnapshot([]KeyRange{{Start: []byte("a"), End: []byte("d")}})
419+
defer snap.Close()
420+
checkIter := func() {
421+
iter, err := snap.NewIter(nil)
422+
// The iter sees a and c, since it precedes the excise.
423+
require.NoError(t, err)
424+
require.True(t, iter.First())
425+
require.Equal(t, []byte("a"), iter.Key())
426+
require.True(t, iter.Next())
427+
require.Equal(t, []byte("c"), iter.Key())
428+
require.False(t, iter.Next())
429+
require.NoError(t, iter.Close())
430+
}
431+
checkIter()
432+
// Unblock the excise.
433+
waitForCh <- struct{}{}
434+
// Flush and transition to FOS.
435+
require.NoError(t, d.Flush())
436+
require.NoError(t, snap.WaitForFileOnlySnapshot(context.Background(), 0))
437+
checkIter()
438+
}

0 commit comments

Comments
 (0)