Skip to content

Commit 8099786

Browse files
committed
invariants: add buffer mangler, use for blob.ValueFetcher
This moves the buffer mangling logic from the valblk fetcher to a generic construct and uses it for the blob fetcher as well.
1 parent dc93da6 commit 8099786

File tree

4 files changed

+39
-19
lines changed

4 files changed

+39
-19
lines changed

internal/invariants/off.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ func (*Value[V]) Get() V {
5050
// Set the value; no-op in non-invariant builds.
5151
func (*Value[V]) Set(v V) {}
5252

53+
// BufMangler is a utility that can be used to test that the caller doesn't use
54+
type BufMangler struct{}
55+
56+
// MaybeMangleLater returns either the given buffer or a copy of it which will
57+
// be mangled the next time this function is called.
58+
func (bm *BufMangler) MaybeMangleLater(buf []byte) []byte {
59+
return buf
60+
}
61+
5362
// CheckBounds panics if the index is not in the range [0, n). No-op in
5463
// non-invariant builds.
5564
func CheckBounds[T Integer](i T, n T) {}

internal/invariants/on.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package invariants
99
import (
1010
"fmt"
1111
"math/rand/v2"
12+
"slices"
1213
)
1314

1415
// Sometimes returns true percent% of the time if invariants are Enabled (i.e.
@@ -71,6 +72,27 @@ func (v *Value[V]) Get() V {
7172
return v.v
7273
}
7374

75+
// BufMangler is a utility that can be used to test that the caller doesn't use
76+
type BufMangler struct {
77+
lastReturnedBuf []byte
78+
}
79+
80+
// MaybeMangleLater returns either the given buffer or a copy of it which will
81+
// be mangled the next time this function is called.
82+
func (bm *BufMangler) MaybeMangleLater(buf []byte) []byte {
83+
if bm.lastReturnedBuf != nil {
84+
for i := range bm.lastReturnedBuf {
85+
bm.lastReturnedBuf[i] = 0xCC
86+
}
87+
bm.lastReturnedBuf = nil
88+
}
89+
if rand.Uint32N(2) == 0 {
90+
bm.lastReturnedBuf = slices.Clone(buf)
91+
return bm.lastReturnedBuf
92+
}
93+
return buf
94+
}
95+
7496
// Set the value; no-op in non-invariant builds.
7597
func (v *Value[V]) Set(inner V) {
7698
v.v = inner

sstable/blob/fetcher.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type ValueFetcher struct {
6868
env block.ReadEnv
6969
fetchCount int
7070
readers [maxCachedReaders]cachedReader
71+
bufMangler invariants.BufMangler
7172
}
7273

7374
// TODO(jackson): Support setting up a read handle for compaction when relevant.
@@ -97,6 +98,9 @@ func (r *ValueFetcher) Fetch(
9798
ValueID: handleSuffix.ValueID,
9899
}
99100
v, err := r.retrieve(ctx, vh)
101+
if invariants.Enabled {
102+
v = r.bufMangler.MaybeMangleLater(v)
103+
}
100104
return v, false, err
101105
}
102106

sstable/valblk/reader.go

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package valblk
66

77
import (
88
"context"
9-
"math/rand/v2"
109
"unsafe"
1110

1211
"github.com/cockroachdb/pebble/internal/base"
@@ -165,11 +164,12 @@ type valueBlockFetcher struct {
165164
valueBlockPtr unsafe.Pointer
166165
valueCache block.BufferHandle
167166
closed bool
168-
bufToMangle []byte
169167

170168
// lazyFetcher is the LazyFetcher value embedded in any LazyValue that we
171169
// return. It is used to avoid having a separate allocation for that.
172170
lazyFetcher base.LazyFetcher
171+
172+
bufMangler invariants.BufMangler
173173
}
174174

175175
var _ base.ValueFetcher = (*valueBlockFetcher)(nil)
@@ -193,9 +193,9 @@ func (f *valueBlockFetcher) Fetch(
193193
ctx context.Context, handle []byte, _ base.BlobFileID, valLen uint32, buf []byte,
194194
) (val []byte, callerOwned bool, err error) {
195195
if !f.closed {
196-
val, err := f.getValueInternal(handle, valLen)
196+
val, err = f.getValueInternal(handle, valLen)
197197
if invariants.Enabled {
198-
val = f.doValueMangling(val)
198+
val = f.bufMangler.MaybeMangleLater(val)
199199
}
200200
return val, false, err
201201
}
@@ -235,21 +235,6 @@ func (f *valueBlockFetcher) close() {
235235
// implemented.
236236
}
237237

238-
// doValueMangling attempts to uncover violations of the contract listed in
239-
// the declaration comment of LazyValue. It is expensive, hence only called
240-
// when invariants.Enabled.
241-
func (f *valueBlockFetcher) doValueMangling(v []byte) []byte {
242-
// Randomly set the bytes in the previous retrieved value to 0, since
243-
// property P1 only requires the valueBlockReader to maintain the memory of
244-
// one fetched value.
245-
if rand.IntN(2) == 0 {
246-
clear(f.bufToMangle)
247-
}
248-
// Store the current value in a new buffer for future mangling.
249-
f.bufToMangle = append([]byte(nil), v...)
250-
return f.bufToMangle
251-
}
252-
253238
func (f *valueBlockFetcher) getValueInternal(handle []byte, valLen uint32) (val []byte, err error) {
254239
vh := DecodeRemainingHandle(handle)
255240
vh.ValueLen = valLen

0 commit comments

Comments
 (0)