github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/snapshot_test.go (about)

     1  // Copyright 2012 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  	"math/rand"
    11  	"reflect"
    12  	"runtime"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/cockroachdb/datadriven"
    19  	"github.com/cockroachdb/errors"
    20  	"github.com/cockroachdb/pebble/vfs"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestSnapshotListToSlice(t *testing.T) {
    25  	testCases := []struct {
    26  		vals []uint64
    27  	}{
    28  		{nil},
    29  		{[]uint64{1}},
    30  		{[]uint64{1, 2, 3}},
    31  		{[]uint64{3, 2, 1}},
    32  	}
    33  	for _, c := range testCases {
    34  		t.Run("", func(t *testing.T) {
    35  			var l snapshotList
    36  			l.init()
    37  			for _, v := range c.vals {
    38  				l.pushBack(&Snapshot{seqNum: v})
    39  			}
    40  			slice := l.toSlice()
    41  			if !reflect.DeepEqual(c.vals, slice) {
    42  				t.Fatalf("expected %d, but got %d", c.vals, slice)
    43  			}
    44  		})
    45  	}
    46  }
    47  
    48  func testSnapshotImpl(t *testing.T, newSnapshot func(d *DB) Reader) {
    49  	var d *DB
    50  	var snapshots map[string]Reader
    51  
    52  	close := func() {
    53  		for _, s := range snapshots {
    54  			require.NoError(t, s.Close())
    55  		}
    56  		snapshots = nil
    57  		if d != nil {
    58  			require.NoError(t, d.Close())
    59  			d = nil
    60  		}
    61  	}
    62  	defer close()
    63  
    64  	randVersion := func() FormatMajorVersion {
    65  		minVersion := formatUnusedPrePebblev1MarkedCompacted
    66  		return FormatMajorVersion(int(minVersion) + rand.Intn(
    67  			int(internalFormatNewest)-int(minVersion)+1))
    68  	}
    69  	datadriven.RunTest(t, "testdata/snapshot", func(t *testing.T, td *datadriven.TestData) string {
    70  		switch td.Cmd {
    71  		case "define":
    72  			close()
    73  
    74  			var err error
    75  			options := &Options{
    76  				FS:                 vfs.NewMem(),
    77  				FormatMajorVersion: randVersion(),
    78  			}
    79  			if td.HasArg("block-size") {
    80  				var blockSize int
    81  				td.ScanArgs(t, "block-size", &blockSize)
    82  				options.Levels = make([]LevelOptions, 1)
    83  				options.Levels[0].BlockSize = blockSize
    84  				options.Levels[0].IndexBlockSize = blockSize
    85  			}
    86  			d, err = Open("", options)
    87  			if err != nil {
    88  				return err.Error()
    89  			}
    90  			snapshots = make(map[string]Reader)
    91  
    92  			for _, line := range strings.Split(td.Input, "\n") {
    93  				parts := strings.Fields(line)
    94  				if len(parts) == 0 {
    95  					continue
    96  				}
    97  				var err error
    98  				switch parts[0] {
    99  				case "set":
   100  					if len(parts) != 3 {
   101  						return fmt.Sprintf("%s expects 2 arguments", parts[0])
   102  					}
   103  					err = d.Set([]byte(parts[1]), []byte(parts[2]), nil)
   104  				case "del":
   105  					if len(parts) != 2 {
   106  						return fmt.Sprintf("%s expects 1 argument", parts[0])
   107  					}
   108  					err = d.Delete([]byte(parts[1]), nil)
   109  				case "merge":
   110  					if len(parts) != 3 {
   111  						return fmt.Sprintf("%s expects 2 arguments", parts[0])
   112  					}
   113  					err = d.Merge([]byte(parts[1]), []byte(parts[2]), nil)
   114  				case "snapshot":
   115  					if len(parts) != 2 {
   116  						return fmt.Sprintf("%s expects 1 argument", parts[0])
   117  					}
   118  					snapshots[parts[1]] = newSnapshot(d)
   119  				case "compact":
   120  					if len(parts) != 2 {
   121  						return fmt.Sprintf("%s expects 1 argument", parts[0])
   122  					}
   123  					keys := strings.Split(parts[1], "-")
   124  					if len(keys) != 2 {
   125  						return fmt.Sprintf("malformed key range: %s", parts[1])
   126  					}
   127  					err = d.Compact([]byte(keys[0]), []byte(keys[1]), false)
   128  				default:
   129  					return fmt.Sprintf("unknown op: %s", parts[0])
   130  				}
   131  				if err != nil {
   132  					return err.Error()
   133  				}
   134  			}
   135  			return ""
   136  
   137  		case "db-state":
   138  			d.mu.Lock()
   139  			s := d.mu.versions.currentVersion().String()
   140  			d.mu.Unlock()
   141  			return s
   142  
   143  		case "iter":
   144  			var iter *Iterator
   145  			if len(td.CmdArgs) == 1 {
   146  				if td.CmdArgs[0].Key != "snapshot" {
   147  					return fmt.Sprintf("unknown argument: %s", td.CmdArgs[0])
   148  				}
   149  				if len(td.CmdArgs[0].Vals) != 1 {
   150  					return fmt.Sprintf("%s expects 1 value: %s", td.CmdArgs[0].Key, td.CmdArgs[0])
   151  				}
   152  				name := td.CmdArgs[0].Vals[0]
   153  				snapshot := snapshots[name]
   154  				if snapshot == nil {
   155  					return fmt.Sprintf("unable to find snapshot \"%s\"", name)
   156  				}
   157  				iter, _ = snapshot.NewIter(nil)
   158  			} else {
   159  				iter, _ = d.NewIter(nil)
   160  			}
   161  			defer iter.Close()
   162  
   163  			var b bytes.Buffer
   164  			for _, line := range strings.Split(td.Input, "\n") {
   165  				parts := strings.Fields(line)
   166  				if len(parts) == 0 {
   167  					continue
   168  				}
   169  				switch parts[0] {
   170  				case "first":
   171  					iter.First()
   172  				case "last":
   173  					iter.Last()
   174  				case "seek-ge":
   175  					if len(parts) != 2 {
   176  						return "seek-ge <key>\n"
   177  					}
   178  					iter.SeekGE([]byte(strings.TrimSpace(parts[1])))
   179  				case "seek-lt":
   180  					if len(parts) != 2 {
   181  						return "seek-lt <key>\n"
   182  					}
   183  					iter.SeekLT([]byte(strings.TrimSpace(parts[1])))
   184  				case "next":
   185  					iter.Next()
   186  				case "prev":
   187  					iter.Prev()
   188  				default:
   189  					return fmt.Sprintf("unknown op: %s", parts[0])
   190  				}
   191  				if iter.Valid() {
   192  					fmt.Fprintf(&b, "%s:%s\n", iter.Key(), iter.Value())
   193  				} else if err := iter.Error(); err != nil {
   194  					fmt.Fprintf(&b, "err=%v\n", err)
   195  				} else {
   196  					fmt.Fprintf(&b, ".\n")
   197  				}
   198  			}
   199  			return b.String()
   200  
   201  		default:
   202  			return fmt.Sprintf("unknown command: %s", td.Cmd)
   203  		}
   204  	})
   205  }
   206  
   207  func TestSnapshot(t *testing.T) {
   208  	testSnapshotImpl(t, func(d *DB) Reader {
   209  		return d.NewSnapshot()
   210  	})
   211  }
   212  
   213  func TestEventuallyFileOnlySnapshot(t *testing.T) {
   214  	testSnapshotImpl(t, func(d *DB) Reader {
   215  		// NB: all keys in testdata/snapshot fall within the ASCII keyrange a-z.
   216  		return d.NewEventuallyFileOnlySnapshot([]KeyRange{{Start: []byte("a"), End: []byte("z")}})
   217  	})
   218  }
   219  
   220  func TestSnapshotClosed(t *testing.T) {
   221  	d, err := Open("", &Options{
   222  		FS: vfs.NewMem(),
   223  	})
   224  	require.NoError(t, err)
   225  
   226  	catch := func(f func()) (err error) {
   227  		defer func() {
   228  			if r := recover(); r != nil {
   229  				err = r.(error)
   230  			}
   231  		}()
   232  		f()
   233  		return nil
   234  	}
   235  
   236  	snap := d.NewSnapshot()
   237  	require.NoError(t, snap.Close())
   238  	require.True(t, errors.Is(catch(func() { _ = snap.Close() }), ErrClosed))
   239  	require.True(t, errors.Is(catch(func() { _, _, _ = snap.Get(nil) }), ErrClosed))
   240  	require.True(t, errors.Is(catch(func() { snap.NewIter(nil) }), ErrClosed))
   241  
   242  	require.NoError(t, d.Close())
   243  }
   244  
   245  func TestSnapshotRangeDeletionStress(t *testing.T) {
   246  	const runs = 200
   247  	const middleKey = runs * runs
   248  
   249  	d, err := Open("", &Options{
   250  		FS: vfs.NewMem(),
   251  	})
   252  	require.NoError(t, err)
   253  
   254  	mkkey := func(k int) []byte {
   255  		return []byte(fmt.Sprintf("%08d", k))
   256  	}
   257  	v := []byte("hello world")
   258  
   259  	snapshots := make([]*Snapshot, 0, runs)
   260  	for r := 0; r < runs; r++ {
   261  		// We use a keyspace that is 2*runs*runs wide. In other words there are
   262  		// 2*runs sections of the keyspace, each with runs elements. On every
   263  		// run, we write to the r-th element of each section of the keyspace.
   264  		for i := 0; i < 2*runs; i++ {
   265  			err := d.Set(mkkey(runs*i+r), v, nil)
   266  			require.NoError(t, err)
   267  		}
   268  
   269  		// Now we delete some of the keyspace through a DeleteRange. We delete from
   270  		// the middle of the keyspace outwards. The keyspace is made of 2*runs
   271  		// sections, and we delete an additional two of these sections per run.
   272  		err := d.DeleteRange(mkkey(middleKey-runs*r), mkkey(middleKey+runs*r), nil)
   273  		require.NoError(t, err)
   274  
   275  		snapshots = append(snapshots, d.NewSnapshot())
   276  	}
   277  
   278  	// Check that all the snapshots contain the expected number of keys.
   279  	// Iterating over so many keys is slow, so do it in parallel.
   280  	var wg sync.WaitGroup
   281  	sem := make(chan struct{}, runtime.GOMAXPROCS(0))
   282  	for r := range snapshots {
   283  		wg.Add(1)
   284  		sem <- struct{}{}
   285  		go func(r int) {
   286  			defer func() {
   287  				<-sem
   288  				wg.Done()
   289  			}()
   290  
   291  			// Count the keys at this snapshot.
   292  			iter, _ := snapshots[r].NewIter(nil)
   293  			var keysFound int
   294  			for iter.First(); iter.Valid(); iter.Next() {
   295  				keysFound++
   296  			}
   297  			err := firstError(iter.Error(), iter.Close())
   298  			if err != nil {
   299  				t.Error(err)
   300  				return
   301  			}
   302  
   303  			// At the time that this snapshot was taken, (r+1)*2*runs unique keys
   304  			// were Set (one in each of the 2*runs sections per run).  But this
   305  			// run also deleted the 2*r middlemost sections.  When this snapshot
   306  			// was taken, a Set to each of those sections had been made (r+1)
   307  			// times, so 2*r*(r+1) previously-set keys are now deleted.
   308  
   309  			keysExpected := (r+1)*2*runs - 2*r*(r+1)
   310  			if keysFound != keysExpected {
   311  				t.Errorf("%d: found %d keys, want %d", r, keysFound, keysExpected)
   312  			}
   313  			if err := snapshots[r].Close(); err != nil {
   314  				t.Error(err)
   315  			}
   316  		}(r)
   317  	}
   318  	wg.Wait()
   319  	require.NoError(t, d.Close())
   320  }
   321  
   322  // TestNewSnapshotRace tests atomicity of NewSnapshot.
   323  //
   324  // It tests for a regression of a previous race condition in which NewSnapshot
   325  // would retrieve the visible sequence number for a new snapshot before
   326  // locking the database mutex to add the snapshot. A write and flush that
   327  // that occurred between the reading of the sequence number and appending the
   328  // snapshot could drop keys required by the snapshot.
   329  func TestNewSnapshotRace(t *testing.T) {
   330  	const runs = 10
   331  	d, err := Open("", &Options{FS: vfs.NewMem()})
   332  	require.NoError(t, err)
   333  
   334  	v := []byte(`foo`)
   335  	ch := make(chan string)
   336  	var wg sync.WaitGroup
   337  	wg.Add(1)
   338  
   339  	go func() {
   340  		defer wg.Done()
   341  		for k := range ch {
   342  			if err := d.Set([]byte(k), v, nil); err != nil {
   343  				t.Error(err)
   344  				return
   345  			}
   346  			if err := d.Flush(); err != nil {
   347  				t.Error(err)
   348  				return
   349  			}
   350  		}
   351  	}()
   352  	for i := 0; i < runs; i++ {
   353  		// This main test goroutine sets `k` before creating a new snapshot.
   354  		// The key `k` should always be present within the snapshot.
   355  		k := fmt.Sprintf("key%06d", i)
   356  		require.NoError(t, d.Set([]byte(k), v, nil))
   357  
   358  		// Lock d.mu in another goroutine so that our call to NewSnapshot
   359  		// will need to contend for d.mu.
   360  		wg.Add(1)
   361  		locked := make(chan struct{})
   362  		go func() {
   363  			defer wg.Done()
   364  			d.mu.Lock()
   365  			close(locked)
   366  			time.Sleep(20 * time.Millisecond)
   367  			d.mu.Unlock()
   368  		}()
   369  		<-locked
   370  
   371  		// Tell the other goroutine to overwrite `k` with a later sequence
   372  		// number. It's indeterminate which key we'll read, but we should
   373  		// always read one of them.
   374  		ch <- k
   375  		s := d.NewSnapshot()
   376  		_, c, err := s.Get([]byte(k))
   377  		require.NoError(t, err)
   378  		require.NoError(t, c.Close())
   379  		require.NoError(t, s.Close())
   380  	}
   381  	close(ch)
   382  	wg.Wait()
   383  	require.NoError(t, d.Close())
   384  }