Skip to content

Commit c16f88c

Browse files
committed
sstable: add checksum to footer
Introduce a new FMV and table format to add a checksum to the footer of an sstable as stated in #4344
1 parent 7a210e2 commit c16f88c

26 files changed

+1900
-135
lines changed

cockroachkvs/cockroachkvs_bench_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ func BenchmarkRandSeekInSST(b *testing.B) {
5454
valueLen: 128, // ~200 KVs per data block
5555
version: sstable.TableFormatPebblev5,
5656
},
57+
{
58+
name: "v6/single-level",
59+
numKeys: 200 * 100, // ~100 data blocks.
60+
valueLen: 128, // ~200 KVs per data block
61+
version: sstable.TableFormatPebblev6,
62+
},
63+
{
64+
name: "v6/two-level",
65+
numKeys: 200 * 5000, // ~5000 data blocks
66+
valueLen: 128, // ~200 KVs per data block
67+
version: sstable.TableFormatPebblev6,
68+
},
5769
}
5870
keyCfg := keyGenConfig{
5971
PrefixAlphabetLen: 26,

compaction_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,12 @@ func TestManualCompaction(t *testing.T) {
12761276
{
12771277
testData: "testdata/manual_compaction_set_with_del_sstable_Pebblev5",
12781278
minVersion: FormatColumnarBlocks,
1279+
maxVersion: FormatColumnarBlocks,
1280+
},
1281+
{
1282+
testData: "testdata/manual_compaction_set_with_del_sstable_Pebblev6",
1283+
minVersion: formatChecksumFooter,
1284+
maxVersion: formatChecksumFooter,
12791285
},
12801286
}
12811287

format_major_version.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ const (
214214

215215
// -- Add experimental versions here --
216216

217+
// formatChecksumFooter is a format major version enabling use of the
218+
// TableFormatPebblev6 table format. It is a format allowing for the checksum
219+
// of sstable footers.
220+
formatChecksumFooter
221+
217222
// internalFormatNewest is the most recent, possibly experimental format major
218223
// version.
219224
internalFormatNewest FormatMajorVersion = iota - 2
@@ -244,6 +249,8 @@ func (v FormatMajorVersion) MaxTableFormat() sstable.TableFormat {
244249
return sstable.TableFormatPebblev4
245250
case FormatColumnarBlocks, FormatWALSyncChunks:
246251
return sstable.TableFormatPebblev5
252+
case formatChecksumFooter:
253+
return sstable.TableFormatPebblev6
247254
default:
248255
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
249256
}
@@ -255,7 +262,8 @@ func (v FormatMajorVersion) MinTableFormat() sstable.TableFormat {
255262
switch v {
256263
case FormatDefault, FormatFlushableIngest, FormatPrePebblev1MarkedCompacted,
257264
FormatDeleteSizedAndObsolete, FormatVirtualSSTables, FormatSyntheticPrefixSuffix,
258-
FormatFlushableIngestExcises, FormatColumnarBlocks, FormatWALSyncChunks:
265+
FormatFlushableIngestExcises, FormatColumnarBlocks, FormatWALSyncChunks,
266+
formatChecksumFooter:
259267
return sstable.TableFormatPebblev1
260268
default:
261269
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
@@ -301,6 +309,9 @@ var formatMajorVersionMigrations = map[FormatMajorVersion]func(*DB) error{
301309
FormatWALSyncChunks: func(d *DB) error {
302310
return d.finalizeFormatVersUpgrade(FormatWALSyncChunks)
303311
},
312+
formatChecksumFooter: func(d *DB) error {
313+
return d.finalizeFormatVersUpgrade(formatChecksumFooter)
314+
},
304315
}
305316

306317
const formatVersionMarkerName = `format-version`

format_major_version_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ func TestFormatMajorVersionStableValues(t *testing.T) {
2626
require.Equal(t, FormatSyntheticPrefixSuffix, FormatMajorVersion(17))
2727
require.Equal(t, FormatFlushableIngestExcises, FormatMajorVersion(18))
2828
require.Equal(t, FormatColumnarBlocks, FormatMajorVersion(19))
29+
require.Equal(t, FormatWALSyncChunks, FormatMajorVersion(20))
2930

3031
// When we add a new version, we should add a check for the new version in
3132
// addition to updating these expected values.
3233
require.Equal(t, FormatNewest, FormatMajorVersion(20))
33-
require.Equal(t, internalFormatNewest, FormatMajorVersion(20))
34+
require.Equal(t, internalFormatNewest, FormatMajorVersion(21))
3435
}
3536

3637
func TestFormatMajorVersion_MigrationDefined(t *testing.T) {
@@ -63,6 +64,8 @@ func TestRatchetFormat(t *testing.T) {
6364
require.Equal(t, FormatColumnarBlocks, d.FormatMajorVersion())
6465
require.NoError(t, d.RatchetFormatMajorVersion(FormatWALSyncChunks))
6566
require.Equal(t, FormatWALSyncChunks, d.FormatMajorVersion())
67+
require.NoError(t, d.RatchetFormatMajorVersion(formatChecksumFooter))
68+
require.Equal(t, formatChecksumFooter, d.FormatMajorVersion())
6669

6770
require.NoError(t, d.Close())
6871

@@ -221,6 +224,7 @@ func TestFormatMajorVersions_TableFormat(t *testing.T) {
221224
FormatFlushableIngestExcises: {sstable.TableFormatPebblev1, sstable.TableFormatPebblev4},
222225
FormatColumnarBlocks: {sstable.TableFormatPebblev1, sstable.TableFormatPebblev5},
223226
FormatWALSyncChunks: {sstable.TableFormatPebblev1, sstable.TableFormatPebblev5},
227+
formatChecksumFooter: {sstable.TableFormatPebblev1, sstable.TableFormatPebblev6},
224228
}
225229

226230
// Valid versions.

open_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ func TestNewDBFilenames(t *testing.T) {
332332
"LOCK",
333333
"MANIFEST-000001",
334334
"OPTIONS-000003",
335-
"marker.format-version.000007.020",
335+
"marker.format-version.000008.021",
336336
"marker.manifest.000001.MANIFEST-000001",
337337
},
338338
}

sstable/copier_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func TestCopySpan(t *testing.T) {
5353
tableFormat = TableFormatPebblev4
5454
case "pebblev5":
5555
tableFormat = TableFormatPebblev5
56+
case "pebblev6":
57+
tableFormat = TableFormatPebblev6
5658
}
5759
case "block_size":
5860
var err error

sstable/format.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
TableFormatPebblev3 // Value blocks.
3030
TableFormatPebblev4 // DELSIZED tombstones.
3131
TableFormatPebblev5 // Columnar blocks.
32+
TableFormatPebblev6 // Checksum Footer.
3233
NumTableFormats
3334

3435
TableFormatMax = NumTableFormats - 1
@@ -236,6 +237,8 @@ func parseTableFormat(magic []byte, version uint32) (TableFormat, error) {
236237
return TableFormatPebblev4, nil
237238
case 5:
238239
return TableFormatPebblev5, nil
240+
case 6:
241+
return TableFormatPebblev6, nil
239242
default:
240243
return TableFormatUnspecified, base.CorruptionErrorf(
241244
"(unsupported pebble format version %d)", errors.Safe(version))
@@ -276,6 +279,8 @@ func (f TableFormat) AsTuple() (string, uint32) {
276279
return pebbleDBMagic, 4
277280
case TableFormatPebblev5:
278281
return pebbleDBMagic, 5
282+
case TableFormatPebblev6:
283+
return pebbleDBMagic, 6
279284
default:
280285
panic("sstable: unknown table format version tuple")
281286
}
@@ -300,6 +305,8 @@ func (f TableFormat) String() string {
300305
return "(Pebble,v4)"
301306
case TableFormatPebblev5:
302307
return "(Pebble,v5)"
308+
case TableFormatPebblev6:
309+
return "(Pebble,v6)"
303310
default:
304311
panic("sstable: unknown table format version tuple")
305312
}

sstable/format_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ func TestTableFormat_RoundTrip(t *testing.T) {
6565
version: 5,
6666
want: TableFormatPebblev5,
6767
},
68+
{
69+
name: "PebbleDBv6",
70+
magic: pebbleDBMagic,
71+
version: 6,
72+
want: TableFormatPebblev6,
73+
},
6874
// Invalid cases.
6975
{
7076
name: "Invalid RocksDB version",
@@ -75,8 +81,8 @@ func TestTableFormat_RoundTrip(t *testing.T) {
7581
{
7682
name: "Invalid PebbleDB version",
7783
magic: pebbleDBMagic,
78-
version: 6,
79-
wantErr: "pebble/table: invalid table 000001: (unsupported pebble format version 6)",
84+
version: 7,
85+
wantErr: "pebble/table: invalid table 000001: (unsupported pebble format version 7)",
8086
},
8187
{
8288
name: "Unknown magic string",

sstable/layout.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/cockroachdb/pebble/internal/base"
1919
"github.com/cockroachdb/pebble/internal/binfmt"
2020
"github.com/cockroachdb/pebble/internal/bytealloc"
21+
"github.com/cockroachdb/pebble/internal/crc"
2122
"github.com/cockroachdb/pebble/internal/sstableinternal"
2223
"github.com/cockroachdb/pebble/internal/treeprinter"
2324
"github.com/cockroachdb/pebble/objstorage"
@@ -140,6 +141,24 @@ func (l *Layout) Describe(
140141
trailer, offset := make([]byte, b.Length), 0
141142
_ = r.blockReader.Readable().ReadAt(ctx, trailer, int64(b.Offset))
142143

144+
// In all cases, we know the version is right before the magic.
145+
version := binary.LittleEndian.Uint32(trailer[len(trailer)-magicLen-versionLen:])
146+
magicNumber := trailer[len(trailer)-magicLen:]
147+
format, err := parseTableFormat(magicNumber, version)
148+
if err != nil {
149+
panic("Error parsing table format.")
150+
}
151+
152+
var computedChecksum uint32
153+
var encodedChecksum uint32
154+
if format >= TableFormatPebblev6 {
155+
computedChecksum = crc.CRC(0).
156+
Update(trailer[:checkedPebbleDBChecksumOffset]).
157+
Update(trailer[checkedPebbleDBVersionOffset:]).
158+
Value()
159+
encodedChecksum = binary.LittleEndian.Uint32(trailer[checkedPebbleDBChecksumOffset:])
160+
}
161+
143162
if b.Name == "footer" {
144163
checksumType := block.ChecksumType(trailer[0])
145164
tpNode.Childf("%03d checksum type: %s", offset, checksumType)
@@ -160,22 +179,21 @@ func (l *Layout) Describe(
160179
if b.Name == "leveldb-footer" {
161180
trailing = 8
162181
}
163-
164182
offset += len(trailer) - trailing
165-
trailer = trailer[len(trailer)-trailing:]
166183

167-
if b.Name == "footer" {
168-
version := trailer[:4]
169-
tpNode.Childf("%03d version: %d", offset, binary.LittleEndian.Uint32(version))
170-
trailer, offset = trailer[4:], offset+4
184+
if format >= TableFormatPebblev6 {
185+
if computedChecksum == encodedChecksum {
186+
tpNode.Childf("%03d footer checksum: 0x%04x", offset-4, encodedChecksum)
187+
} else {
188+
tpNode.Childf("%03d invalid footer checksum: 0x%04x, expected: 0x%04x", offset-4, encodedChecksum, computedChecksum)
189+
}
171190
}
172191

173-
magicNumber := trailer
192+
tpNode.Childf("%03d version: %d", offset, version)
193+
offset = offset + 4
174194
tpNode.Childf("%03d magic number: 0x%x", offset, magicNumber)
175-
176195
continue
177196
}
178-
179197
// Read the block and format it. Returns an error if we couldn't read the
180198
// block.
181199
err := func() error {

0 commit comments

Comments
 (0)