diff --git a/store/boltdb.go b/store/boltdb.go new file mode 100644 index 0000000..1d789b7 --- /dev/null +++ b/store/boltdb.go @@ -0,0 +1,32 @@ +// +build !windows + +package store + +import ( + "github.com/siddontang/copier" + "github.com/siddontang/ledisdb/store/boltdb" + "github.com/siddontang/ledisdb/store/driver" +) + +const BoltDBName = "boltdb" + +type BoltDBStore struct { +} + +func (s BoltDBStore) Open(cfg *Config) (driver.IDB, error) { + c := &boltdb.Config{} + copier.Copy(c, cfg) + + return boltdb.Open(c) +} + +func (s BoltDBStore) Repair(cfg *Config) error { + c := &boltdb.Config{} + copier.Copy(c, cfg) + + return boltdb.Repair(c) +} + +func init() { + Register(BoltDBName, BoltDBStore{}) +} diff --git a/store/boltdb/db.go b/store/boltdb/db.go new file mode 100644 index 0000000..e1addf5 --- /dev/null +++ b/store/boltdb/db.go @@ -0,0 +1,146 @@ +package boltdb + +import ( + "github.com/boltdb/bolt" + "github.com/siddontang/ledisdb/store/driver" + "os" + "path" +) + +var bucketName = []byte("ledisdb") + +type Config struct { + Path string `json:"path"` + NoSync bool `json:"nosync"` +} + +type DB struct { + cfg *Config + db *bolt.DB +} + +func Open(cfg *Config) (*DB, error) { + os.MkdirAll(cfg.Path, os.ModePerm) + name := path.Join(cfg.Path, "ledis_bolt.db") + db := new(DB) + var err error + db.db, err = bolt.Open(name, 0600, nil) + if err != nil { + return nil, err + } + + db.db.NoSync = cfg.NoSync + + var tx *bolt.Tx + tx, err = db.db.Begin(true) + if err != nil { + return nil, err + } + + _, err = tx.CreateBucketIfNotExists(bucketName) + if err != nil { + tx.Rollback() + return nil, err + } + + if err = tx.Commit(); err != nil { + return nil, err + } + + return db, nil +} + +func Repair(cfg *Config) error { + return nil +} + +func (db *DB) Close() error { + return db.db.Close() +} + +func (db *DB) Get(key []byte) ([]byte, error) { + var value []byte + + t, err := db.db.Begin(false) + if err != nil { + return nil, err + } + b := t.Bucket(bucketName) + + value = b.Get(key) + err = t.Rollback() + + if err != nil { + return nil, err + } + + if value == nil { + return nil, nil + } else { + return append([]byte{}, value...), nil + } +} + +func (db *DB) Put(key []byte, value []byte) error { + err := db.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(bucketName) + return b.Put(key, value) + }) + return err +} + +func (db *DB) Delete(key []byte) error { + err := db.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(bucketName) + return b.Delete(key) + }) + return err + +} + +func (db *DB) NewIterator() driver.IIterator { + tx, err := db.db.Begin(false) + if err != nil { + return &Iterator{} + } + b := tx.Bucket(bucketName) + + return &Iterator{ + tx: tx, + it: b.Cursor()} +} + +func (db *DB) NewWriteBatch() driver.IWriteBatch { + return driver.NewWriteBatch(db) +} + +func (db *DB) Begin() (driver.Tx, error) { + tx, err := db.db.Begin(true) + if err != nil { + return nil, err + } + + return &Tx{ + tx: tx, + b: tx.Bucket(bucketName), + }, nil +} + +func (db *DB) BatchPut(writes []driver.Write) error { + err := db.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(bucketName) + var err error + for _, w := range writes { + if w.Value == nil { + err = b.Delete(w.Key) + } else { + err = b.Put(w.Key, w.Value) + } + if err != nil { + return err + } + } + return nil + }) + return err +} diff --git a/store/boltdb/iterator.go b/store/boltdb/iterator.go new file mode 100644 index 0000000..aea7508 --- /dev/null +++ b/store/boltdb/iterator.go @@ -0,0 +1,50 @@ +package boltdb + +import ( + "github.com/boltdb/bolt" +) + +type Iterator struct { + tx *bolt.Tx + it *bolt.Cursor + key []byte + value []byte +} + +func (it *Iterator) Close() error { + if it.tx != nil { + return it.tx.Rollback() + } else { + return nil + } +} + +func (it *Iterator) First() { + it.key, it.value = it.it.First() +} + +func (it *Iterator) Last() { + it.key, it.value = it.it.Last() +} + +func (it *Iterator) Seek(key []byte) { + it.key, it.value = it.it.Seek(key) +} + +func (it *Iterator) Next() { + it.key, it.value = it.it.Next() +} +func (it *Iterator) Prev() { + it.key, it.value = it.it.Prev() +} + +func (it *Iterator) Valid() bool { + return !(it.key == nil && it.value == nil) +} + +func (it *Iterator) Key() []byte { + return it.key +} +func (it *Iterator) Value() []byte { + return it.value +} diff --git a/store/boltdb/tx.go b/store/boltdb/tx.go new file mode 100644 index 0000000..058a5f7 --- /dev/null +++ b/store/boltdb/tx.go @@ -0,0 +1,57 @@ +package boltdb + +import ( + "github.com/boltdb/bolt" + "github.com/siddontang/ledisdb/store/driver" +) + +type Tx struct { + tx *bolt.Tx + b *bolt.Bucket +} + +func (t *Tx) Get(key []byte) ([]byte, error) { + return t.b.Get(key), nil +} + +func (t *Tx) Put(key []byte, value []byte) error { + return t.b.Put(key, value) +} + +func (t *Tx) Delete(key []byte) error { + return t.b.Delete(key) +} + +func (t *Tx) NewIterator() driver.IIterator { + return &Iterator{ + tx: nil, + it: t.b.Cursor(), + } +} + +func (t *Tx) NewWriteBatch() driver.IWriteBatch { + return driver.NewWriteBatch(t) +} + +func (t *Tx) BatchPut(writes []driver.Write) error { + var err error + for _, w := range writes { + if w.Value == nil { + err = t.b.Delete(w.Key) + } else { + err = t.b.Put(w.Key, w.Value) + } + if err != nil { + return err + } + } + return nil +} + +func (t *Tx) Rollback() error { + return t.tx.Rollback() +} + +func (t *Tx) Commit() error { + return t.tx.Commit() +} diff --git a/store/boltdb_test.go b/store/boltdb_test.go new file mode 100644 index 0000000..6617644 --- /dev/null +++ b/store/boltdb_test.go @@ -0,0 +1,38 @@ +package store + +import ( + "os" + "testing" +) + +func newTestBoltDB() *DB { + cfg := new(Config) + cfg.Name = BoltDBName + cfg.Path = "/tmp/testdb/boltdb" + + os.RemoveAll(cfg.Path) + + db, err := Open(cfg) + if err != nil { + println(err.Error()) + panic(err) + } + + return db +} + +func TestBoltDB(t *testing.T) { + db := newTestBoltDB() + + testStore(db, t) + + db.Close() +} + +func TestBoltDBTx(t *testing.T) { + db := newTestBoltDB() + + testTx(db, t) + + db.Close() +} diff --git a/store/config.go b/store/config.go index 16666a2..73afad4 100644 --- a/store/config.go +++ b/store/config.go @@ -14,4 +14,7 @@ type Config struct { //for lmdb MapSize int `json:"map_size"` + + //for boltdb + NoSync bool `json:"nosync"` } diff --git a/store/mdb/batch.go b/store/driver/batch.go similarity index 75% rename from store/mdb/batch.go rename to store/driver/batch.go index 7c99be2..6b79c21 100644 --- a/store/mdb/batch.go +++ b/store/driver/batch.go @@ -1,6 +1,6 @@ -package mdb +package driver -type batchPut interface { +type BatchPuter interface { BatchPut([]Write) error } @@ -10,7 +10,7 @@ type Write struct { } type WriteBatch struct { - batch batchPut + batch BatchPuter wb []Write } @@ -33,3 +33,7 @@ func (w *WriteBatch) Rollback() error { w.wb = w.wb[0:0] return nil } + +func NewWriteBatch(puter BatchPuter) IWriteBatch { + return &WriteBatch{puter, []Write{}} +} diff --git a/store/mdb/mdb.go b/store/mdb/mdb.go index d06f413..88c4856 100644 --- a/store/mdb/mdb.go +++ b/store/mdb/mdb.go @@ -85,7 +85,7 @@ func (db MDB) Put(key, value []byte) error { return itr.Close() } -func (db MDB) BatchPut(writes []Write) error { +func (db MDB) BatchPut(writes []driver.Write) error { itr := db.iterator(false) for _, w := range writes { @@ -258,7 +258,7 @@ func (db MDB) NewIterator() driver.IIterator { } func (db MDB) NewWriteBatch() driver.IWriteBatch { - return &WriteBatch{&db, []Write{}} + return driver.NewWriteBatch(db) } func (db MDB) Begin() (driver.Tx, error) { diff --git a/store/mdb/tx.go b/store/mdb/tx.go index 4fb29b4..b78f488 100644 --- a/store/mdb/tx.go +++ b/store/mdb/tx.go @@ -45,10 +45,10 @@ func (t *Tx) newIterator() *MDBIterator { } func (t *Tx) NewWriteBatch() driver.IWriteBatch { - return &WriteBatch{t, []Write{}} + return driver.NewWriteBatch(t) } -func (t *Tx) BatchPut(writes []Write) error { +func (t *Tx) BatchPut(writes []driver.Write) error { itr := t.newIterator() for _, w := range writes {