Restructure storage code a bit

This commit is contained in:
2016-07-05 23:16:33 +01:00
parent 53fa0bd397
commit d1a330187e
4 changed files with 173 additions and 120 deletions

View File

@@ -1,81 +0,0 @@
package storage
import (
"strconv"
"github.com/syndtr/goleveldb/leveldb"
)
// GoleveldbStore allows storing data into a goleveldb database.
type GoleveldbStore struct {
DB *leveldb.DB
}
// NewGoleveldbStore creates a new GoleveldbStore using given path to persist
// data.
func NewGoleveldbStore(path string) (GoleveldbStore, error) {
db, err := leveldb.OpenFile(path, nil)
if err != nil {
return GoleveldbStore{}, err
}
return GoleveldbStore{DB: db}, nil
}
// Close underlying goleveldb database.
func (s *GoleveldbStore) Close() error {
return s.DB.Close()
}
// Get a given key's value.
func (s *GoleveldbStore) Get(key []byte) ([]byte, error) {
value, err := s.DB.Get(key, nil)
if err != nil && err.Error() == "leveldb: not found" {
return value, nil
}
return value, err
}
// Set a given key's to the specified value.
func (s *GoleveldbStore) Set(key []byte, value []byte) error {
return s.DB.Put(key, value, nil)
}
// Delete a given key.
func (s *GoleveldbStore) Delete(key []byte) error {
return s.DB.Delete(key, nil)
}
// Incr increments a given key (must be numeric-like value)
func (s *GoleveldbStore) Incr(key []byte) (int, error) {
tx, err := s.DB.OpenTransaction()
if err != nil {
return -1, err
}
value, err := tx.Get(key, nil)
if value == nil {
value = []byte("0")
}
num, err := strconv.Atoi(string(value))
if err != nil {
return -1, err
}
num++
value = []byte(strconv.Itoa(num))
err = tx.Put(key, value, nil)
if err != nil {
return -1, err
}
err = tx.Commit()
if err != nil {
return -1, err
}
return num, nil
}

View File

@@ -0,0 +1,98 @@
package goleveldbstore
import (
"errors"
"strconv"
"github.com/syndtr/goleveldb/leveldb"
)
// DefaultSequenceKey is used by NextSequence().
var DefaultSequenceKey = []byte("__SEQUENCE_ID__")
// ErrNotFound is returned when Get() tries to fetch a non-existent key.
var ErrNotFound = errors.New("not found")
// New creates a new Store using given path to persist data.
func New(path string) (*Store, error) {
db, err := leveldb.OpenFile(path, nil)
if err != nil {
return &Store{}, err
}
store := Store{
DB: db,
SequenceKey: DefaultSequenceKey,
}
return &store, nil
}
// Store allows storing data into a goleveldb database.
type Store struct {
DB *leveldb.DB
SequenceKey []byte
}
// Close underlying goleveldb database.
func (s *Store) Close() error {
return s.DB.Close()
}
// Get a given key's value.
func (s *Store) Get(key []byte) ([]byte, error) {
value, err := s.DB.Get(key, nil)
if err != nil && err.Error() == "leveldb: not found" {
return nil, ErrNotFound
}
return value, err
}
// Set a given key's to the specified value.
func (s *Store) Set(key []byte, value []byte) error {
return s.DB.Put(key, value, nil)
}
// Delete a given key.
func (s *Store) Delete(key []byte) error {
return s.DB.Delete(key, nil)
}
// NextSequence returns a auto-incrementing int.
func (s *Store) NextSequence() (int, error) {
return s.Incr(s.SequenceKey)
}
// Incr increments a given key (must be numeric-like value)
func (s *Store) Incr(key []byte) (int, error) {
tx, err := s.DB.OpenTransaction()
if err != nil {
return -1, err
}
value, err := tx.Get(key, nil)
if value == nil {
value = []byte("0")
}
num, err := strconv.Atoi(string(value))
if err != nil {
return -1, err
}
num++
value = []byte(strconv.Itoa(num))
err = tx.Put(key, value, nil)
if err != nil {
return -1, err
}
err = tx.Commit()
if err != nil {
return -1, err
}
return num, nil
}

View File

@@ -1,9 +1,10 @@
package storage
package goleveldbstore
import (
"os"
"testing"
"github.com/jimeh/ozu.io/storage"
"github.com/stretchr/testify/suite"
"github.com/syndtr/goleveldb/leveldb"
)
@@ -21,37 +22,41 @@ var examples = []struct {
{key: []byte("wtf"), value: []byte("dude")},
}
type GoleveldbStoreSuite struct {
type StoreSuite struct {
suite.Suite
store Store
store *Store
db *leveldb.DB
}
func (s *GoleveldbStoreSuite) Seed() {
func (s *StoreSuite) Seed() {
for _, e := range examples {
err := s.db.Put(e.key, e.value, nil)
s.Require().Nil(err)
s.Require().NoError(err)
}
}
func (s *GoleveldbStoreSuite) SetupTest() {
store, err := NewGoleveldbStore(testDbPath)
s.Nil(err)
s.store = &store
func (s *StoreSuite) SetupTest() {
store, err := New(testDbPath)
s.Require().NoError(err)
s.store = store
s.db = store.DB
}
func (s *GoleveldbStoreSuite) TearDownTest() {
s.store.Close()
os.RemoveAll(testDbPath)
func (s *StoreSuite) TearDownTest() {
_ = s.store.Close()
_ = os.RemoveAll(testDbPath)
}
// Tests
func (s *GoleveldbStoreSuite) TestSet() {
func (s *StoreSuite) TestStoreInterface() {
s.Implements(new(storage.Store), new(Store))
}
func (s *StoreSuite) TestSet() {
for _, e := range examples {
err := s.store.Set(e.key, e.value)
s.Nil(err)
s.NoError(err)
}
for _, e := range examples {
@@ -60,23 +65,23 @@ func (s *GoleveldbStoreSuite) TestSet() {
}
}
func (s *GoleveldbStoreSuite) TestGetExisting() {
func (s *StoreSuite) TestGetExisting() {
s.Seed()
for _, e := range examples {
result, err := s.store.Get(e.key)
s.Nil(err)
s.NoError(err)
s.Equal(e.value, result)
}
}
func (s *GoleveldbStoreSuite) TestGetNonExistant() {
func (s *StoreSuite) TestGetNonExistant() {
result, err := s.store.Get([]byte("does-not-exist"))
s.Nil(err)
s.Nil(result)
s.EqualError(err, "not found")
}
func (s *GoleveldbStoreSuite) TestDeleteExisting() {
func (s *StoreSuite) TestDeleteExisting() {
s.Seed()
for _, e := range examples {
@@ -84,7 +89,7 @@ func (s *GoleveldbStoreSuite) TestDeleteExisting() {
s.Require().Equal(e.value, value)
err := s.store.Delete(e.key)
s.Nil(err)
s.NoError(err)
has, _ := s.db.Has(e.key, nil)
s.Equal(false, has)
@@ -93,43 +98,62 @@ func (s *GoleveldbStoreSuite) TestDeleteExisting() {
}
}
func (s *GoleveldbStoreSuite) TestDeleteNonExistant() {
func (s *StoreSuite) TestDeleteNonExistant() {
err := s.store.Delete([]byte("does-not-exist"))
s.Nil(err)
s.NoError(err)
}
func (s *GoleveldbStoreSuite) TestIncrExisting() {
key := []byte("my-counter")
func (s *StoreSuite) TestNextSequenceExisting() {
err := s.db.Put(DefaultSequenceKey, []byte("5"), nil)
s.Require().NoError(err)
err := s.db.Put(key, []byte("5"), nil)
s.Require().Nil(err)
result, err := s.store.Incr(key)
s.Nil(err)
result, err := s.store.NextSequence()
s.NoError(err)
s.Equal(6, result)
}
func (s *GoleveldbStoreSuite) TestIncrNonExistant() {
func (s *StoreSuite) TestNextSequenceNonExistant() {
for i := 1; i < 10; i++ {
result, err := s.store.NextSequence()
s.NoError(err)
s.Equal(i, result)
}
}
func (s *StoreSuite) TestIncrExisting() {
key := []byte("my-counter")
err := s.db.Put(key, []byte("5"), nil)
s.Require().NoError(err)
result, err := s.store.Incr(key)
s.NoError(err)
s.Equal(6, result)
}
func (s *StoreSuite) TestIncrNonExistant() {
for i := 1; i < 10; i++ {
result, err := s.store.Incr([]byte("counter"))
s.Nil(err)
s.NoError(err)
s.Equal(i, result)
}
}
// Run Suite
func TestGoleveldbStoreSuite(t *testing.T) {
suite.Run(t, new(GoleveldbStoreSuite))
func TestStoreSuite(t *testing.T) {
suite.Run(t, new(StoreSuite))
}
// Benchmarks
func BenchmarkGet(b *testing.B) {
store, _ := New(testDbPath)
key := []byte("hello")
value := []byte("world")
store, _ := NewGoleveldbStore(testDbPath)
_ = store.Set(key, value)
for n := 0; n < b.N; n++ {
@@ -141,13 +165,25 @@ func BenchmarkGet(b *testing.B) {
}
func BenchmarkSet(b *testing.B) {
store, _ := New(testDbPath)
key := []byte("hello")
value := []byte("world")
store, _ := NewGoleveldbStore(testDbPath)
_ = store.Set(key, value)
for n := 0; n < b.N; n++ {
_ = store.Set(append(key, string(n)...), append(value, string(n)...))
_ = store.Set(append(key, string(n)...), value)
}
_ = store.Close()
_ = os.RemoveAll(testDbPath)
}
func BenchmarkNextSequence(b *testing.B) {
store, _ := New(testDbPath)
for n := 0; n < b.N; n++ {
_, _ = store.NextSequence()
}
_ = store.Close()
@@ -155,9 +191,9 @@ func BenchmarkSet(b *testing.B) {
}
func BenchmarkIncr(b *testing.B) {
key := []byte("incr-benchmark-counter")
store, _ := NewGoleveldbStore(testDbPath)
store, _ := New(testDbPath)
key := []byte("incr-benchmark-counter")
for n := 0; n < b.N; n++ {
_, _ = store.Incr(key)
}

View File

@@ -6,5 +6,5 @@ type Store interface {
Get([]byte) ([]byte, error)
Set([]byte, []byte) error
Delete([]byte) error
Incr([]byte) (int, error)
NextSequence() (int, error)
}