@@ -7,6 +7,7 @@ package pebble
77import (
88 "context"
99 "fmt"
10+ "math/rand/v2"
1011 "strconv"
1112 "strings"
1213 "sync"
@@ -306,6 +307,7 @@ func TestExcise(t *testing.T) {
306307 fileNums [base .FileNum (fNum )] = struct {}{}
307308 }
308309 d .mu .Lock ()
310+ defer d .mu .Unlock ()
309311 currVersion := d .mu .versions .currentVersion ()
310312 var ptr * manifest.FileBacking
311313 for _ , level := range currVersion .Levels {
@@ -322,7 +324,6 @@ func TestExcise(t *testing.T) {
322324 }
323325 }
324326 }
325- d .mu .Unlock ()
326327 return "file backings are the same"
327328 case "compact" :
328329 if len (td .CmdArgs ) != 2 {
@@ -431,7 +432,6 @@ func TestConcurrentExcise(t *testing.T) {
431432 FormatMajorVersion : FormatVirtualSSTables ,
432433 Logger : testLogger {t },
433434 }
434- // lel.
435435 lel := MakeLoggingEventListener (testLogger {t })
436436 tel := TeeEventListener (lel , el )
437437 opts1 .EventListener = & tel
@@ -763,3 +763,108 @@ func TestConcurrentExcise(t *testing.T) {
763763 }
764764 })
765765}
766+
767+ func TestExciseBounds (t * testing.T ) {
768+ const sstPath = "foo.sst"
769+ var fs vfs.FS
770+ var m * manifest.TableMetadata
771+ cmp := base .DefaultComparer .Compare
772+
773+ datadriven .RunTest (t , "testdata/excise_bounds" , func (t * testing.T , td * datadriven.TestData ) string {
774+ checkErr := func (err error ) {
775+ if err != nil {
776+ t .Helper ()
777+ td .Fatalf (t , "%v" , err )
778+ }
779+ }
780+ var buf strings.Builder
781+ printBounds := func (title string , m * tableMetadata ) {
782+ fmt .Fprintf (& buf , "%s:\n " , title )
783+ fmt .Fprintf (& buf , " overall: %v - %v\n " , m .Smallest , m .Largest )
784+ if m .HasPointKeys {
785+ fmt .Fprintf (& buf , " point: %v - %v\n " , m .SmallestPointKey , m .LargestPointKey )
786+ }
787+ if m .HasRangeKeys {
788+ fmt .Fprintf (& buf , " range: %v - %v\n " , m .SmallestRangeKey , m .LargestRangeKey )
789+ }
790+ }
791+ switch td .Cmd {
792+ case "build-sst" :
793+ fs = vfs .NewMem ()
794+ var writerOpts sstable.WriterOptions
795+ writerOpts .TableFormat = sstable .TableFormat (rand .IntN (int (sstable .TableFormatMax )) + 1 )
796+ // We need at least v2 for tests with range keys.
797+ writerOpts .TableFormat = max (writerOpts .TableFormat , sstable .TableFormatPebblev2 )
798+ sstMeta , err := runBuildSSTCmd (td .Input , td .CmdArgs , sstPath , fs , withDefaultWriterOpts (writerOpts ))
799+ checkErr (err )
800+
801+ m = & manifest.TableMetadata {}
802+ if sstMeta .HasPointKeys {
803+ m .ExtendPointKeyBounds (cmp , sstMeta .SmallestPoint , sstMeta .LargestPoint )
804+ }
805+ if sstMeta .HasRangeDelKeys {
806+ m .ExtendPointKeyBounds (cmp , sstMeta .SmallestRangeDel , sstMeta .LargestRangeDel )
807+ }
808+ if sstMeta .HasRangeKeys {
809+ m .ExtendRangeKeyBounds (cmp , sstMeta .SmallestRangeKey , sstMeta .LargestRangeKey )
810+ }
811+ printBounds ("Bounds" , m )
812+
813+ case "excise" :
814+ f , err := fs .Open (sstPath )
815+ checkErr (err )
816+ readable , err := sstable .NewSimpleReadable (f )
817+ checkErr (err )
818+ ctx := context .Background ()
819+ r , err := sstable .NewReader (ctx , readable , sstable.ReaderOptions {})
820+ checkErr (err )
821+ pointIter , err := r .NewPointIter (ctx , sstable.IterOptions {})
822+ checkErr (err )
823+ rangeDelIter , err := r .NewRawRangeDelIter (ctx , sstable .NoFragmentTransforms , block.ReadEnv {})
824+ checkErr (err )
825+ rangeKeyIter , err := r .NewRawRangeKeyIter (ctx , sstable .NoFragmentTransforms , block.ReadEnv {})
826+ checkErr (err )
827+ iters := iterSet {
828+ point : pointIter ,
829+ rangeDeletion : rangeDelIter ,
830+ rangeKey : rangeKeyIter ,
831+ }
832+
833+ exciseSpan := base .ParseUserKeyBounds (td .Input )
834+ if cmp (m .Smallest .UserKey , exciseSpan .Start ) < 0 {
835+ var t manifest.TableMetadata
836+ checkErr (determineLeftTableBounds (cmp , m , & t , exciseSpan .Start , iters ))
837+ printBounds ("Left table bounds" , & t )
838+ }
839+
840+ if ! exciseSpan .End .IsUpperBoundForInternalKey (cmp , m .Largest ) {
841+ var t manifest.TableMetadata
842+ err := func () (err error ) {
843+ // determineRightTableBounds can return assertion errors which we want
844+ // to see in the tests but which panic in invariant builds.
845+ defer func () {
846+ if p := recover (); p != nil {
847+ if e , ok := p .(error ); ok {
848+ err = e
849+ } else {
850+ panic (p )
851+ }
852+ }
853+ }()
854+ return determineRightTableBounds (cmp , m , & t , exciseSpan .End , iters )
855+ }()
856+ if err != nil {
857+ fmt .Fprintf (& buf , "determineRightTableBounds error: %v" , err )
858+ } else {
859+ printBounds ("Right table bounds" , & t )
860+ }
861+ }
862+ checkErr (iters .CloseAll ())
863+
864+ default :
865+ td .Fatalf (t , "unknown command %q" , td .Cmd )
866+ }
867+
868+ return buf .String ()
869+ })
870+ }
0 commit comments