Skip to content

Commit 3045f88

Browse files
feat(externalMagic): Introduce external magic number (#1745) (#1852)
Cherry-pick of #1745
1 parent 0d9e263 commit 3045f88

File tree

5 files changed

+70
-20
lines changed

5 files changed

+70
-20
lines changed

badger/cmd/info.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type flagOptions struct {
5151
encryptionKey string
5252
checksumVerificationMode string
5353
discard bool
54+
externalMagicVersion uint16
5455
}
5556

5657
var (
@@ -82,6 +83,8 @@ func init() {
8283
"[none, table, block, tableAndBlock] Specifies when the db should verify checksum for SST.")
8384
infoCmd.Flags().BoolVar(&opt.discard, "discard", false,
8485
"Parse and print DISCARD file from value logs.")
86+
infoCmd.Flags().Uint16Var(&opt.externalMagicVersion, "external-magic", 0,
87+
"External magic number")
8588
}
8689

8790
var infoCmd = &cobra.Command{
@@ -104,7 +107,8 @@ func handleInfo(cmd *cobra.Command, args []string) error {
104107
WithBlockCacheSize(100 << 20).
105108
WithIndexCacheSize(200 << 20).
106109
WithEncryptionKey([]byte(opt.encryptionKey)).
107-
WithChecksumVerificationMode(cvMode)
110+
WithChecksumVerificationMode(cvMode).
111+
WithExternalMagic(opt.externalMagicVersion)
108112

109113
if opt.discard {
110114
ds, err := badger.InitDiscardStats(bopt)
@@ -322,7 +326,7 @@ func printInfo(dir, valueDir string) error {
322326
fp.Close()
323327
}
324328
}()
325-
manifest, truncOffset, err := badger.ReplayManifestFile(fp)
329+
manifest, truncOffset, err := badger.ReplayManifestFile(fp, opt.externalMagicVersion)
326330
if err != nil {
327331
return err
328332
}

manifest.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"hash/crc32"
2525
"io"
26+
"math"
2627
"os"
2728
"path/filepath"
2829
"sync"
@@ -79,6 +80,10 @@ type TableManifest struct {
7980
type manifestFile struct {
8081
fp *os.File
8182
directory string
83+
84+
// The external magic number used by the application running badger.
85+
externalMagic uint16
86+
8287
// We make this configurable so that unit tests can hit rewrite() code quickly
8388
deletionsRewriteThreshold int
8489

@@ -124,11 +129,12 @@ func openOrCreateManifestFile(opt Options) (
124129
if opt.InMemory {
125130
return &manifestFile{inMemory: true}, Manifest{}, nil
126131
}
127-
return helpOpenOrCreateManifestFile(opt.Dir, opt.ReadOnly, manifestDeletionsRewriteThreshold)
132+
return helpOpenOrCreateManifestFile(opt.Dir, opt.ReadOnly, opt.ExternalMagicVersion,
133+
manifestDeletionsRewriteThreshold)
128134
}
129135

130-
func helpOpenOrCreateManifestFile(dir string, readOnly bool, deletionsThreshold int) (
131-
*manifestFile, Manifest, error) {
136+
func helpOpenOrCreateManifestFile(dir string, readOnly bool, extMagic uint16,
137+
deletionsThreshold int) (*manifestFile, Manifest, error) {
132138

133139
path := filepath.Join(dir, ManifestFilename)
134140
var flags y.Flags
@@ -144,21 +150,22 @@ func helpOpenOrCreateManifestFile(dir string, readOnly bool, deletionsThreshold
144150
return nil, Manifest{}, fmt.Errorf("no manifest found, required for read-only db")
145151
}
146152
m := createManifest()
147-
fp, netCreations, err := helpRewrite(dir, &m)
153+
fp, netCreations, err := helpRewrite(dir, &m, extMagic)
148154
if err != nil {
149155
return nil, Manifest{}, err
150156
}
151157
y.AssertTrue(netCreations == 0)
152158
mf := &manifestFile{
153159
fp: fp,
154160
directory: dir,
161+
externalMagic: extMagic,
155162
manifest: m.clone(),
156163
deletionsRewriteThreshold: deletionsThreshold,
157164
}
158165
return mf, m, nil
159166
}
160167

161-
manifest, truncOffset, err := ReplayManifestFile(fp)
168+
manifest, truncOffset, err := ReplayManifestFile(fp, extMagic)
162169
if err != nil {
163170
_ = fp.Close()
164171
return nil, Manifest{}, err
@@ -179,6 +186,7 @@ func helpOpenOrCreateManifestFile(dir string, readOnly bool, deletionsThreshold
179186
mf := &manifestFile{
180187
fp: fp,
181188
directory: dir,
189+
externalMagic: extMagic,
182190
manifest: manifest.clone(),
183191
deletionsRewriteThreshold: deletionsThreshold,
184192
}
@@ -237,20 +245,27 @@ var syncFunc = func(f *os.File) error { return f.Sync() }
237245
// Has to be 4 bytes. The value can never change, ever, anyway.
238246
var magicText = [4]byte{'B', 'd', 'g', 'r'}
239247

240-
// The magic version number.
241-
const magicVersion = 8
248+
// The magic version number. It is allocated 2 bytes, so it's value must be <= math.MaxUint16
249+
const badgerMagicVersion = 8
242250

243-
func helpRewrite(dir string, m *Manifest) (*os.File, int, error) {
251+
func helpRewrite(dir string, m *Manifest, extMagic uint16) (*os.File, int, error) {
244252
rewritePath := filepath.Join(dir, manifestRewriteFilename)
245253
// We explicitly sync.
246254
fp, err := y.OpenTruncFile(rewritePath, false)
247255
if err != nil {
248256
return nil, 0, err
249257
}
250258

259+
// magic bytes are structured as
260+
// +---------------------+-------------------------+-----------------------+
261+
// | magicText (4 bytes) | externalMagic (2 bytes) | badgerMagic (2 bytes) |
262+
// +---------------------+-------------------------+-----------------------+
263+
264+
y.AssertTrue(badgerMagicVersion <= math.MaxUint16)
251265
buf := make([]byte, 8)
252266
copy(buf[0:4], magicText[:])
253-
binary.BigEndian.PutUint32(buf[4:8], magicVersion)
267+
binary.BigEndian.PutUint16(buf[4:6], extMagic)
268+
binary.BigEndian.PutUint16(buf[6:8], badgerMagicVersion)
254269

255270
netCreations := len(m.Tables)
256271
changes := m.asChanges()
@@ -305,7 +320,7 @@ func (mf *manifestFile) rewrite() error {
305320
if err := mf.fp.Close(); err != nil {
306321
return err
307322
}
308-
fp, netCreations, err := helpRewrite(mf.directory, &mf.manifest)
323+
fp, netCreations, err := helpRewrite(mf.directory, &mf.manifest, mf.externalMagic)
309324
if err != nil {
310325
return err
311326
}
@@ -345,7 +360,7 @@ var (
345360
// Also, returns the last offset after a completely read manifest entry -- the file must be
346361
// truncated at that point before further appends are made (if there is a partial entry after
347362
// that). In normal conditions, truncOffset is the file size.
348-
func ReplayManifestFile(fp *os.File) (Manifest, int64, error) {
363+
func ReplayManifestFile(fp *os.File, extMagic uint16) (Manifest, int64, error) {
349364
r := countingReader{wrapped: bufio.NewReader(fp)}
350365

351366
var magicBuf [8]byte
@@ -355,14 +370,22 @@ func ReplayManifestFile(fp *os.File) (Manifest, int64, error) {
355370
if !bytes.Equal(magicBuf[0:4], magicText[:]) {
356371
return Manifest{}, 0, errBadMagic
357372
}
358-
version := y.BytesToU32(magicBuf[4:8])
359-
if version != magicVersion {
373+
374+
extVersion := y.BytesToU16(magicBuf[4:6])
375+
version := y.BytesToU16(magicBuf[6:8])
376+
377+
if version != badgerMagicVersion {
360378
return Manifest{}, 0,
361379
//nolint:lll
362380
fmt.Errorf("manifest has unsupported version: %d (we support %d).\n"+
363381
"Please see https://github.com/dgraph-io/badger/blob/master/README.md#i-see-manifest-has-unsupported-version-x-we-support-y-error"+
364382
" on how to fix this.",
365-
version, magicVersion)
383+
version, badgerMagicVersion)
384+
}
385+
if extVersion != extMagic {
386+
return Manifest{}, 0,
387+
fmt.Errorf("Cannot open DB because the external magic number doesn't match. "+
388+
"Expected: %d, version present in manifest: %d\n", extMagic, extVersion)
366389
}
367390

368391
stat, err := fp.Stat()

manifest_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func TestManifestMagic(t *testing.T) {
104104
}
105105

106106
func TestManifestVersion(t *testing.T) {
107-
helpTestManifestFileCorruption(t, 4, "unsupported version")
107+
helpTestManifestFileCorruption(t, 6, "unsupported version")
108108
}
109109

110110
func TestManifestChecksum(t *testing.T) {
@@ -215,7 +215,7 @@ func TestManifestRewrite(t *testing.T) {
215215
require.NoError(t, err)
216216
defer removeDir(dir)
217217
deletionsThreshold := 10
218-
mf, m, err := helpOpenOrCreateManifestFile(dir, false, deletionsThreshold)
218+
mf, m, err := helpOpenOrCreateManifestFile(dir, false, 0, deletionsThreshold)
219219
defer func() {
220220
if mf != nil {
221221
mf.close()
@@ -241,7 +241,7 @@ func TestManifestRewrite(t *testing.T) {
241241
err = mf.close()
242242
require.NoError(t, err)
243243
mf = nil
244-
mf, m, err = helpOpenOrCreateManifestFile(dir, false, deletionsThreshold)
244+
mf, m, err = helpOpenOrCreateManifestFile(dir, false, 0, deletionsThreshold)
245245
require.NoError(t, err)
246246
require.Equal(t, map[uint64]TableManifest{
247247
uint64(deletionsThreshold * 3): {Level: 0},
@@ -260,7 +260,7 @@ func TestConcurrentManifestCompaction(t *testing.T) {
260260
return f.Sync()
261261
}
262262

263-
mf, _, err := helpOpenOrCreateManifestFile(dir, false, 0)
263+
mf, _, err := helpOpenOrCreateManifestFile(dir, false, 0, 0)
264264
require.NoError(t, err)
265265

266266
cs := &pb.ManifestChangeSet{}

options.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ type Options struct {
112112
// NamespaceOffset specifies the offset from where the next 8 bytes contains the namespace.
113113
NamespaceOffset int
114114

115+
// Magic version used by the application using badger to ensure that it doesn't open the DB
116+
// with incompatible data format.
117+
ExternalMagicVersion uint16
118+
115119
// Transaction start and commit timestamps are managed by end-user.
116120
// This is only useful for databases built on top of Badger (like Dgraph).
117121
// Not recommended for most users.
@@ -779,6 +783,13 @@ func (opt Options) WithNamespaceOffset(offset int) Options {
779783
return opt
780784
}
781785

786+
// WithExternalMagic returns a new Options value with ExternalMagicVersion set to the given value.
787+
// The DB would fail to start if either the internal or the external magic number fails validated.
788+
func (opt Options) WithExternalMagic(magic uint16) Options {
789+
opt.ExternalMagicVersion = magic
790+
return opt
791+
}
792+
782793
func (opt Options) getFileFlags() int {
783794
var flags int
784795
// opt.SyncWrites would be using msync to sync. All writes go through mmap.

y/y.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,18 @@ func (t *Throttle) Finish() error {
257257
return t.finishErr
258258
}
259259

260+
// U16ToBytes converts the given Uint16 to bytes
261+
func U16ToBytes(v uint16) []byte {
262+
var uBuf [2]byte
263+
binary.BigEndian.PutUint16(uBuf[:], v)
264+
return uBuf[:]
265+
}
266+
267+
// BytesToU16 converts the given byte slice to uint16
268+
func BytesToU16(b []byte) uint16 {
269+
return binary.BigEndian.Uint16(b)
270+
}
271+
260272
// U32ToBytes converts the given Uint32 to bytes
261273
func U32ToBytes(v uint32) []byte {
262274
var uBuf [4]byte

0 commit comments

Comments
 (0)