Skip to content

Commit d343bc4

Browse files
committed
db: fix virtual table race
We initialize the virtual params in the file cache, but this is racy. To fix this: - we change Virtual to a bool, and change `VirtualReaderParams` to a non-pointer field. - we add `AttachVirtualBacking` and use that instead of setting `FileBacking` directly. This function also initializes the `VirtualParams`. Fixes #4536
1 parent 9b5d34b commit d343bc4

25 files changed

+125
-111
lines changed

checkpoint.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ func (d *DB) Checkpoint(
283283
}
284284

285285
fileBacking := f.FileBacking
286-
if f.Virtual != nil {
286+
if f.Virtual {
287287
if _, ok := requiredVirtualBackingFiles[fileBacking.DiskFileNum]; ok {
288288
continue
289289
}

compaction.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ func newCompaction(
424424
if mustCopy {
425425
// If the source is virtual, it's best to just rewrite the file as all
426426
// conditions in the above comment are met.
427-
if meta.Virtual == nil {
427+
if !meta.Virtual {
428428
c.kind = compactionKindCopy
429429
}
430430
} else {
@@ -2401,7 +2401,7 @@ func (d *DB) cleanupVersionEdit(ve *versionEdit) {
24012401
deletedFiles[key.FileNum] = struct{}{}
24022402
}
24032403
for i := range ve.NewTables {
2404-
if ve.NewTables[i].Meta.Virtual != nil {
2404+
if ve.NewTables[i].Meta.Virtual {
24052405
// We handle backing files separately.
24062406
continue
24072407
}
@@ -2562,7 +2562,7 @@ func (d *DB) runCopyCompaction(
25622562
}
25632563
// Note that based on logic in the compaction picker, we're guaranteed
25642564
// inputMeta.Virtual is nil.
2565-
if inputMeta.Virtual != nil {
2565+
if inputMeta.Virtual {
25662566
panic(errors.AssertionFailedf("cannot do a copy compaction of a virtual sstable across local/remote storage"))
25672567
}
25682568
}
@@ -2700,7 +2700,7 @@ func (d *DB) runCopyCompaction(
27002700
Level: c.outputLevel.level,
27012701
Meta: newMeta,
27022702
}}
2703-
if newMeta.Virtual != nil {
2703+
if newMeta.Virtual {
27042704
ve.CreatedBackingTables = []*fileBacking{newMeta.FileBacking}
27052705
}
27062706
c.metrics[c.outputLevel.level] = &LevelMetrics{
@@ -2904,7 +2904,7 @@ func (d *DB) runDeleteOnlyCompaction(
29042904
// NewFiles.
29052905
usedBackingFiles := make(map[base.DiskFileNum]struct{})
29062906
for _, e := range ve.NewTables {
2907-
if e.Meta.Virtual != nil {
2907+
if e.Meta.Virtual {
29082908
usedBackingFiles[e.Meta.FileBacking.DiskFileNum] = struct{}{}
29092909
}
29102910
}

compaction_picker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ func pickCompactionSeedFile(
11861186
func responsibleForGarbageBytes(
11871187
virtualBackings *manifest.VirtualBackings, m *tableMetadata,
11881188
) uint64 {
1189-
if m.Virtual == nil {
1189+
if !m.Virtual {
11901190
return 0
11911191
}
11921192
useCount, virtualizedSize := virtualBackings.Usage(m.FileBacking.DiskFileNum)

compaction_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ func TestCompaction(t *testing.T) {
600600
defer provider.Close()
601601
for _, levelMetadata := range v.Levels {
602602
for meta := range levelMetadata.All() {
603-
if meta.Virtual != nil {
603+
if meta.Virtual {
604604
continue
605605
}
606606
f, err := provider.OpenForReading(context.Background(), base.FileTypeTable, meta.FileBacking.DiskFileNum, objstorage.OpenOptions{})
@@ -3001,7 +3001,7 @@ func hasExternalFiles(d *DB) bool {
30013001
for level := 0; level < manifest.NumLevels; level++ {
30023002
iter := v.Levels[level].Iter()
30033003
for m := iter.First(); m != nil; m = iter.Next() {
3004-
if m.Virtual != nil {
3004+
if m.Virtual {
30053005
meta, err := d.objProvider.Lookup(base.FileTypeTable, m.FileBacking.DiskFileNum)
30063006
if err != nil {
30073007
panic(err)

data_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,9 +1288,8 @@ func runSSTablePropertiesCmd(t *testing.T, td *datadriven.TestData, d *DB) strin
12881288

12891289
props := r.Properties.String()
12901290
env := sstable.ReadEnv{}
1291-
if m != nil && m.Virtual != nil {
1292-
m.InitVirtual()
1293-
env.Virtual = m.Virtual
1291+
if m != nil && m.Virtual {
1292+
env.Virtual = &m.VirtualParams
12941293
scaledProps := r.Properties.GetScaledProperties(m.FileBacking.Size, m.Size)
12951294
props = scaledProps.String()
12961295
}

db.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2248,7 +2248,7 @@ func (d *DB) SSTables(opts ...SSTablesOption) ([][]SSTableInfo, error) {
22482248
}
22492249
destTables[j].Properties = p
22502250
}
2251-
destTables[j].Virtual = m.Virtual != nil
2251+
destTables[j].Virtual = m.Virtual
22522252
destTables[j].BackingSSTNum = m.FileBacking.DiskFileNum
22532253
objMeta, err := d.objProvider.Lookup(base.FileTypeTable, m.FileBacking.DiskFileNum)
22542254
if err != nil {

download.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ func firstExternalFileInLevelIter(
403403
f = it.Next()
404404
}
405405
for ; f != nil && endBound.IsUpperBoundFor(cmp, f.Smallest.UserKey); f = it.Next() {
406-
if f.Virtual != nil && objstorage.IsExternalTable(objProvider, f.FileBacking.DiskFileNum) {
406+
if f.Virtual && objstorage.IsExternalTable(objProvider, f.FileBacking.DiskFileNum) {
407407
return f
408408
}
409409
}

download_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func TestDownloadTask(t *testing.T) {
181181
ch <- ErrCancelledCompaction
182182
} else {
183183
fmt.Fprintf(&buf, "downloading %s\n", f.FileNum)
184-
f.Virtual = nil
184+
f.Virtual = false
185185
f.FileBacking.DiskFileNum = base.DiskFileNum(f.FileNum)
186186
ch <- nil
187187
}

excise.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"github.com/cockroachdb/pebble/internal/base"
1313
"github.com/cockroachdb/pebble/internal/invariants"
1414
"github.com/cockroachdb/pebble/internal/manifest"
15-
"github.com/cockroachdb/pebble/sstable/virtual"
1615
)
1716

1817
// Excise atomically deletes all data overlapping with the provided span. All
@@ -103,9 +102,8 @@ func (d *DB) exciseTable(
103102
// https://github.com/cockroachdb/pebble/issues/2112 .
104103
if d.cmp(m.Smallest.UserKey, exciseSpan.Start) < 0 {
105104
leftTable = &tableMetadata{
106-
Virtual: &virtual.VirtualReaderParams{},
107-
FileBacking: m.FileBacking,
108-
FileNum: d.mu.versions.getNextFileNum(),
105+
Virtual: true,
106+
FileNum: d.mu.versions.getNextFileNum(),
109107
// Note that these are loose bounds for smallest/largest seqnums, but they're
110108
// sufficient for maintaining correctness.
111109
SmallestSeqNum: m.SmallestSeqNum,
@@ -118,6 +116,7 @@ func (d *DB) exciseTable(
118116
}
119117

120118
if leftTable.HasRangeKeys || leftTable.HasPointKeys {
119+
leftTable.AttachVirtualBacking(m.FileBacking)
121120
if err := determineExcisedTableSize(d.fileCache, m, leftTable); err != nil {
122121
return nil, nil, err
123122
}
@@ -136,9 +135,8 @@ func (d *DB) exciseTable(
136135
// See comment before the definition of leftFile for the motivation behind
137136
// calculating tight user-key bounds.
138137
rightTable = &tableMetadata{
139-
Virtual: &virtual.VirtualReaderParams{},
140-
FileBacking: m.FileBacking,
141-
FileNum: d.mu.versions.getNextFileNum(),
138+
Virtual: true,
139+
FileNum: d.mu.versions.getNextFileNum(),
142140
// Note that these are loose bounds for smallest/largest seqnums, but they're
143141
// sufficient for maintaining correctness.
144142
SmallestSeqNum: m.SmallestSeqNum,
@@ -150,6 +148,7 @@ func (d *DB) exciseTable(
150148
return nil, nil, err
151149
}
152150
if rightTable.HasRangeKeys || rightTable.HasPointKeys {
151+
rightTable.AttachVirtualBacking(m.FileBacking)
153152
if err := determineExcisedTableSize(d.fileCache, m, rightTable); err != nil {
154153
return nil, nil, err
155154
}
@@ -352,7 +351,7 @@ func applyExciseToVersionEdit(
352351
if leftTable == nil && rightTable == nil {
353352
return
354353
}
355-
if originalTable.Virtual == nil {
354+
if !originalTable.Virtual {
356355
// If the original table was virtual, then its file backing is already known
357356
// to the manifest; we don't need to create another file backing. Note that
358357
// there must be only one CreatedBackingTables entry per backing sstable.

file_cache.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,13 +311,17 @@ func (h *fileCacheHandle) estimateSize(
311311
return size, err
312312
}
313313

314-
func createReader(v *fileCacheValue, file *tableMetadata) (*sstable.Reader, sstable.ReadEnv) {
314+
func createReader(v *fileCacheValue, meta *tableMetadata) (*sstable.Reader, sstable.ReadEnv) {
315315
r := v.mustSSTableReader()
316316
env := sstable.ReadEnv{}
317-
if file.Virtual != nil {
318-
file.InitVirtual()
319-
env.Virtual = file.Virtual
320-
env.IsSharedIngested = v.isShared && file.SyntheticSeqNum() != 0
317+
if meta.Virtual {
318+
if invariants.Enabled {
319+
if meta.VirtualParams.FileNum == 0 || meta.VirtualParams.Lower.UserKey == nil || meta.VirtualParams.Upper.UserKey == nil {
320+
panic("virtual params not initialized")
321+
}
322+
}
323+
env.Virtual = &meta.VirtualParams
324+
env.IsSharedIngested = v.isShared && meta.SyntheticSeqNum() != 0
321325
}
322326
return r, env
323327
}
@@ -339,9 +343,13 @@ func (h *fileCacheHandle) withReader(
339343
env := sstable.ReadEnv{Block: blockEnv}
340344

341345
r := v.mustSSTableReader()
342-
if meta.Virtual != nil {
343-
meta.InitVirtual()
344-
env.Virtual = meta.Virtual
346+
if meta.Virtual {
347+
if invariants.Enabled {
348+
if meta.VirtualParams.FileNum == 0 || meta.VirtualParams.Lower.UserKey == nil || meta.VirtualParams.Upper.UserKey == nil {
349+
panic("virtual params not initialized")
350+
}
351+
}
352+
env.Virtual = &meta.VirtualParams
345353
env.IsSharedIngested = v.isShared && meta.SyntheticSeqNum() != 0
346354
}
347355

0 commit comments

Comments
 (0)