Skip to content

Add pebble database implementation #1999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 50 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
d24ee4c
initial commit
Sep 11, 2023
d578d00
Merge remote-tracking branch 'upstream/dev' into pebble
Sep 11, 2023
db2eeeb
update pebble dep
Sep 11, 2023
0983042
Merge remote-tracking branch 'upstream/dev' into pebble
Sep 12, 2023
528cc2d
appease linter
Sep 12, 2023
1bcfa65
nit
Sep 12, 2023
73a934f
nit
Sep 12, 2023
1e094b4
nit
Sep 12, 2023
5f39300
Merge branch 'dev' into pebble
Sep 12, 2023
d937e6c
Merge branch 'dev' into pebble
Sep 15, 2023
721275e
nit
Sep 15, 2023
5a8d855
Merge branch 'dev' into pebble
Sep 18, 2023
9f7665b
fix tests
Sep 18, 2023
1db44c4
nit
Sep 18, 2023
7e4cf61
Merge branch 'dev' into pebble
Sep 19, 2023
4051e88
nit
Sep 19, 2023
4bc0630
Merge branch 'dev' into pebble
Sep 22, 2023
662975f
Merge branch 'dev' into pebble
Sep 25, 2023
cd0a8e2
appease linter
Sep 25, 2023
261e710
Merge branch 'dev' into pebble
dhrubabasu Sep 27, 2023
59a8615
Merge remote-tracking branch 'upstream/dev' into pebble
Oct 2, 2023
a710f54
remove TODO
Oct 2, 2023
f21c81d
remove unneeded var
Oct 2, 2023
cc7a15e
unexport error
Oct 2, 2023
f9192ed
atomic bool --> bool
Oct 2, 2023
155edc9
merged
StephenButtolph Oct 24, 2023
72a249d
match pebble version
StephenButtolph Oct 24, 2023
4b08b25
fix test:
Oct 25, 2023
b8fa698
change pebble opening log; edit comment
Oct 25, 2023
a1b7fba
pebbledb.New nit
Oct 25, 2023
6b93c90
Lock --> RLock in Put, Delete
Oct 25, 2023
610e7f8
iterator nits
Oct 25, 2023
fc91cd1
Compact Lock --> RLock
Oct 25, 2023
5aff591
prefixBounds --> keyRange and prefixToUpperBound
Oct 25, 2023
49499fa
Merge remote-tracking branch 'upstream/dev' into pebble
Oct 25, 2023
bba3048
remove unneeded iterator creator code
Oct 25, 2023
3ffc6a8
remove unneeded check in iter.Next
Oct 25, 2023
a8ff7b8
remove unused var
Oct 25, 2023
c6bacc7
change switch order in iter.Next to not overwrite iter error
Oct 25, 2023
b886790
remove unneeded lock grab and db closed check in iter.Next
Oct 25, 2023
f771665
prevent Compact panic
Oct 25, 2023
3412216
fix default MaxConcurrentCompactions; fix compaction iterator leak; a…
Oct 25, 2023
f0e3f2e
cleanup and comments
Oct 25, 2023
c4d1a87
add Key and Value invariant on database.Iterator
Oct 25, 2023
d379e77
spelling is hard
Oct 25, 2023
8eea9eb
make pebble.New take in config bytes instead of config
Oct 25, 2023
c350116
Merge remote-tracking branch 'upstream/dev' into pebble
Oct 25, 2023
b64de08
os.MkDirTemp --> t.TempDir
Oct 25, 2023
b1768c1
remove unneeded file removal
Oct 25, 2023
afd88e3
Close db
StephenButtolph Oct 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions database/pebble/batch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package pebble

import (
"fmt"
"sync/atomic"

"github.com/cockroachdb/pebble"

"github.com/ava-labs/avalanchego/database"
)

var _ database.Batch = (*batch)(nil)

// Not safe for concurrent use.
type batch struct {
batch *pebble.Batch
db *Database
size int

// True iff [batch] has been written to the database
// since the last time [Reset] was called.
written atomic.Bool
}

func (db *Database) NewBatch() database.Batch {
return &batch{
db: db,
batch: db.pebbleDB.NewBatch(),
}
}

func (b *batch) Put(key, value []byte) error {
b.size += len(key) + len(value) + pebbleByteOverHead
return b.batch.Set(key, value, pebble.Sync)
}

func (b *batch) Delete(key []byte) error {
b.size += len(key) + pebbleByteOverHead
return b.batch.Delete(key, pebble.Sync)
}

func (b *batch) Size() int {
return b.size
}

// Assumes [b.db.lock] is not held.
func (b *batch) Write() error {
b.db.lock.RLock()
defer b.db.lock.RUnlock()

// Committing to a closed database makes pebble panic
// so make sure [b.db] isn't closed.
if b.db.closed {
return database.ErrClosed
}

if b.written.CompareAndSwap(false, true) {
// This batch has not been written to the database yet.
return updateError(b.batch.Commit(pebble.Sync))
}

// pebble doesn't support writing a batch twice so we have to clone
// [b] and commit the clone.
batchClone := b.db.pebbleDB.NewBatch()

// Copy the batch.
if err := batchClone.Apply(b.batch, nil); err != nil {
return err
}
Comment on lines +73 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it isn't used - but wondering if this would be cleaner if we passed in pebble.Sync here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm...I don't feel super strongly. If we pass in pebble.Sync then people looking at this code in the future might think that we think now that the value does something, even though it doesn't. We could just put a comment saying that we know it isn't used, but it feels weird to be like "this does nothing but we're gonna do it anyway." So I'd lean toward leaving it but w/e


// Commit the new batch.
return updateError(batchClone.Commit(pebble.Sync))
}

func (b *batch) Reset() {
b.batch.Reset()
b.written.Store(false)
b.size = 0
}

func (b *batch) Replay(w database.KeyValueWriterDeleter) error {
reader := b.batch.Reader()
for {
kind, k, v, ok := reader.Next()
if !ok {
return nil
}
switch kind {
case pebble.InternalKeyKindSet:
if err := w.Put(k, v); err != nil {
return err
}
case pebble.InternalKeyKindDelete:
if err := w.Delete(k); err != nil {
return err
}
default:
return fmt.Errorf("%w: %v", ErrInvalidOperation, kind)
}
}
}

func (b *batch) Inner() database.Batch {
return b
}
47 changes: 47 additions & 0 deletions database/pebble/batch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package pebble

import (
"os"
"testing"

"github.com/prometheus/client_golang/prometheus"

"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/utils/logging"
)

// Note: TestInterface tests other batch functionality.
func TestBatch(t *testing.T) {
require := require.New(t)
dirName := os.TempDir()
defer os.Remove(dirName)

db, err := New(dirName, DefaultConfig, logging.NoLog{}, "", prometheus.NewRegistry())
require.NoError(err)

batchIntf := db.NewBatch()
batch, ok := batchIntf.(*batch)
require.True(ok)

require.False(batch.written.Load())

key1, value1 := []byte("key1"), []byte("value1")
require.NoError(batch.Put(key1, value1))
require.Equal(len(key1)+len(value1)+pebbleByteOverHead, batch.Size())

require.NoError(batch.Write())

require.True(batch.written.Load())

got, err := db.Get(key1)
require.NoError(err)
require.Equal(value1, got)

batch.Reset()
require.False(batch.written.Load())
require.Zero(batch.Size())
}
Loading