github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/iterator_histories_test.go (about) 1 // Copyright 2022 The LevelDB-Go and Pebble 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 package pebble 5 6 import ( 7 "bytes" 8 "fmt" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "testing" 13 14 "github.com/cockroachdb/datadriven" 15 "github.com/cockroachdb/errors" 16 "github.com/cockroachdb/pebble/internal/invariants" 17 "github.com/cockroachdb/pebble/internal/testkeys" 18 "github.com/cockroachdb/pebble/sstable" 19 "github.com/cockroachdb/pebble/vfs" 20 "github.com/stretchr/testify/require" 21 ) 22 23 // TODO(jackson): Add a range keys test with concurrency: the logic to cache 24 // fragmented spans is susceptible to races. 25 26 func TestIterHistories(t *testing.T) { 27 datadriven.Walk(t, "testdata/iter_histories", func(t *testing.T, path string) { 28 filename := filepath.Base(path) 29 switch { 30 case invariants.Enabled && strings.Contains(filename, "no_invariants"): 31 t.Skip("disabled when run with -tags invariants due to nondeterminism") 32 } 33 34 var d *DB 35 var buf bytes.Buffer 36 iters := map[string]*Iterator{} 37 batches := map[string]*Batch{} 38 newIter := func(name string, reader Reader, o *IterOptions) *Iterator { 39 it, _ := reader.NewIter(o) 40 iters[name] = it 41 return it 42 } 43 var opts *Options 44 parseOpts := func(td *datadriven.TestData) (*Options, error) { 45 opts = &Options{ 46 FS: vfs.NewMem(), 47 Comparer: testkeys.Comparer, 48 FormatMajorVersion: FormatRangeKeys, 49 BlockPropertyCollectors: []func() BlockPropertyCollector{ 50 sstable.NewTestKeysBlockPropertyCollector, 51 }, 52 } 53 54 opts.DisableAutomaticCompactions = true 55 opts.EnsureDefaults() 56 opts.WithFSDefaults() 57 if err := parseDBOptionsArgs(opts, td.CmdArgs); err != nil { 58 return nil, err 59 } 60 return opts, nil 61 } 62 cleanup := func() (err error) { 63 for key, batch := range batches { 64 err = firstError(err, batch.Close()) 65 delete(batches, key) 66 } 67 for key, iter := range iters { 68 err = firstError(err, iter.Close()) 69 delete(iters, key) 70 } 71 if d != nil { 72 err = firstError(err, d.Close()) 73 d = nil 74 } 75 return err 76 } 77 defer cleanup() 78 79 datadriven.RunTest(t, path, func(t *testing.T, td *datadriven.TestData) string { 80 buf.Reset() 81 switch td.Cmd { 82 case "define": 83 var err error 84 if err := cleanup(); err != nil { 85 return err.Error() 86 } 87 opts, err = parseOpts(td) 88 if err != nil { 89 return err.Error() 90 } 91 d, err = runDBDefineCmd(td, opts) 92 if err != nil { 93 return err.Error() 94 } 95 return runLSMCmd(td, d) 96 case "reopen": 97 var err error 98 if err := cleanup(); err != nil { 99 return err.Error() 100 } 101 if err := parseDBOptionsArgs(opts, td.CmdArgs); err != nil { 102 return err.Error() 103 } 104 d, err = Open("", opts) 105 require.NoError(t, err) 106 return "" 107 case "reset": 108 var err error 109 if err := cleanup(); err != nil { 110 return err.Error() 111 } 112 opts, err = parseOpts(td) 113 if err != nil { 114 return err.Error() 115 } 116 117 d, err = Open("", opts) 118 require.NoError(t, err) 119 return "" 120 case "populate": 121 b := d.NewBatch() 122 runPopulateCmd(t, td, b) 123 count := b.Count() 124 require.NoError(t, b.Commit(nil)) 125 return fmt.Sprintf("wrote %d keys\n", count) 126 case "batch": 127 var name string 128 td.MaybeScanArgs(t, "name", &name) 129 commit := td.HasArg("commit") 130 b := d.NewIndexedBatch() 131 require.NoError(t, runBatchDefineCmd(td, b)) 132 var err error 133 if commit { 134 func() { 135 defer func() { 136 if r := recover(); r != nil { 137 err = errors.New(r.(string)) 138 } 139 }() 140 err = b.Commit(nil) 141 }() 142 } else if name != "" { 143 batches[name] = b 144 } 145 if err != nil { 146 return err.Error() 147 } 148 count := b.Count() 149 if commit { 150 return fmt.Sprintf("committed %d keys\n", count) 151 } 152 return fmt.Sprintf("wrote %d keys to batch %q\n", count, name) 153 case "compact": 154 if err := runCompactCmd(td, d); err != nil { 155 return err.Error() 156 } 157 return runLSMCmd(td, d) 158 case "flush": 159 err := d.Flush() 160 if err != nil { 161 return err.Error() 162 } 163 return "" 164 case "get": 165 var reader Reader = d 166 if arg, ok := td.Arg("reader"); ok { 167 if reader, ok = batches[arg.Vals[0]]; !ok { 168 return fmt.Sprintf("unknown reader %q", arg.Vals[0]) 169 } 170 } 171 for _, l := range strings.Split(td.Input, "\n") { 172 v, closer, err := reader.Get([]byte(l)) 173 if err != nil { 174 fmt.Fprintf(&buf, "%s: error: %s\n", l, err) 175 } else { 176 fmt.Fprintf(&buf, "%s: %s\n", l, v) 177 } 178 if err := closer.Close(); err != nil { 179 fmt.Fprintf(&buf, "close err: %s\n", err) 180 } 181 } 182 return buf.String() 183 case "ingest": 184 if err := runBuildCmd(td, d, d.opts.FS); err != nil { 185 return err.Error() 186 } 187 if err := runIngestCmd(td, d, d.opts.FS); err != nil { 188 return err.Error() 189 } 190 return "" 191 case "layout": 192 var verbose bool 193 var filename string 194 td.ScanArgs(t, "filename", &filename) 195 td.MaybeScanArgs(t, "verbose", &verbose) 196 f, err := opts.FS.Open(filename) 197 if err != nil { 198 return err.Error() 199 } 200 readable, err := sstable.NewSimpleReadable(f) 201 if err != nil { 202 return err.Error() 203 } 204 r, err := sstable.NewReader(readable, opts.MakeReaderOptions()) 205 if err != nil { 206 return err.Error() 207 } 208 defer r.Close() 209 l, err := r.Layout() 210 if err != nil { 211 return err.Error() 212 } 213 l.Describe(&buf, verbose, r, nil) 214 return buf.String() 215 case "lsm": 216 return runLSMCmd(td, d) 217 case "metrics": 218 d.mu.Lock() 219 d.waitTableStats() 220 d.mu.Unlock() 221 m := d.Metrics() 222 return fmt.Sprintf("Metrics.Keys.RangeKeySetsCount = %d\n", m.Keys.RangeKeySetsCount) 223 case "mutate": 224 var batchName string 225 td.ScanArgs(t, "batch", &batchName) 226 mut := newBatch(d) 227 if err := runBatchDefineCmd(td, mut); err != nil { 228 return err.Error() 229 } 230 if err := batches[batchName].Apply(mut, nil); err != nil { 231 return err.Error() 232 } 233 return "" 234 case "clone": 235 var from, to string 236 var cloneOpts CloneOptions 237 var iterOpts IterOptions 238 td.ScanArgs(t, "from", &from) 239 td.ScanArgs(t, "to", &to) 240 td.ScanArgs(t, "refresh-batch", &cloneOpts.RefreshBatchView) 241 fromIter := iters[from] 242 if foundAny, err := parseIterOptions(&iterOpts, &fromIter.opts, strings.Fields(td.Input)); err != nil { 243 return fmt.Sprintf("clone: %s", err.Error()) 244 } else if foundAny { 245 cloneOpts.IterOptions = &iterOpts 246 } 247 var err error 248 iters[to], err = fromIter.Clone(cloneOpts) 249 if err != nil { 250 return err.Error() 251 } 252 return "" 253 case "commit": 254 name := pluckStringCmdArg(td, "batch") 255 b := batches[name] 256 defer b.Close() 257 count := b.Count() 258 require.NoError(t, d.Apply(b, nil)) 259 delete(batches, name) 260 return fmt.Sprintf("committed %d keys\n", count) 261 case "combined-iter": 262 o := &IterOptions{KeyTypes: IterKeyTypePointsAndRanges} 263 var reader Reader = d 264 var name string 265 for _, arg := range td.CmdArgs { 266 switch arg.Key { 267 case "mask-suffix": 268 o.RangeKeyMasking.Suffix = []byte(arg.Vals[0]) 269 case "mask-filter": 270 o.RangeKeyMasking.Filter = func() BlockPropertyFilterMask { 271 return sstable.NewTestKeysMaskingFilter() 272 } 273 case "lower": 274 o.LowerBound = []byte(arg.Vals[0]) 275 case "upper": 276 o.UpperBound = []byte(arg.Vals[0]) 277 case "name": 278 name = arg.Vals[0] 279 case "reader": 280 reader = batches[arg.Vals[0]] 281 if reader == nil { 282 return fmt.Sprintf("unknown reader %q", arg.Vals[0]) 283 } 284 case "point-key-filter": 285 if len(arg.Vals) != 2 { 286 return fmt.Sprintf("blockprop-filter expects 2 arguments, received %d", len(arg.Vals)) 287 } 288 min, err := strconv.ParseUint(arg.Vals[0], 10, 64) 289 if err != nil { 290 return err.Error() 291 } 292 max, err := strconv.ParseUint(arg.Vals[1], 10, 64) 293 if err != nil { 294 return err.Error() 295 } 296 o.PointKeyFilters = []sstable.BlockPropertyFilter{ 297 sstable.NewTestKeysBlockPropertyFilter(min, max), 298 } 299 o.SkipPoint = func(k []byte) bool { 300 i := testkeys.Comparer.Split(k) 301 if i == len(k) { 302 return false 303 } 304 v, err := testkeys.ParseSuffix(k[i:]) 305 if err != nil { 306 return false 307 } 308 return uint64(v) < min || uint64(v) >= max 309 } 310 case "snapshot": 311 s, err := strconv.ParseUint(arg.Vals[0], 10, 64) 312 if err != nil { 313 return err.Error() 314 } 315 func() { 316 d.mu.Lock() 317 defer d.mu.Unlock() 318 l := &d.mu.snapshots 319 for i := l.root.next; i != &l.root; i = i.next { 320 if i.seqNum == s { 321 reader = i 322 break 323 } 324 } 325 }() 326 case "use-l6-filter": 327 o.UseL6Filters = true 328 } 329 } 330 var iter *Iterator 331 var err error 332 func() { 333 defer func() { 334 if r := recover(); r != nil { 335 switch v := r.(type) { 336 case string: 337 err = errors.New(v) 338 case error: 339 err = v 340 default: 341 panic(r) 342 } 343 } 344 }() 345 iter = newIter(name, reader, o) 346 }() 347 if err != nil { 348 return err.Error() 349 } 350 return runIterCmd(td, iter, name == "" /* close iter */) 351 case "rangekey-iter": 352 name := pluckStringCmdArg(td, "name") 353 iter := newIter(name, d, &IterOptions{KeyTypes: IterKeyTypeRangesOnly}) 354 return runIterCmd(td, iter, name == "" /* close iter */) 355 case "scan-rangekeys": 356 iter := newIter( 357 pluckStringCmdArg(td, "name"), 358 d, 359 &IterOptions{KeyTypes: IterKeyTypeRangesOnly}, 360 ) 361 func() { 362 defer iter.Close() 363 for iter.First(); iter.Valid(); iter.Next() { 364 start, end := iter.RangeBounds() 365 fmt.Fprintf(&buf, "[%s, %s)\n", start, end) 366 writeRangeKeys(&buf, iter) 367 fmt.Fprintln(&buf) 368 } 369 }() 370 return buf.String() 371 case "iter": 372 var name string 373 td.ScanArgs(t, "iter", &name) 374 return runIterCmd(td, iters[name], false /* close iter */) 375 case "wait-table-stats": 376 d.mu.Lock() 377 d.waitTableStats() 378 d.mu.Unlock() 379 return "" 380 default: 381 return fmt.Sprintf("unknown command %q", td.Cmd) 382 } 383 }) 384 }) 385 } 386 387 func pluckStringCmdArg(td *datadriven.TestData, key string) string { 388 if arg, ok := td.Arg(key); ok { 389 return arg.Vals[0] 390 } 391 return "" 392 }