@@ -44,32 +44,27 @@ func (d *DB) Excise(ctx context.Context, span KeyRange) error {
4444 return err
4545}
4646
47- // exciseTable updates ve to include a replacement of the table m with new
48- // virtual sstables that exclude exciseSpan, returning a slice of newly-created
49- // files if any. If the entirety of m is deleted by exciseSpan, no new sstables
50- // are added and m is deleted. Note that ve is updated in-place .
47+ // exciseTable initializes up to two virtual tables for what is left over after
48+ // excising the given span from the table.
49+ //
50+ // Returns the left and/or right tables, if they exist .
5151//
5252// The file bounds must overlap with the excise span.
5353//
5454// This method is agnostic to whether d.mu is held or not. Some cases call it with
5555// the db mutex held (eg. ingest-time excises), while in the case of compactions
5656// the mutex is not held.
5757func (d * DB ) exciseTable (
58- ctx context.Context , exciseSpan base.UserKeyBounds , m * tableMetadata , ve * versionEdit , level int ,
59- ) ([]manifest.NewTableEntry , error ) {
60- numCreatedFiles := 0
58+ ctx context.Context , exciseSpan base.UserKeyBounds , m * tableMetadata , level int ,
59+ ) (leftTable , rightTable * tableMetadata , _ error ) {
6160 // Check if there's actually an overlap between m and exciseSpan.
6261 mBounds := m .UserKeyBounds ()
6362 if ! exciseSpan .Overlaps (d .cmp , & mBounds ) {
64- return nil , base .AssertionFailedf ("excise span does not overlap table" )
63+ return nil , nil , base .AssertionFailedf ("excise span does not overlap table" )
6564 }
66- ve .DeletedTables [deletedFileEntry {
67- Level : level ,
68- FileNum : m .FileNum ,
69- }] = m
7065 // Fast path: m sits entirely within the exciseSpan, so just delete it.
7166 if exciseSpan .ContainsInternalKey (d .cmp , m .Smallest ) && exciseSpan .ContainsInternalKey (d .cmp , m .Largest ) {
72- return nil , nil
67+ return nil , nil , nil
7368 }
7469
7570 // The file partially overlaps the excise span; we will need to open it to
@@ -79,11 +74,10 @@ func (d *DB) exciseTable(
7974 layer : manifest .Level (level ),
8075 }, internalIterOpts {}, iterPointKeys | iterRangeDeletions | iterRangeKeys )
8176 if err != nil {
82- return nil , err
77+ return nil , nil , err
8378 }
8479 defer iters .CloseAll ()
8580
86- needsBacking := false
8781 // Create a file to the left of the excise span, if necessary.
8882 // The bounds of this file will be [m.Smallest, lastKeyBefore(exciseSpan.Start)].
8983 //
@@ -107,7 +101,7 @@ func (d *DB) exciseTable(
107101 // have changed since our previous calculation. Do this optimiaztino as part of
108102 // https://github.com/cockroachdb/pebble/issues/2112 .
109103 if d .cmp (m .Smallest .UserKey , exciseSpan .Start ) < 0 {
110- leftFile : = & tableMetadata {
104+ leftTable = & tableMetadata {
111105 Virtual : true ,
112106 FileBacking : m .FileBacking ,
113107 FileNum : d .mu .versions .getNextFileNum (),
@@ -118,93 +112,73 @@ func (d *DB) exciseTable(
118112 LargestSeqNumAbsolute : m .LargestSeqNumAbsolute ,
119113 SyntheticPrefixAndSuffix : m .SyntheticPrefixAndSuffix ,
120114 }
121- if err := determineLeftTableBounds (d .cmp , m , leftFile , exciseSpan .Start , iters ); err != nil {
122- return nil , err
115+ if err := determineLeftTableBounds (d .cmp , m , leftTable , exciseSpan .Start , iters ); err != nil {
116+ return nil , nil , err
123117 }
124118
125- if leftFile .HasRangeKeys || leftFile .HasPointKeys {
119+ if leftTable .HasRangeKeys || leftTable .HasPointKeys {
126120 var err error
127- leftFile .Size , err = d .fileCache .estimateSize (m , leftFile .Smallest .UserKey , leftFile .Largest .UserKey )
121+ leftTable .Size , err = d .fileCache .estimateSize (m , leftTable .Smallest .UserKey , leftTable .Largest .UserKey )
128122 if err != nil {
129- return nil , err
123+ return nil , nil , err
130124 }
131- if leftFile .Size == 0 {
125+ if leftTable .Size == 0 {
132126 // On occasion, estimateSize gives us a low estimate, i.e. a 0 file size,
133127 // such as if the excised file only has range keys/dels and no point
134128 // keys. This can cause panics in places where we divide by file sizes.
135129 // Correct for it here.
136- leftFile .Size = 1
130+ leftTable .Size = 1
137131 }
138- if err := leftFile .Validate (d .cmp , d .opts .Comparer .FormatKey ); err != nil {
139- return nil , err
132+ if err := leftTable .Validate (d .cmp , d .opts .Comparer .FormatKey ); err != nil {
133+ return nil , nil , err
140134 }
141- leftFile .ValidateVirtual (m )
142- ve .NewTables = append (ve .NewTables , newTableEntry {Level : level , Meta : leftFile })
143- needsBacking = true
144- numCreatedFiles ++
135+ leftTable .ValidateVirtual (m )
136+ } else {
137+ leftTable = nil
145138 }
146139 }
147140 // Create a file to the right, if necessary.
148- if exciseSpan .ContainsInternalKey (d .cmp , m .Largest ) {
149- // No key exists to the right of the excise span in this file.
150- if needsBacking && ! m .Virtual {
151- // If m is virtual, then its file backing is already known to the manifest.
152- // We don't need to create another file backing. Note that there must be
153- // only one CreatedBackingTables entry per backing sstable. This is
154- // indicated by the VersionEdit.CreatedBackingTables invariant.
155- ve .CreatedBackingTables = append (ve .CreatedBackingTables , m .FileBacking )
156- }
157- return ve .NewTables [len (ve .NewTables )- numCreatedFiles :], nil
158- }
159- // Create a new file, rightFile, between [firstKeyAfter(exciseSpan.End), m.Largest].
160- //
161- // See comment before the definition of leftFile for the motivation behind
162- // calculating tight user-key bounds.
163- rightFile := & tableMetadata {
164- Virtual : true ,
165- FileBacking : m .FileBacking ,
166- FileNum : d .mu .versions .getNextFileNum (),
167- // Note that these are loose bounds for smallest/largest seqnums, but they're
168- // sufficient for maintaining correctness.
169- SmallestSeqNum : m .SmallestSeqNum ,
170- LargestSeqNum : m .LargestSeqNum ,
171- LargestSeqNumAbsolute : m .LargestSeqNumAbsolute ,
172- SyntheticPrefixAndSuffix : m .SyntheticPrefixAndSuffix ,
173- }
174- if err := determineRightTableBounds (d .cmp , m , rightFile , exciseSpan .End , iters ); err != nil {
175- return nil , err
176- }
177- if rightFile .HasRangeKeys || rightFile .HasPointKeys {
178- var err error
179- rightFile .Size , err = d .fileCache .estimateSize (m , rightFile .Smallest .UserKey , rightFile .Largest .UserKey )
180- if err != nil {
181- return nil , err
141+ if ! exciseSpan .End .IsUpperBoundForInternalKey (d .cmp , m .Largest ) {
142+ // Create a new file, rightFile, between [firstKeyAfter(exciseSpan.End), m.Largest].
143+ //
144+ // See comment before the definition of leftFile for the motivation behind
145+ // calculating tight user-key bounds.
146+ rightTable = & tableMetadata {
147+ Virtual : true ,
148+ FileBacking : m .FileBacking ,
149+ FileNum : d .mu .versions .getNextFileNum (),
150+ // Note that these are loose bounds for smallest/largest seqnums, but they're
151+ // sufficient for maintaining correctness.
152+ SmallestSeqNum : m .SmallestSeqNum ,
153+ LargestSeqNum : m .LargestSeqNum ,
154+ LargestSeqNumAbsolute : m .LargestSeqNumAbsolute ,
155+ SyntheticPrefixAndSuffix : m .SyntheticPrefixAndSuffix ,
182156 }
183- if rightFile .Size == 0 {
184- // On occasion, estimateSize gives us a low estimate, i.e. a 0 file size,
185- // such as if the excised file only has range keys/dels and no point keys.
186- // This can cause panics in places where we divide by file sizes. Correct
187- // for it here.
188- rightFile .Size = 1
157+ if err := determineRightTableBounds (d .cmp , m , rightTable , exciseSpan .End , iters ); err != nil {
158+ return nil , nil , err
189159 }
190- if err := rightFile .Validate (d .cmp , d .opts .Comparer .FormatKey ); err != nil {
191- return nil , err
160+ if rightTable .HasRangeKeys || rightTable .HasPointKeys {
161+ var err error
162+ rightTable .Size , err = d .fileCache .estimateSize (m , rightTable .Smallest .UserKey , rightTable .Largest .UserKey )
163+ if err != nil {
164+ return nil , nil , err
165+ }
166+ if rightTable .Size == 0 {
167+ // On occasion, estimateSize gives us a low estimate, i.e. a 0 file size,
168+ // such as if the excised file only has range keys/dels and no point keys.
169+ // This can cause panics in places where we divide by file sizes. Correct
170+ // for it here.
171+ rightTable .Size = 1
172+ }
173+ if err := rightTable .Validate (d .cmp , d .opts .Comparer .FormatKey ); err != nil {
174+ return nil , nil , err
175+ }
176+ rightTable .ValidateVirtual (m )
177+ } else {
178+ rightTable = nil
192179 }
193- rightFile .ValidateVirtual (m )
194- ve .NewTables = append (ve .NewTables , newTableEntry {Level : level , Meta : rightFile })
195- needsBacking = true
196- numCreatedFiles ++
197- }
198-
199- if needsBacking && ! m .Virtual {
200- // If m is virtual, then its file backing is already known to the manifest.
201- // We don't need to create another file backing. Note that there must be
202- // only one CreatedBackingTables entry per backing sstable. This is
203- // indicated by the VersionEdit.CreatedBackingTables invariant.
204- ve .CreatedBackingTables = append (ve .CreatedBackingTables , m .FileBacking )
205180 }
206-
207- return ve .NewTables [len (ve .NewTables )- numCreatedFiles :], nil
181+ return leftTable , rightTable , nil
208182}
209183
210184// exciseOverlapBounds examines the provided list of snapshots, examining each
@@ -363,3 +337,34 @@ func determineRightTableBounds(
363337 }
364338 return nil
365339}
340+
341+ // applyExciseToVersionEdit updates ve with a table deletion for the original
342+ // table and table additions for the left and/or right table.
343+ //
344+ // Either or both of leftTable/rightTable can be nil.
345+ func applyExciseToVersionEdit (
346+ ve * versionEdit , originalTable , leftTable , rightTable * tableMetadata , level int ,
347+ ) (newFiles []manifest.NewTableEntry ) {
348+ ve .DeletedTables [deletedFileEntry {
349+ Level : level ,
350+ FileNum : originalTable .FileNum ,
351+ }] = originalTable
352+ if leftTable == nil && rightTable == nil {
353+ return
354+ }
355+ if ! originalTable .Virtual {
356+ // If the original table was virtual, then its file backing is already known
357+ // to the manifest; we don't need to create another file backing. Note that
358+ // there must be only one CreatedBackingTables entry per backing sstable.
359+ // This is indicated by the VersionEdit.CreatedBackingTables invariant.
360+ ve .CreatedBackingTables = append (ve .CreatedBackingTables , originalTable .FileBacking )
361+ }
362+ originalLen := len (ve .NewTables )
363+ if leftTable != nil {
364+ ve .NewTables = append (ve .NewTables , newTableEntry {Level : level , Meta : leftTable })
365+ }
366+ if rightTable != nil {
367+ ve .NewTables = append (ve .NewTables , newTableEntry {Level : level , Meta : rightTable })
368+ }
369+ return ve .NewTables [originalLen :]
370+ }
0 commit comments