Skip to content

Commit 754278d

Browse files
committed
Enforce ValueThreshold max to uint16.
Keep ValueThreshold to below uint16, because that value size limit is baked into the header of the key-value pairs in SSTable. Keeps most of the simplifications of the previous change by Andy Kimball, while bringing the uint16 limits back. In fact, this change enforces that limit by returning an explicit error for invalid ValueThreshold.
1 parent 8b2e280 commit 754278d

File tree

7 files changed

+25
-14
lines changed

7 files changed

+25
-14
lines changed

db.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ func Open(opt Options) (db *DB, err error) {
167167
opt.maxBatchSize = (15 * opt.MaxTableSize) / 100
168168
opt.maxBatchCount = opt.maxBatchSize / int64(skl.MaxNodeSize)
169169

170+
if opt.ValueThreshold > math.MaxUint16-16 {
171+
return nil, ErrValueThreshold
172+
}
173+
170174
if opt.ReadOnly {
171175
// Can't truncate if the DB is read only.
172176
opt.Truncate = false

db_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,7 +1621,12 @@ func TestLSMOnly(t *testing.T) {
16211621
require.NotEqual(t, dopts.ValueLogLoadingMode, opts.ValueLogLoadingMode)
16221622
require.NotEqual(t, dopts.ValueLogFileSize, opts.ValueLogFileSize)
16231623

1624+
dopts.ValueThreshold = 1 << 16
1625+
_, err = Open(dopts)
1626+
require.Equal(t, ErrValueThreshold, err)
1627+
16241628
db, err := Open(opts)
1629+
require.NoError(t, err)
16251630
if err != nil {
16261631
t.Fatal(err)
16271632
}

errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ var (
2727
// range.
2828
ErrValueLogSize = errors.New("Invalid ValueLogFileSize, must be between 1MB and 2GB")
2929

30+
// ErrValueThreshold is returned when ValueThreshold is set to a value close to or greater than
31+
// uint16.
32+
ErrValueThreshold = errors.New("Invalid ValueThreshold, must be lower than uint16.")
33+
3034
// ErrKeyNotFound is returned when key isn't found on a txn.Get.
3135
ErrKeyNotFound = errors.New("Key not found")
3236

options.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,7 @@ var LSMOnlyOptions = Options{}
129129
func init() {
130130
LSMOnlyOptions = DefaultOptions
131131

132-
// TODO: Once we switch Skiplist code to store uint32 value sizes, we can increase this.
133-
// But for now, ensure that the value length fits in uint16.
134-
LSMOnlyOptions.ValueThreshold = 65001
132+
LSMOnlyOptions.ValueThreshold = 65500 // Max value length which fits in uint16.
135133
LSMOnlyOptions.ValueLogFileSize = 64 << 20 // Allow easy space reclamation.
136134
LSMOnlyOptions.ValueLogLoadingMode = options.FileIO
137135
}

skl/arena.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
// so that the node.value field is 64-bit aligned. This is necessary because
3131
// node.getValueOffset uses atomic.LoadUint64, which expects its input
3232
// pointer to be 64-bit aligned.
33-
nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1
33+
nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1
3434
)
3535

3636
// Arena should be lock-free.
@@ -120,8 +120,8 @@ func (s *Arena) getKey(offset uint32, size uint16) []byte {
120120

121121
// getVal returns byte slice at offset. The given size should be just the value
122122
// size and should NOT include the meta bytes.
123-
func (s *Arena) getVal(offset uint32, size uint32) (ret y.ValueStruct) {
124-
ret.Decode(s.buf[offset : offset+size])
123+
func (s *Arena) getVal(offset uint32, size uint16) (ret y.ValueStruct) {
124+
ret.Decode(s.buf[offset : offset+uint32(size)])
125125
return
126126
}
127127

skl/skl.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type node struct {
5353
// Multiple parts of the value are encoded as a single uint64 so that it
5454
// can be atomically loaded and stored:
5555
// value offset: uint32 (bits 0-31)
56-
// value size : uint32 (bits 32-63)
56+
// value size : uint16 (bits 32-47)
5757
value uint64
5858

5959
// A byte slice is 24 bytes. We are trying to save space here.
@@ -112,13 +112,13 @@ func newNode(arena *Arena, key []byte, v y.ValueStruct, height int) *node {
112112
return node
113113
}
114114

115-
func encodeValue(valOffset uint32, valSize uint32) uint64 {
115+
func encodeValue(valOffset uint32, valSize uint16) uint64 {
116116
return uint64(valSize)<<32 | uint64(valOffset)
117117
}
118118

119-
func decodeValue(value uint64) (valOffset uint32, valSize uint32) {
119+
func decodeValue(value uint64) (valOffset uint32, valSize uint16) {
120120
valOffset = uint32(value)
121-
valSize = uint32(value >> 32)
121+
valSize = uint16(value >> 32)
122122
return
123123
}
124124

@@ -134,7 +134,7 @@ func NewSkiplist(arenaSize int64) *Skiplist {
134134
}
135135
}
136136

137-
func (s *node) getValueOffset() (uint32, uint32) {
137+
func (s *node) getValueOffset() (uint32, uint16) {
138138
value := atomic.LoadUint64(&s.value)
139139
return decodeValue(value)
140140
}

y/iterator.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ func sizeVarint(x uint64) (n int) {
4747
}
4848

4949
// EncodedSize is the size of the ValueStruct when encoded
50-
func (v *ValueStruct) EncodedSize() uint32 {
50+
func (v *ValueStruct) EncodedSize() uint16 {
5151
sz := len(v.Value) + 2 // meta, usermeta.
5252
if v.ExpiresAt == 0 {
53-
return uint32(sz + 1)
53+
return uint16(sz + 1)
5454
}
5555

5656
enc := sizeVarint(v.ExpiresAt)
57-
return uint32(sz + enc)
57+
return uint16(sz + enc)
5858
}
5959

6060
// Decode uses the length of the slice to infer the length of the Value field.

0 commit comments

Comments
 (0)