github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/table_stats_test.go (about) 1 // Copyright 2020 The LevelDB-Go and Pebble and Bitalostored Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package bitalostable 6 7 import ( 8 "bytes" 9 "fmt" 10 "strings" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 "github.com/zuoyebang/bitalostable/internal/base" 15 "github.com/zuoyebang/bitalostable/internal/datadriven" 16 "github.com/zuoyebang/bitalostable/internal/keyspan" 17 "github.com/zuoyebang/bitalostable/internal/rangekey" 18 "github.com/zuoyebang/bitalostable/internal/testkeys" 19 "github.com/zuoyebang/bitalostable/sstable" 20 "github.com/zuoyebang/bitalostable/vfs" 21 ) 22 23 func TestTableStats(t *testing.T) { 24 fs := vfs.NewMem() 25 // loadedInfo is protected by d.mu. 26 var loadedInfo *TableStatsInfo 27 opts := &Options{ 28 FS: fs, 29 EventListener: EventListener{ 30 TableStatsLoaded: func(info TableStatsInfo) { 31 loadedInfo = &info 32 }, 33 }, 34 } 35 opts.DisableAutomaticCompactions = true 36 opts.Comparer = testkeys.Comparer 37 opts.FormatMajorVersion = FormatRangeKeys 38 39 d, err := Open("", opts) 40 require.NoError(t, err) 41 defer func() { 42 if d != nil { 43 require.NoError(t, d.Close()) 44 } 45 }() 46 47 datadriven.RunTest(t, "testdata/table_stats", func(td *datadriven.TestData) string { 48 switch td.Cmd { 49 case "disable": 50 d.mu.Lock() 51 d.opts.private.disableTableStats = true 52 d.mu.Unlock() 53 return "" 54 55 case "enable": 56 d.mu.Lock() 57 d.opts.private.disableTableStats = false 58 d.maybeCollectTableStatsLocked() 59 d.mu.Unlock() 60 return "" 61 62 case "define": 63 require.NoError(t, d.Close()) 64 loadedInfo = nil 65 66 d, err = runDBDefineCmd(td, opts) 67 if err != nil { 68 return err.Error() 69 } 70 d.mu.Lock() 71 s := d.mu.versions.currentVersion().String() 72 d.mu.Unlock() 73 return s 74 75 case "reopen": 76 require.NoError(t, d.Close()) 77 loadedInfo = nil 78 79 // Open using existing file system. 80 d, err = Open("", opts) 81 require.NoError(t, err) 82 return "" 83 84 case "batch": 85 b := d.NewBatch() 86 if err := runBatchDefineCmd(td, b); err != nil { 87 return err.Error() 88 } 89 b.Commit(nil) 90 return "" 91 92 case "flush": 93 if err := d.Flush(); err != nil { 94 return err.Error() 95 } 96 97 d.mu.Lock() 98 s := d.mu.versions.currentVersion().String() 99 d.mu.Unlock() 100 return s 101 102 case "ingest": 103 if err = runBuildCmd(td, d, d.opts.FS); err != nil { 104 return err.Error() 105 } 106 if err = runIngestCmd(td, d, d.opts.FS); err != nil { 107 return err.Error() 108 } 109 d.mu.Lock() 110 s := d.mu.versions.currentVersion().String() 111 d.mu.Unlock() 112 return s 113 114 case "wait-pending-table-stats": 115 return runTableStatsCmd(td, d) 116 117 case "wait-loaded-initial": 118 d.mu.Lock() 119 for d.mu.tableStats.loading || !d.mu.tableStats.loadedInitial { 120 d.mu.tableStats.cond.Wait() 121 } 122 s := loadedInfo.String() 123 d.mu.Unlock() 124 return s 125 126 case "compact": 127 if err := runCompactCmd(td, d); err != nil { 128 return err.Error() 129 } 130 d.mu.Lock() 131 // Disable the "dynamic base level" code for this test. 132 d.mu.versions.picker.forceBaseLevel1() 133 s := d.mu.versions.currentVersion().String() 134 d.mu.Unlock() 135 return s 136 137 default: 138 return fmt.Sprintf("unknown command: %s", td.Cmd) 139 } 140 }) 141 } 142 143 func TestTableRangeDeletionIter(t *testing.T) { 144 var m *fileMetadata 145 cmp := base.DefaultComparer.Compare 146 fs := vfs.NewMem() 147 datadriven.RunTest(t, "testdata/table_stats_deletion_iter", func(td *datadriven.TestData) string { 148 switch cmd := td.Cmd; cmd { 149 case "build": 150 f, err := fs.Create("tmp.sst") 151 if err != nil { 152 return err.Error() 153 } 154 w := sstable.NewWriter(f, sstable.WriterOptions{ 155 TableFormat: sstable.TableFormatMax, 156 }) 157 m = &fileMetadata{} 158 for _, line := range strings.Split(td.Input, "\n") { 159 s := keyspan.ParseSpan(line) 160 // Range dels can be written sequentially. Range keys must be collected. 161 rKeySpan := &keyspan.Span{Start: s.Start, End: s.End} 162 for _, k := range s.Keys { 163 if rangekey.IsRangeKey(k.Kind()) { 164 rKeySpan.Keys = append(rKeySpan.Keys, k) 165 } else { 166 k := base.InternalKey{UserKey: s.Start, Trailer: k.Trailer} 167 if err = w.Add(k, s.End); err != nil { 168 return err.Error() 169 } 170 } 171 } 172 err = rangekey.Encode(rKeySpan, func(k base.InternalKey, v []byte) error { 173 return w.AddRangeKey(k, v) 174 }) 175 if err != nil { 176 return err.Error() 177 } 178 } 179 if err = w.Close(); err != nil { 180 return err.Error() 181 } 182 meta, err := w.Metadata() 183 if err != nil { 184 return err.Error() 185 } 186 if meta.HasPointKeys { 187 m.ExtendPointKeyBounds(cmp, meta.SmallestPoint, meta.LargestPoint) 188 } 189 if meta.HasRangeDelKeys { 190 m.ExtendPointKeyBounds(cmp, meta.SmallestRangeDel, meta.LargestRangeDel) 191 } 192 if meta.HasRangeKeys { 193 m.ExtendRangeKeyBounds(cmp, meta.SmallestRangeKey, meta.LargestRangeKey) 194 } 195 return m.DebugString(base.DefaultFormatter, false /* verbose */) 196 case "spans": 197 f, err := fs.Open("tmp.sst") 198 if err != nil { 199 return err.Error() 200 } 201 var r *sstable.Reader 202 r, err = sstable.NewReader(f, sstable.ReaderOptions{}) 203 if err != nil { 204 return err.Error() 205 } 206 defer r.Close() 207 iter, err := newCombinedDeletionKeyspanIter(base.DefaultComparer, r, m) 208 if err != nil { 209 return err.Error() 210 } 211 defer iter.Close() 212 var buf bytes.Buffer 213 for s := iter.First(); s != nil; s = iter.Next() { 214 buf.WriteString(s.String() + "\n") 215 } 216 if buf.Len() == 0 { 217 return "(none)" 218 } 219 return buf.String() 220 default: 221 return fmt.Sprintf("unknown command: %s", cmd) 222 } 223 }) 224 }