github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/data_test.go (about)

     1  // Copyright 2018 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  
     5  package pebble
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/petermattis/pebble/internal/base"
    14  	"github.com/petermattis/pebble/internal/datadriven"
    15  	"github.com/petermattis/pebble/vfs"
    16  )
    17  
    18  type iterCmdOpt int
    19  
    20  const (
    21  	iterCmdVerboseKey iterCmdOpt = iota
    22  )
    23  
    24  func checkValidPrefix(prefix, key []byte) bool {
    25  	return prefix == nil || bytes.HasPrefix(key, prefix)
    26  }
    27  
    28  func runIterCmd(d *datadriven.TestData, iter *Iterator) string {
    29  	var b bytes.Buffer
    30  	for _, line := range strings.Split(d.Input, "\n") {
    31  		parts := strings.Fields(line)
    32  		if len(parts) == 0 {
    33  			continue
    34  		}
    35  		var valid bool
    36  		switch parts[0] {
    37  		case "seek-ge":
    38  			if len(parts) != 2 {
    39  				return fmt.Sprintf("seek-ge <key>\n")
    40  			}
    41  			valid = iter.SeekGE([]byte(strings.TrimSpace(parts[1])))
    42  		case "seek-prefix-ge":
    43  			if len(parts) != 2 {
    44  				return fmt.Sprintf("seek-prefix-ge <key>\n")
    45  			}
    46  			valid = iter.SeekPrefixGE([]byte(strings.TrimSpace(parts[1])))
    47  		case "seek-lt":
    48  			if len(parts) != 2 {
    49  				return fmt.Sprintf("seek-lt <key>\n")
    50  			}
    51  			valid = iter.SeekLT([]byte(strings.TrimSpace(parts[1])))
    52  		case "first":
    53  			valid = iter.First()
    54  		case "last":
    55  			valid = iter.Last()
    56  		case "next":
    57  			valid = iter.Next()
    58  		case "prev":
    59  			valid = iter.Prev()
    60  		case "set-bounds":
    61  			if len(parts) <= 1 || len(parts) > 3 {
    62  				return fmt.Sprintf("set-bounds lower=<lower> upper=<upper>\n")
    63  			}
    64  			var lower []byte
    65  			var upper []byte
    66  			for _, part := range parts[1:] {
    67  				arg := strings.Split(strings.TrimSpace(part), "=")
    68  				switch arg[0] {
    69  				case "lower":
    70  					lower = []byte(arg[1])
    71  				case "upper":
    72  					upper = []byte(arg[1])
    73  				default:
    74  					return fmt.Sprintf("set-bounds: unknown arg: %s", arg)
    75  				}
    76  			}
    77  			iter.SetBounds(lower, upper)
    78  			valid = iter.Valid()
    79  		default:
    80  			return fmt.Sprintf("unknown op: %s", parts[0])
    81  		}
    82  		if err := iter.Error(); err != nil {
    83  			fmt.Fprintf(&b, "err=%v\n", err)
    84  		} else if valid != iter.Valid() {
    85  			fmt.Fprintf(&b, "mismatched valid states: %t vs %t\n", valid, iter.Valid())
    86  		} else if valid {
    87  			fmt.Fprintf(&b, "%s:%s\n", iter.Key(), iter.Value())
    88  		} else {
    89  			fmt.Fprintf(&b, ".\n")
    90  		}
    91  	}
    92  	return b.String()
    93  }
    94  
    95  func runInternalIterCmd(d *datadriven.TestData, iter internalIterator, opts ...iterCmdOpt) string {
    96  	var verboseKey bool
    97  	for _, opt := range opts {
    98  		if opt == iterCmdVerboseKey {
    99  			verboseKey = true
   100  		}
   101  	}
   102  
   103  	var b bytes.Buffer
   104  	var prefix []byte
   105  	for _, line := range strings.Split(d.Input, "\n") {
   106  		parts := strings.Fields(line)
   107  		if len(parts) == 0 {
   108  			continue
   109  		}
   110  		switch parts[0] {
   111  		case "seek-ge":
   112  			if len(parts) != 2 {
   113  				return fmt.Sprintf("seek-ge <key>\n")
   114  			}
   115  			prefix = nil
   116  			iter.SeekGE([]byte(strings.TrimSpace(parts[1])))
   117  		case "seek-prefix-ge":
   118  			if len(parts) != 2 {
   119  				return fmt.Sprintf("seek-prefix-ge <key>\n")
   120  			}
   121  			prefix = []byte(strings.TrimSpace(parts[1]))
   122  			iter.SeekPrefixGE(prefix, prefix /* key */)
   123  		case "seek-lt":
   124  			if len(parts) != 2 {
   125  				return fmt.Sprintf("seek-lt <key>\n")
   126  			}
   127  			prefix = nil
   128  			iter.SeekLT([]byte(strings.TrimSpace(parts[1])))
   129  		case "first":
   130  			prefix = nil
   131  			iter.First()
   132  		case "last":
   133  			prefix = nil
   134  			iter.Last()
   135  		case "next":
   136  			iter.Next()
   137  		case "prev":
   138  			iter.Prev()
   139  		case "set-bounds":
   140  			if len(parts) <= 1 || len(parts) > 3 {
   141  				return fmt.Sprintf("set-bounds lower=<lower> upper=<upper>\n")
   142  			}
   143  			var lower []byte
   144  			var upper []byte
   145  			for _, part := range parts[1:] {
   146  				arg := strings.Split(strings.TrimSpace(part), "=")
   147  				switch arg[0] {
   148  				case "lower":
   149  					lower = []byte(arg[1])
   150  				case "upper":
   151  					upper = []byte(arg[1])
   152  				default:
   153  					return fmt.Sprintf("set-bounds: unknown arg: %s", arg)
   154  				}
   155  			}
   156  			iter.SetBounds(lower, upper)
   157  			continue
   158  		default:
   159  			return fmt.Sprintf("unknown op: %s", parts[0])
   160  		}
   161  		if iter.Valid() && checkValidPrefix(prefix, iter.Key().UserKey) {
   162  			if verboseKey {
   163  				fmt.Fprintf(&b, "%s:%s\n", iter.Key(), iter.Value())
   164  			} else {
   165  				fmt.Fprintf(&b, "%s:%s\n", iter.Key().UserKey, iter.Value())
   166  			}
   167  		} else if err := iter.Error(); err != nil {
   168  			fmt.Fprintf(&b, "err=%v\n", err)
   169  		} else {
   170  			fmt.Fprintf(&b, ".\n")
   171  		}
   172  	}
   173  	return b.String()
   174  }
   175  
   176  func runBatchDefineCmd(d *datadriven.TestData, b *Batch) error {
   177  	for _, line := range strings.Split(d.Input, "\n") {
   178  		parts := strings.Fields(line)
   179  		if len(parts) == 0 {
   180  			continue
   181  		}
   182  		if parts[1] == `<nil>` {
   183  			parts[1] = ""
   184  		}
   185  		var err error
   186  		switch parts[0] {
   187  		case "set":
   188  			if len(parts) != 3 {
   189  				return fmt.Errorf("%s expects 2 arguments", parts[0])
   190  			}
   191  			err = b.Set([]byte(parts[1]), []byte(parts[2]), nil)
   192  		case "del":
   193  			if len(parts) != 2 {
   194  				return fmt.Errorf("%s expects 1 argument", parts[0])
   195  			}
   196  			err = b.Delete([]byte(parts[1]), nil)
   197  		case "del-range":
   198  			if len(parts) != 3 {
   199  				return fmt.Errorf("%s expects 2 arguments", parts[0])
   200  			}
   201  			err = b.DeleteRange([]byte(parts[1]), []byte(parts[2]), nil)
   202  		case "merge":
   203  			if len(parts) != 3 {
   204  				return fmt.Errorf("%s expects 2 arguments", parts[0])
   205  			}
   206  			err = b.Merge([]byte(parts[1]), []byte(parts[2]), nil)
   207  		default:
   208  			return fmt.Errorf("unknown op: %s", parts[0])
   209  		}
   210  		if err != nil {
   211  			return err
   212  		}
   213  	}
   214  	return nil
   215  }
   216  
   217  func runCompactCommand(td *datadriven.TestData, d *DB) error {
   218  	if len(td.CmdArgs) > 2 {
   219  		return fmt.Errorf("%s expects at most two arguments", td.Cmd)
   220  	}
   221  	parts := strings.Split(td.CmdArgs[0].Key, "-")
   222  	if len(parts) != 2 {
   223  		return fmt.Errorf("expected <begin>-<end>: %s", td.Input)
   224  	}
   225  	if len(td.CmdArgs) == 2 {
   226  		levelString := td.CmdArgs[1].String()
   227  		iStart := base.MakeInternalKey([]byte(parts[0]), InternalKeySeqNumMax, InternalKeyKindMax)
   228  		iEnd := base.MakeInternalKey([]byte(parts[1]), 0, 0)
   229  		if levelString[0] != 'L' {
   230  			return fmt.Errorf("expected L<n>: %s", levelString)
   231  		}
   232  		level, err := strconv.Atoi(levelString[1:])
   233  		if err != nil {
   234  			return err
   235  		}
   236  		return d.manualCompact(&manualCompaction{
   237  			done:  make(chan error, 1),
   238  			level: level,
   239  			start: iStart,
   240  			end:   iEnd,
   241  		})
   242  	}
   243  	return d.Compact([]byte(parts[0]), []byte(parts[1]))
   244  }
   245  
   246  func runDBDefineCmd(td *datadriven.TestData, opts *Options) (*DB, error) {
   247  	if td.Input == "" {
   248  		return nil, fmt.Errorf("empty test input")
   249  	}
   250  
   251  	opts = opts.EnsureDefaults()
   252  	opts.FS = vfs.NewMem()
   253  
   254  	var snapshots []uint64
   255  	for _, arg := range td.CmdArgs {
   256  		switch arg.Key {
   257  		case "target-file-sizes":
   258  			opts.Levels = make([]LevelOptions, len(arg.Vals))
   259  			for i := range arg.Vals {
   260  				size, err := strconv.ParseInt(arg.Vals[i], 10, 64)
   261  				if err != nil {
   262  					return nil, err
   263  				}
   264  				opts.Levels[i].TargetFileSize = size
   265  			}
   266  		case "snapshots":
   267  			snapshots = make([]uint64, len(arg.Vals))
   268  			for i := range arg.Vals {
   269  				seqNum, err := strconv.ParseUint(arg.Vals[i], 10, 64)
   270  				if err != nil {
   271  					return nil, err
   272  				}
   273  				snapshots[i] = seqNum
   274  				if i > 0 && snapshots[i] < snapshots[i-1] {
   275  					return nil, fmt.Errorf("Snapshots must be in ascending order")
   276  				}
   277  			}
   278  		default:
   279  			return nil, fmt.Errorf("%s: unknown arg: %s", td.Cmd, arg.Key)
   280  		}
   281  	}
   282  	d, err := Open("", opts)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	d.mu.Lock()
   287  	d.mu.versions.dynamicBaseLevel = false
   288  	for i := range snapshots {
   289  		s := &Snapshot{db: d}
   290  		s.seqNum = snapshots[i]
   291  		d.mu.snapshots.pushBack(s)
   292  	}
   293  	defer d.mu.Unlock()
   294  
   295  	var mem *memTable
   296  	ve := &versionEdit{}
   297  	level := -1
   298  
   299  	maybeFlush := func() error {
   300  		if level < 0 {
   301  			return nil
   302  		}
   303  
   304  		c := newFlush(d.opts, d.mu.versions.currentVersion(),
   305  			d.mu.versions.picker.baseLevel, []flushable{mem}, &d.bytesFlushed)
   306  		c.disableRangeTombstoneElision = true
   307  		newVE, _, err := d.runCompaction(0, c, nilPacer)
   308  		if err != nil {
   309  			return nil
   310  		}
   311  		for _, f := range newVE.NewFiles {
   312  			ve.NewFiles = append(ve.NewFiles, newFileEntry{
   313  				Level: level,
   314  				Meta:  f.Meta,
   315  			})
   316  		}
   317  		level = -1
   318  		return nil
   319  	}
   320  
   321  	for _, line := range strings.Split(td.Input, "\n") {
   322  		fields := strings.Fields(line)
   323  		if len(fields) > 0 {
   324  			switch fields[0] {
   325  			case "mem":
   326  				if err := maybeFlush(); err != nil {
   327  					return nil, err
   328  				}
   329  				// Add a memtable layer.
   330  				if !d.mu.mem.mutable.empty() {
   331  					d.mu.mem.mutable = newMemTable(d.opts)
   332  					d.mu.mem.queue = append(d.mu.mem.queue, d.mu.mem.mutable)
   333  					d.updateReadStateLocked()
   334  				}
   335  				mem = d.mu.mem.mutable
   336  				fields = fields[1:]
   337  			case "L0", "L1", "L2", "L3", "L4", "L5", "L6":
   338  				if err := maybeFlush(); err != nil {
   339  					return nil, err
   340  				}
   341  				var err error
   342  				if level, err = strconv.Atoi(fields[0][1:]); err != nil {
   343  					return nil, err
   344  				}
   345  				fields = fields[1:]
   346  				mem = newMemTable(d.opts)
   347  			}
   348  		}
   349  
   350  		for _, data := range fields {
   351  			i := strings.Index(data, ":")
   352  			key := base.ParseInternalKey(data[:i])
   353  			value := []byte(data[i+1:])
   354  			if err := mem.set(key, value); err != nil {
   355  				return nil, err
   356  			}
   357  		}
   358  	}
   359  
   360  	if err := maybeFlush(); err != nil {
   361  		return nil, err
   362  	}
   363  
   364  	if len(ve.NewFiles) > 0 {
   365  		jobID := d.mu.nextJobID
   366  		d.mu.nextJobID++
   367  		if err := d.mu.versions.logAndApply(jobID, ve, nil, d.dataDir); err != nil {
   368  			return nil, err
   369  		}
   370  		d.updateReadStateLocked()
   371  		for i := range ve.NewFiles {
   372  			meta := &ve.NewFiles[i].Meta
   373  			delete(d.mu.compact.pendingOutputs, meta.FileNum)
   374  		}
   375  	}
   376  
   377  	return d, nil
   378  }