Skip to content

Commit

Permalink
Existence Invariants (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
bbengfort authored Sep 7, 2023
1 parent 9966c0c commit cca5877
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 301 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
go-version: 1.19.x

- name: Install Staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest
Expand All @@ -33,7 +33,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
go-version: 1.19.x

- name: Checkout Code
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
go-version: 1.19.x

- name: Import GPG key
id: import_gpg
Expand Down
3 changes: 1 addition & 2 deletions bench_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package honu_test

import (
"io/ioutil"
"math/rand"
"os"
"testing"
Expand All @@ -21,7 +20,7 @@ var (

func setupLevelDB(t testing.TB) (*leveldb.DB, string) {
// Create a new leveldb database in a temporary directory
tmpDir, err := ioutil.TempDir("", "leveldb-*")
tmpDir, err := os.MkdirTemp("", "leveldb-*")
require.NoError(t, err)

// Open a leveldb database directly without honu wrapper
Expand Down
1 change: 1 addition & 0 deletions engines/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Engine interface {
// Store is a simple key/value interface that allows for Get, Put, and Delete. Nearly
// all engines should support the Store interface.
type Store interface {
Has(key []byte, options *opts.Options) (exists bool, err error)
Get(key []byte, options *opts.Options) (value []byte, err error)
Put(key, value []byte, options *opts.Options) error
Delete(key []byte, options *opts.Options) error
Expand Down
5 changes: 3 additions & 2 deletions engines/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package engine
import "errors"

var (
ErrNotFound = errors.New("not found")
ErrReadOnlyTx = errors.New("cannot execute a write operation in a read only transaction")
ErrNotFound = errors.New("not found")
ErrReadOnlyTx = errors.New("cannot execute a write operation in a read only transaction")
ErrAlreadyExists = errors.New("specified key already exists in the database")
)
29 changes: 29 additions & 0 deletions engines/leveldb/leveldb.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,35 @@ func (tx *Transaction) Finish() error {
return nil
}

// Has returns true if the DB does contains the given key.
func (tx *Transaction) Has(key []byte, options *opts.Options) (bool, error) {
return tx.db.has(key, options)
}

// Has returns true if the DB does contains the given key.
func (db *LevelDBEngine) Has(key []byte, options *opts.Options) (bool, error) {
db.RLock()
defer db.RUnlock()
return db.has(key, options)
}

// Has returns true if the DB does contains the given key.
func (db *LevelDBEngine) has(key []byte, options *opts.Options) (_ bool, err error) {
// Create a default to prevent panics when accessing options.
if options == nil {
if options, err = opts.New(); err != nil {
return false, err
}
}

// Namespaces in leveldb are provided not by buckets but by namespace:: prefixed keys
if options.Namespace != "" {
key = prepend(options.Namespace, key)
}

return db.ldb.Has(key, options.LevelDBRead)
}

// Get the latest version of the object stored by the key. This is the Transaction Get
// method which can be used in either readonly or write modes. This is the preferred
// mechanism to access the underlying engine.
Expand Down
12 changes: 10 additions & 2 deletions engines/leveldb/leveldb_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package leveldb_test

import (
"io/ioutil"
"os"
"testing"

Expand Down Expand Up @@ -38,7 +37,7 @@ var testNamespaces = []string{

// Returns a LevelDBEngine and the path where it was created.
func setupLevelDBEngine(t testing.TB) (_ *leveldb.LevelDBEngine, path string) {
tempDir, err := ioutil.TempDir("", "leveldb-*")
tempDir, err := os.MkdirTemp("", "leveldb-*")
require.NoError(t, err)

conf, _ := config.New()
Expand Down Expand Up @@ -71,6 +70,13 @@ func checkPut(ldbStore engine.Store, opts *options.Options, key []byte, value []
require.NoError(t, err)
}

// Wraps engine.Store.Has with testing checks
func checkHas(ldbStore engine.Store, opts *options.Options, key []byte, assert require.BoolAssertionFunc, t *testing.T) {
exists, err := ldbStore.Has(key, opts)
require.NoError(t, err)
assert(t, exists)
}

// Wraps engine.Store.Get with testing checks.
func checkGet(ldbStore engine.Store, opts *options.Options, key []byte, expectedValue []byte, t *testing.T) {
getValue, err := ldbStore.Get(key, opts)
Expand Down Expand Up @@ -134,7 +140,9 @@ func TestLevelDBTransactions(t *testing.T) {
opts, err := options.New(options.WithNamespace(namespace))
require.NoError(t, err)
value := []byte(namespace)
checkHas(tx, opts, key, require.False, t)
checkPut(tx, opts, key, value, t)
checkHas(tx, opts, key, require.True, t)
checkGet(tx, opts, key, value, t)
checkDelete(tx, opts, key, t)

Expand Down
47 changes: 39 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
module github.com/rotationalio/honu

go 1.16
go 1.19

require (
github.com/cockroachdb/pebble v0.0.0-20211021161301-9106d5d2238f
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2
github.com/klauspost/compress v1.12.3 // indirect
github.com/stretchr/testify v1.6.1
github.com/cockroachdb/pebble v0.0.0-20230906203007-2129a6e99d0f
github.com/golang/protobuf v1.5.3
github.com/stretchr/testify v1.8.4
github.com/syndtr/goleveldb v1.0.0
google.golang.org/grpc v1.39.0
google.golang.org/protobuf v1.27.1
google.golang.org/grpc v1.58.0
google.golang.org/protobuf v1.31.0
)

require (
github.com/DataDog/zstd v1.5.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cockroachdb/errors v1.11.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/getsentry/sentry-go v0.24.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/onsi/ginkgo v1.13.0 // indirect
github.com/onsi/gomega v1.10.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit cca5877

Please sign in to comment.