Skip to content

Commit b64943a

Browse files
committed
db: ignore closed EFOS in check
Fixes #5390
1 parent ca08fb0 commit b64943a

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed

db.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,11 @@ type DB struct {
494494
}
495495

496496
snapshots struct {
497-
// The list of active snapshots.
497+
// The list of active snapshots, including EFOS snapshots that have not
498+
// transitioned. An EFOS snapshot that transitions to FOS is removed
499+
// from this list atomically with the transition (using DB.mu). A closed
500+
// snapshot (including an EFOS snapshot) may still be in this list
501+
// transiently, since the removal is not atomic with the close.
498502
snapshotList
499503

500504
// The cumulative count and size of snapshot-pinned keys written to

ingest.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,8 +2216,16 @@ func (d *DB) ingestApply(
22162216
if exciseSpan.Valid() {
22172217
for s := d.mu.snapshots.root.next; s != &d.mu.snapshots.root; s = s.next {
22182218
// Skip non-EFOS snapshots, and also skip any EFOS that were created
2219-
// *after* the excise.
2220-
if s.efos == nil || base.Visible(exciseSeqNum, s.efos.seqNum, base.SeqNumMax) {
2219+
// *after* the excise, or are already closed.
2220+
//
2221+
// NB: this code can race with EventuallyFileOnlySnapshot.Close, which
2222+
// closes the channel that isClosed() reads, without holding DB.mu. This
2223+
// is harmless: if this code observes !isClosed, that means the EFOS was
2224+
// not closed until after the excise has already updated the LSM state.
2225+
// It is then fair to confirm that the EFOS did not overlap with the
2226+
// excise.
2227+
if s.efos == nil || base.Visible(exciseSeqNum, s.efos.seqNum, base.SeqNumMax) ||
2228+
s.efos.isClosed() {
22212229
continue
22222230
}
22232231
efos := s.efos
@@ -2227,7 +2235,13 @@ func (d *DB) ingestApply(
22272235
// snapshot.
22282236
for i := range efos.protectedRanges {
22292237
if efos.protectedRanges[i].OverlapsKeyRange(d.cmp, exciseSpan) {
2230-
panic("unexpected excise of an EventuallyFileOnlySnapshot's bounds")
2238+
panic(errors.AssertionFailedf(
2239+
"unexpected excise of an EventuallyFileOnlySnapshot's bounds: "+
2240+
"excise [%s, %s) seqnum: %s, efos [%s, %s) seqnum: %s transitioned: %t closed: %t visible: %s",
2241+
d.opts.Comparer.FormatKey(exciseSpan.Start), d.opts.Comparer.FormatKey(exciseSpan.End),
2242+
exciseSeqNum, d.opts.Comparer.FormatKey(efos.protectedRanges[i].Start),
2243+
d.opts.Comparer.FormatKey(efos.protectedRanges[i].End), efos.seqNum,
2244+
efos.hasTransitioned(), efos.isClosed(), d.mu.versions.visibleSeqNum.Load()))
22312245
}
22322246
}
22332247
}

snapshot.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,15 @@ func (es *EventuallyFileOnlySnapshot) hasTransitioned() bool {
377377
return es.mu.vers != nil
378378
}
379379

380+
func (es *EventuallyFileOnlySnapshot) isClosed() bool {
381+
select {
382+
case <-es.closed:
383+
return true
384+
default:
385+
return false
386+
}
387+
}
388+
380389
// waitForFlush waits for a flush on any memtables that need to be flushed
381390
// before this EFOS can transition to a file-only snapshot. If this EFOS is
382391
// waiting on a flush of the mutable memtable, it forces a rotation within

0 commit comments

Comments
 (0)