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  }