github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/metamorphic/key_manager_test.go (about)

     1  package metamorphic
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/cockroachdb/datadriven"
    11  	"github.com/cockroachdb/pebble/internal/randvar"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestObjKey(t *testing.T) {
    16  	testCases := []struct {
    17  		key  objKey
    18  		want string
    19  	}{
    20  		{
    21  			key:  makeObjKey(makeObjID(dbTag, 1), []byte("foo")),
    22  			want: "db1:foo",
    23  		},
    24  		{
    25  			key:  makeObjKey(makeObjID(batchTag, 1), []byte("bar")),
    26  			want: "batch1:bar",
    27  		},
    28  	}
    29  
    30  	for _, tc := range testCases {
    31  		t.Run("", func(t *testing.T) {
    32  			require.Equal(t, tc.want, tc.key.String())
    33  		})
    34  	}
    35  }
    36  
    37  func TestKeyManager_AddKey(t *testing.T) {
    38  	m := newKeyManager(1 /* numInstances */)
    39  	require.Empty(t, m.globalKeys)
    40  
    41  	k1 := []byte("foo")
    42  	require.True(t, m.addNewKey(k1))
    43  	require.Len(t, m.globalKeys, 1)
    44  	require.Len(t, m.globalKeyPrefixes, 1)
    45  	require.Contains(t, m.globalKeys, k1)
    46  	require.Contains(t, m.globalKeyPrefixes, k1)
    47  	require.False(t, m.addNewKey(k1))
    48  	require.True(t, m.prefixExists([]byte("foo")))
    49  	require.False(t, m.prefixExists([]byte("bar")))
    50  
    51  	k2 := []byte("bar")
    52  	require.True(t, m.addNewKey(k2))
    53  	require.Len(t, m.globalKeys, 2)
    54  	require.Len(t, m.globalKeyPrefixes, 2)
    55  	require.Contains(t, m.globalKeys, k2)
    56  	require.Contains(t, m.globalKeyPrefixes, k2)
    57  	require.True(t, m.prefixExists([]byte("bar")))
    58  	k3 := []byte("bax@4")
    59  	require.True(t, m.addNewKey(k3))
    60  	require.Len(t, m.globalKeys, 3)
    61  	require.Len(t, m.globalKeyPrefixes, 3)
    62  	require.Contains(t, m.globalKeys, k3)
    63  	require.Contains(t, m.globalKeyPrefixes, []byte("bax"))
    64  	require.True(t, m.prefixExists([]byte("bax")))
    65  	k4 := []byte("foo@6")
    66  	require.True(t, m.addNewKey(k4))
    67  	require.Len(t, m.globalKeys, 4)
    68  	require.Len(t, m.globalKeyPrefixes, 3)
    69  	require.Contains(t, m.globalKeys, k4)
    70  	require.True(t, m.prefixExists([]byte("foo")))
    71  
    72  	require.Equal(t, [][]byte{
    73  		[]byte("bar"), []byte("bax"), []byte("foo"),
    74  	}, m.prefixes())
    75  }
    76  
    77  func TestKeyManager_GetOrInit(t *testing.T) {
    78  	id := makeObjID(batchTag, 1)
    79  	key := []byte("foo")
    80  	o := makeObjKey(id, key)
    81  
    82  	m := newKeyManager(1 /* numInstances */)
    83  	require.NotContains(t, m.byObjKey, o.String())
    84  	require.NotContains(t, m.byObj, id)
    85  	require.Contains(t, m.byObj, makeObjID(dbTag, 1)) // Always contains the DB key.
    86  
    87  	meta1 := m.getOrInit(id, key)
    88  	require.Contains(t, m.byObjKey, o.String())
    89  	require.Contains(t, m.byObj, id)
    90  
    91  	// Idempotent.
    92  	meta2 := m.getOrInit(id, key)
    93  	require.Equal(t, meta1, meta2)
    94  }
    95  
    96  func mustParseObjID(s string) objID {
    97  	id, err := parseObjID(s)
    98  	if err != nil {
    99  		panic(err)
   100  	}
   101  	return id
   102  }
   103  
   104  func printKeys(w io.Writer, keys [][]byte) {
   105  	if len(keys) == 0 {
   106  		fmt.Fprintln(w, "(none)")
   107  		return
   108  	}
   109  	for i, key := range keys {
   110  		if i > 0 {
   111  			fmt.Fprint(w, ", ")
   112  		}
   113  		fmt.Fprintf(w, "%q", key)
   114  	}
   115  	fmt.Fprintln(w)
   116  }
   117  
   118  func TestKeyManager(t *testing.T) {
   119  	var buf bytes.Buffer
   120  	km := newKeyManager(1 /* numInstances */)
   121  	datadriven.RunTest(t, "testdata/key_manager", func(t *testing.T, td *datadriven.TestData) string {
   122  		switch td.Cmd {
   123  		case "reset":
   124  			km = newKeyManager(1 /* numInstances */)
   125  			return ""
   126  		case "run":
   127  			buf.Reset()
   128  			for _, line := range strings.Split(td.Input, "\n") {
   129  				fields := strings.Fields(line)
   130  				switch fields[0] {
   131  				case "add-new-key":
   132  					if km.addNewKey([]byte(fields[1])) {
   133  						fmt.Fprintf(&buf, "%q is new\n", fields[1])
   134  					} else {
   135  						fmt.Fprintf(&buf, "%q already tracked\n", fields[1])
   136  					}
   137  				case "bounds":
   138  					for i := 1; i < len(fields); i++ {
   139  						objID := mustParseObjID(fields[1])
   140  						fmt.Fprintf(&buf, "%s: %s\n", objID, km.boundsByObj[objID])
   141  					}
   142  				case "keys":
   143  					fmt.Fprintf(&buf, "keys: ")
   144  					printKeys(&buf, km.globalKeys)
   145  				case "singledel-keys":
   146  					objID := mustParseObjID(fields[1])
   147  					fmt.Fprintf(&buf, "can singledel on %s: ", objID)
   148  					printKeys(&buf, km.eligibleSingleDeleteKeys(objID))
   149  				case "conflicts":
   150  					var collapsed bool
   151  					args := fields[1:]
   152  					if args[0] == "collapsed" {
   153  						collapsed = true
   154  						args = args[1:]
   155  					}
   156  					src := mustParseObjID(args[0])
   157  					dst := mustParseObjID(args[1])
   158  					fmt.Fprintf(&buf, "conflicts merging %s", src)
   159  					if collapsed {
   160  						fmt.Fprint(&buf, " (collapsed)")
   161  					}
   162  					fmt.Fprintf(&buf, " into %s: ", dst)
   163  					printKeys(&buf, km.checkForSingleDelConflicts(src, dst, collapsed))
   164  				case "op":
   165  					ops, err := parse([]byte(strings.TrimPrefix(line, "op")), parserOpts{
   166  						allowUndefinedObjs: true,
   167  					})
   168  					if err != nil {
   169  						t.Fatalf("parsing line %q: %s", line, err)
   170  					} else if len(ops) != 1 {
   171  						t.Fatalf("expected 1 op but found %d", len(ops))
   172  					}
   173  					km.update(ops[0])
   174  					fmt.Fprintf(&buf, "[%s]\n", ops[0])
   175  				default:
   176  					return fmt.Sprintf("unrecognized subcommand %q", fields[0])
   177  				}
   178  			}
   179  			return buf.String()
   180  		default:
   181  			return fmt.Sprintf("unrecognized command %q", td.Cmd)
   182  		}
   183  	})
   184  }
   185  
   186  func TestOpWrittenKeys(t *testing.T) {
   187  	for name, info := range methods {
   188  		t.Run(name, func(t *testing.T) {
   189  			// Any operations that exist in methods but are not handled in
   190  			// opWrittenKeys will result in a panic, failing the subtest.
   191  			opWrittenKeys(info.constructor())
   192  		})
   193  	}
   194  }
   195  
   196  func TestLoadPrecedingKeys(t *testing.T) {
   197  	rng := randvar.NewRand()
   198  	cfg := defaultConfig()
   199  	km := newKeyManager(1 /* numInstances */)
   200  	ops := generate(rng, 1000, cfg, km)
   201  
   202  	cfg2 := defaultConfig()
   203  	km2 := newKeyManager(1 /* numInstances */)
   204  	loadPrecedingKeys(t, ops, &cfg2, km2)
   205  
   206  	// NB: We can't assert equality, because the original run may not have
   207  	// ever used the max of the distribution.
   208  	require.Greater(t, cfg2.writeSuffixDist.Max(), uint64(1))
   209  
   210  	// NB: We can't assert equality, because the original run may have generated
   211  	// keys that it didn't end up using in operations.
   212  	require.Subset(t, km.globalKeys, km2.globalKeys)
   213  	require.Subset(t, km.globalKeyPrefixes, km2.globalKeyPrefixes)
   214  }