Skip to content

Commit 59715af

Browse files
authored
Merge branch 'feature/reduce-snapshot-memory' into copilot/sub-pr-414
2 parents 6e096d4 + 756c14a commit 59715af

2 files changed

Lines changed: 50 additions & 2 deletions

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+timestampSize)
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: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ func TestPebbleStore_SnapshotRestoreLargeValues(t *testing.T) {
275275

276276
s2, err := NewPebbleStore(dir2)
277277
require.NoError(t, err)
278-
defer s2.Close()
278+
defer func() { assert.NoError(t, s2.Close()) }()
279279

280280
require.NoError(t, s2.Restore(bytes.NewReader(raw.Bytes())))
281281

@@ -594,3 +594,44 @@ func TestMVCCStore_ApplyMutations_ValueTooLarge(t *testing.T) {
594594
require.Error(t, err)
595595
assert.ErrorIs(t, err, ErrValueTooLarge)
596596
}
597+
598+
// TestPebbleStore_SnapshotRestore_MaxSizeKey verifies that a key of exactly
599+
// maxSnapshotKeySize bytes survives a Snapshot()+Restore() round-trip.
600+
// The encoded on-disk Pebble key is maxSnapshotKeySize+timestampSize bytes;
601+
// previously the restore path used maxSnapshotKeySize as the limit and would
602+
// reject this key.
603+
func TestPebbleStore_SnapshotRestore_MaxSizeKey(t *testing.T) {
604+
dir, err := os.MkdirTemp("", "pebble-maxkey-snap-*")
605+
require.NoError(t, err)
606+
defer os.RemoveAll(dir)
607+
608+
s, err := NewPebbleStore(dir)
609+
require.NoError(t, err)
610+
defer func() { assert.NoError(t, s.Close()) }()
611+
612+
ctx := context.Background()
613+
bigKey := bytes.Repeat([]byte("k"), maxSnapshotKeySize)
614+
require.NoError(t, s.PutAt(ctx, bigKey, []byte("val"), 1, 0))
615+
616+
snap, err := s.Snapshot()
617+
require.NoError(t, err)
618+
defer func() { assert.NoError(t, snap.Close()) }()
619+
620+
var buf bytes.Buffer
621+
_, err = snap.WriteTo(&buf)
622+
require.NoError(t, err)
623+
624+
dir2, err := os.MkdirTemp("", "pebble-maxkey-restore-*")
625+
require.NoError(t, err)
626+
defer os.RemoveAll(dir2)
627+
628+
s2, err := NewPebbleStore(dir2)
629+
require.NoError(t, err)
630+
defer func() { assert.NoError(t, s2.Close()) }()
631+
632+
require.NoError(t, s2.Restore(bytes.NewReader(buf.Bytes())))
633+
634+
got, err := s2.GetAt(ctx, bigKey, 1)
635+
require.NoError(t, err)
636+
assert.Equal(t, []byte("val"), got)
637+
}

0 commit comments

Comments
 (0)