Skip to content
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

feat(store/v2): remove cosmos-db depdency from store #19229

Merged
merged 12 commits into from
Jan 29, 2024
4 changes: 2 additions & 2 deletions store/commitment/iavl/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package iavl
import (
"fmt"

dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/iavl"
ics23 "github.com/cosmos/ics23/go"

log "cosmossdk.io/log"
"cosmossdk.io/store/v2/commitment"
dbm "cosmossdk.io/store/v2/db"
)

var _ commitment.Tree = (*IavlTree)(nil)
Expand All @@ -20,7 +20,7 @@ type IavlTree struct {

// NewIavlTree creates a new IavlTree instance.
func NewIavlTree(db dbm.DB, logger log.Logger, cfg *Config) *IavlTree {
tree := iavl.NewMutableTree(db, cfg.CacheSize, cfg.SkipFastStorageUpgrade, logger)
tree := iavl.NewMutableTree(dbm.NewWrapper(db), cfg.CacheSize, cfg.SkipFastStorageUpgrade, logger)
return &IavlTree{
tree: tree,
}
Expand Down
2 changes: 1 addition & 1 deletion store/commitment/iavl/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package iavl
import (
"testing"

dbm "github.com/cosmos/cosmos-db"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

"cosmossdk.io/log"
"cosmossdk.io/store/v2/commitment"
dbm "cosmossdk.io/store/v2/db"
)

func TestCommitterSuite(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion store/commitment/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
"io"
"math"

dbm "github.com/cosmos/cosmos-db"
protoio "github.com/cosmos/gogoproto/io"

"cosmossdk.io/log"
"cosmossdk.io/store/v2"
dbm "cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/internal/encoding"
"cosmossdk.io/store/v2/snapshots"
snapshotstypes "cosmossdk.io/store/v2/snapshots/types"
Expand Down
2 changes: 1 addition & 1 deletion store/commitment/store_test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"io"
"sync"

dbm "github.com/cosmos/cosmos-db"
"github.com/stretchr/testify/suite"

"cosmossdk.io/log"
"cosmossdk.io/store/v2"
dbm "cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/snapshots"
snapshotstypes "cosmossdk.io/store/v2/snapshots/types"
)
Expand Down
143 changes: 143 additions & 0 deletions store/db/database.go
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package db

import "errors"

var (
// errBatchClosed is returned when a closed or written batch is used.
errBatchClosed = errors.New("batch has been written or closed")

// errKeyEmpty is returned when attempting to use an empty or nil key.
errKeyEmpty = errors.New("key cannot be empty")

// errValueNil is returned when attempting to set a nil value.
errValueNil = errors.New("value cannot be nil")
)

// DB is the main interface for all key-value database backends. DBs are concurrency-safe.
// Callers must call Close on the database when done.
//
// Keys cannot be nil or empty, while values cannot be nil. Keys and values should be considered
// read-only, both when returned and when given, and must be copied before they are modified.
type DB interface {
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
// Get fetches the value of the given key, or nil if it does not exist.
// CONTRACT: key, value readonly []byte
Get([]byte) ([]byte, error)

// Has checks if a key exists.
// CONTRACT: key, value readonly []byte
Has(key []byte) (bool, error)

// Iterator returns an iterator over a domain of keys, in ascending order. The caller must call
// Close when done. End is exclusive, and start must be less than end. A nil start iterates
// from the first key, and a nil end iterates to the last key (inclusive). Empty keys are not
// valid.
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
// CONTRACT: start, end readonly []byte
Iterator(start, end []byte) (Iterator, error)

// ReverseIterator returns an iterator over a domain of keys, in descending order. The caller
// must call Close when done. End is exclusive, and start must be less than end. A nil end
// iterates from the last key (inclusive), and a nil start iterates to the first key (inclusive).
// Empty keys are not valid.
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
// CONTRACT: start, end readonly []byte
ReverseIterator(start, end []byte) (Iterator, error)

// Close closes the database connection.
Close() error

// NewBatch creates a batch for atomic updates. The caller must call Batch.Close.
NewBatch() Batch

// NewBatchWithSize create a new batch for atomic updates, but with pre-allocated size.
// This will does the same thing as NewBatch if the batch implementation doesn't support pre-allocation.
NewBatchWithSize(int) Batch
}

// Iterator represents an iterator over a domain of keys. Callers must call Close when done.
// No writes can happen to a domain while there exists an iterator over it, some backends may take
// out database locks to ensure this will not happen.
//
// Callers must make sure the iterator is valid before calling any methods on it, otherwise
// these methods will panic. This is in part caused by most backend databases using this convention.
//
// As with DB, keys and values should be considered read-only, and must be copied before they are
// modified.
//
// Typical usage:
//
// var itr Iterator = ...
// defer itr.Close()
//
// for ; itr.Valid(); itr.Next() {
// k, v := itr.Key(); itr.Value()
// ...
// }
//
// if err := itr.Error(); err != nil {
// ...
// }
type Iterator interface {
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
// Domain returns the start (inclusive) and end (exclusive) limits of the iterator.
// CONTRACT: start, end readonly []byte
Domain() (start []byte, end []byte)

// Valid returns whether the current iterator is valid. Once invalid, the Iterator remains
// invalid forever.
Valid() bool

// Next moves the iterator to the next key in the database, as defined by order of iteration.
// If Valid returns false, this method will panic.
Next()

// Key returns the key at the current position. Panics if the iterator is invalid.
// CONTRACT: key readonly []byte
Key() (key []byte)

// Value returns the value at the current position. Panics if the iterator is invalid.
// CONTRACT: value readonly []byte
Value() (value []byte)

// Error returns the last error encountered by the iterator, if any.
Error() error

// Close closes the iterator, relasing any allocated resources.
Close() error
}

// Batch represents a group of writes. They may or may not be written atomically depending on the
// backend. Callers must call Close on the batch when done.
//
// As with DB, given keys and values should be considered read-only, and must not be modified after
// passing them to the batch.
type Batch interface {
// Set sets a key/value pair.
// CONTRACT: key, value readonly []byte
Set(key, value []byte) error

// Delete deletes a key/value pair.
// CONTRACT: key readonly []byte
Delete(key []byte) error

// Write writes the batch, possibly without flushing to disk. Only Close() can be called after,
// other methods will error.
Write() error

// WriteSync writes the batch and flushes it to disk. Only Close() can be called after, other
// methods will error.
WriteSync() error

// Close closes the batch. It is idempotent, but calls to other methods afterwards will error.
Close() error

// GetByteSize that returns the current size of the batch in bytes. Depending on the implementation,
// this may return the size of the underlying LSM batch, including the size of additional metadata
// on top of the expected key and value total byte count.
GetByteSize() (int, error)
}

type (
Options interface {
Get(string) interface{}
}
)
106 changes: 106 additions & 0 deletions store/db/db_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package db

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

type DBTestSuite struct {
suite.Suite

db DB
}

func (s *DBTestSuite) TestDBOperations() {
// Set
b := s.db.NewBatch()
s.Require().NoError(b.Set([]byte("key"), []byte("value")))
s.Require().NoError(b.Set([]byte("key1"), []byte("value1")))
s.Require().NoError(b.Set([]byte("key2"), []byte("value2")))
s.Require().NoError(b.Write())

// Get
value, err := s.db.Get([]byte("key"))
s.Require().NoError(err)
s.Require().Equal([]byte("value"), value)

// Has
has, err := s.db.Has([]byte("key1"))
s.Require().NoError(err)
s.Require().True(has)
has, err = s.db.Has([]byte("key3"))
s.Require().NoError(err)
s.Require().False(has)

// Delete
b = s.db.NewBatch()
s.Require().NoError(b.Delete([]byte("key1")))
s.Require().NoError(b.Write())

// Has
has, err = s.db.Has([]byte("key1"))
s.Require().NoError(err)
s.Require().False(has)
}

func (s *DBTestSuite) TestIterator() {
// Set
b := s.db.NewBatch()
for i := 0; i < 10; i++ {
s.Require().NoError(b.Set([]byte(fmt.Sprintf("key%d", i)), []byte(fmt.Sprintf("value%d", i))))
}
s.Require().NoError(b.Write())

// Iterator
itr, err := s.db.Iterator(nil, nil)
s.Require().NoError(err)
defer itr.Close()

for ; itr.Valid(); itr.Next() {
key := itr.Key()
value := itr.Value()
value1, err := s.db.Get(key)
s.Require().NoError(err)
s.Require().Equal(value1, value)
}

// Reverse Iterator
ritr, err := s.db.ReverseIterator([]byte("key0"), []byte("keys"))
s.Require().NoError(err)
defer ritr.Close()

index := 9
for ; ritr.Valid(); ritr.Next() {
key := ritr.Key()
value := ritr.Value()
s.Require().Equal([]byte(fmt.Sprintf("key%d", index)), key)
value1, err := s.db.Get(key)
s.Require().NoError(err)
s.Require().Equal(value1, value)
index -= 1
}
s.Require().Equal(-1, index)
}

func TestMemDBSuite(t *testing.T) {
suite.Run(t, &DBTestSuite{
db: NewMemDB(),
})
}

func TestGoLevelDBSuite(t *testing.T) {
db, err := NewGoLevelDB("test", t.TempDir(), nil)
require.NoError(t, err)
suite.Run(t, &DBTestSuite{
db: db,
})
}

func TestPrefixDBSuite(t *testing.T) {
suite.Run(t, &DBTestSuite{
db: NewPrefixDB(NewMemDB(), []byte("prefix")),
})
}
Loading
Loading