Skip to content

Commit 756c14a

Browse files
authored
Merge pull request #420 from bootjp/copilot/sub-pr-414-again
store: fix native Pebble restore rejecting max-size user keys
2 parents 5ad94dd + 5c943dd commit 756c14a

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

store/lsm_store.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ const (
2727
dirPerms = 0755
2828
metaLastCommitTS = "_meta_last_commit_ts"
2929
spoolBufSize = 32 * 1024 // buffer size for streaming I/O during restore
30+
31+
// maxPebbleEncodedKeySize is the limit for encoded Pebble on-disk keys,
32+
// which are the user key concatenated with the 8-byte inverted timestamp.
33+
// Using maxSnapshotKeySize+timestampSize (instead of just maxSnapshotKeySize)
34+
// avoids rejecting keys that are valid at the user-key level but slightly
35+
// exceed maxSnapshotKeySize once the timestamp suffix is appended.
36+
maxPebbleEncodedKeySize = maxSnapshotKeySize + timestampSize
3037
)
3138

3239
var metaLastCommitTSBytes = []byte(metaLastCommitTS)
@@ -663,7 +670,7 @@ func (s *pebbleStore) Snapshot() (Snapshot, error) {
663670
// from r. The key bytes are stored in *keyBuf (grown as needed to avoid per-entry
664671
// allocations). Returns (kLen, vLen, eof=true, nil) on clean EOF at the key-length field.
665672
func readRestoreEntry(r io.Reader, keyBuf *[]byte) (kLen, vLen int, eof bool, err error) {
666-
kLen, err = readRestoreFieldLen(r, "snapshot key", maxSnapshotKeySize)
673+
kLen, err = readRestoreFieldLen(r, "snapshot key", maxPebbleEncodedKeySize)
667674
if err != nil {
668675
if errors.Is(err, io.EOF) {
669676
return 0, 0, true, nil

store/lsm_store_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,3 +579,44 @@ func TestMVCCStore_ApplyMutations_ValueTooLarge(t *testing.T) {
579579
require.Error(t, err)
580580
assert.ErrorIs(t, err, ErrValueTooLarge)
581581
}
582+
583+
// TestPebbleStore_SnapshotRestore_MaxSizeKey verifies that a key of exactly
584+
// maxSnapshotKeySize bytes survives a Snapshot()+Restore() round-trip.
585+
// The encoded on-disk Pebble key is maxSnapshotKeySize+timestampSize bytes;
586+
// previously the restore path used maxSnapshotKeySize as the limit and would
587+
// reject this key.
588+
func TestPebbleStore_SnapshotRestore_MaxSizeKey(t *testing.T) {
589+
dir, err := os.MkdirTemp("", "pebble-maxkey-snap-*")
590+
require.NoError(t, err)
591+
defer os.RemoveAll(dir)
592+
593+
s, err := NewPebbleStore(dir)
594+
require.NoError(t, err)
595+
defer func() { assert.NoError(t, s.Close()) }()
596+
597+
ctx := context.Background()
598+
bigKey := bytes.Repeat([]byte("k"), maxSnapshotKeySize)
599+
require.NoError(t, s.PutAt(ctx, bigKey, []byte("val"), 1, 0))
600+
601+
snap, err := s.Snapshot()
602+
require.NoError(t, err)
603+
defer func() { assert.NoError(t, snap.Close()) }()
604+
605+
var buf bytes.Buffer
606+
_, err = snap.WriteTo(&buf)
607+
require.NoError(t, err)
608+
609+
dir2, err := os.MkdirTemp("", "pebble-maxkey-restore-*")
610+
require.NoError(t, err)
611+
defer os.RemoveAll(dir2)
612+
613+
s2, err := NewPebbleStore(dir2)
614+
require.NoError(t, err)
615+
defer func() { assert.NoError(t, s2.Close()) }()
616+
617+
require.NoError(t, s2.Restore(bytes.NewReader(buf.Bytes())))
618+
619+
got, err := s2.GetAt(ctx, bigKey, 1)
620+
require.NoError(t, err)
621+
assert.Equal(t, []byte("val"), got)
622+
}

0 commit comments

Comments
 (0)