mirror of https://github.com/siddontang/go.git
add leveldb wrapper
This commit is contained in:
parent
0a9ad31602
commit
b9100d77aa
|
@ -0,0 +1,34 @@
|
|||
package leveldb
|
||||
|
||||
import (
|
||||
"github.com/jmhodges/levigo"
|
||||
)
|
||||
|
||||
type WriteBatch struct {
|
||||
db *DB
|
||||
wb *levigo.WriteBatch
|
||||
}
|
||||
|
||||
func (wb *WriteBatch) Put(key, value []byte) {
|
||||
wb.wb.Put(key, value)
|
||||
}
|
||||
|
||||
func (wb *WriteBatch) Delete(key []byte) {
|
||||
wb.wb.Delete(key)
|
||||
}
|
||||
|
||||
func (wb *WriteBatch) Commit() error {
|
||||
err := wb.db.db.Write(wb.db.writeOpts, wb.wb)
|
||||
wb.close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (wb *WriteBatch) Rollback() {
|
||||
wb.wb.Clear()
|
||||
wb.close()
|
||||
}
|
||||
|
||||
func (wb *WriteBatch) close() {
|
||||
wb.wb.Close()
|
||||
wb.wb = nil
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
|
||||
#refer https://github.com/norton/lets/blob/master/c_src/build_deps.sh
|
||||
|
||||
#install leveldb and snappy
|
||||
|
||||
#you must set your own snappy and leveldb source directory
|
||||
SNAPPY_SRC=./snappy
|
||||
LEVELDB_SRC=./leveldb
|
||||
|
||||
SNAPPY_DIR=/usr/local/snappy
|
||||
LEVELDB_DIR=/usr/local/leveldb
|
||||
|
||||
if [ ! -f $SNAPPY_DIR/lib/libsnappy.a ]; then
|
||||
(cd $SNAPPY_SRC && \
|
||||
./configure --prefix=$SNAPPY_DIR && \
|
||||
make && \
|
||||
make install)
|
||||
else
|
||||
echo "skip install snappy"
|
||||
fi
|
||||
|
||||
if [ ! -f $LEVELDB_DIR/lib/libleveldb.a ]; then
|
||||
(cd $LEVELDB_SRC && \
|
||||
echo "echo \"PLATFORM_CFLAGS+=-I$SNAPPY_DIR/include\" >> build_config.mk" >> build_detect_platform &&
|
||||
echo "echo \"PLATFORM_CXXFLAGS+=-I$SNAPPY_DIR/include\" >> build_config.mk" >> build_detect_platform &&
|
||||
echo "echo \"PLATFORM_LDFLAGS+=-L $SNAPPY_DIR/lib -lsnappy\" >> build_config.mk" >> build_detect_platform &&
|
||||
make SNAPPY=1 && \
|
||||
make && \
|
||||
mkdir -p $LEVELDB_DIR/include/leveldb && \
|
||||
install include/leveldb/*.h $LEVELDB_DIR/include/leveldb && \
|
||||
mkdir -p $LEVELDB_DIR/lib && \
|
||||
cp -af libleveldb.* $LEVELDB_DIR/lib)
|
||||
else
|
||||
echo "skip install leveldb"
|
||||
fi
|
||||
|
||||
export CGO_CFLAGS="-I$LEVELDB_DIR/include -I$SNAPPY_DIR/include"
|
||||
export CGO_LDFLAGS="-L$LEVELDB_DIR/lib -L$SNAPPY_DIR/lib -lsnappy"
|
||||
|
||||
go get github.com/jmhodges/levigo
|
|
@ -0,0 +1,154 @@
|
|||
package leveldb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/jmhodges/levigo"
|
||||
)
|
||||
|
||||
const defaultFilterBits int = 10
|
||||
|
||||
type Config struct {
|
||||
Path string `json:"path"`
|
||||
|
||||
Compression bool `json:"compression"`
|
||||
|
||||
BlockSize int `json:"block_size"`
|
||||
WriteBufferSize int `json:"write_buffer_size"`
|
||||
CacheSize int `json:"cache_size"`
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
cfg *Config
|
||||
db *levigo.DB
|
||||
|
||||
opts *levigo.Options
|
||||
|
||||
//for default read and write options
|
||||
readOpts *levigo.ReadOptions
|
||||
writeOpts *levigo.WriteOptions
|
||||
iteratorOpts *levigo.ReadOptions
|
||||
|
||||
cache *levigo.Cache
|
||||
|
||||
filter *levigo.FilterPolicy
|
||||
}
|
||||
|
||||
func Open(configJson json.RawMessage) (*DB, error) {
|
||||
cfg := new(Config)
|
||||
err := json.Unmarshal(configJson, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := new(DB)
|
||||
db.cfg = cfg
|
||||
|
||||
db.opts = db.initOptions(cfg)
|
||||
|
||||
db.readOpts = levigo.NewReadOptions()
|
||||
db.writeOpts = levigo.NewWriteOptions()
|
||||
db.iteratorOpts = levigo.NewReadOptions()
|
||||
db.iteratorOpts.SetFillCache(false)
|
||||
|
||||
db.db, err = levigo.Open(cfg.Path, db.opts)
|
||||
return db, err
|
||||
}
|
||||
|
||||
func (db *DB) initOptions(cfg *Config) *levigo.Options {
|
||||
opts := levigo.NewOptions()
|
||||
|
||||
opts.SetCreateIfMissing(true)
|
||||
|
||||
if cfg.CacheSize > 0 {
|
||||
db.cache = levigo.NewLRUCache(cfg.CacheSize)
|
||||
opts.SetCache(db.cache)
|
||||
}
|
||||
|
||||
//we must use bloomfilter
|
||||
db.filter = levigo.NewBloomFilter(defaultFilterBits)
|
||||
opts.SetFilterPolicy(db.filter)
|
||||
|
||||
if !cfg.Compression {
|
||||
opts.SetCompression(levigo.NoCompression)
|
||||
}
|
||||
|
||||
blockSize := cfg.BlockSize * 1024
|
||||
if blockSize > 0 {
|
||||
opts.SetBlockSize(blockSize)
|
||||
}
|
||||
|
||||
writeBufferSize := cfg.WriteBufferSize * 1024 * 1024
|
||||
if writeBufferSize > 0 {
|
||||
opts.SetWriteBufferSize(writeBufferSize)
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (db *DB) Close() {
|
||||
db.opts.Close()
|
||||
|
||||
if db.cache != nil {
|
||||
db.cache.Close()
|
||||
}
|
||||
|
||||
if db.filter != nil {
|
||||
db.filter.Close()
|
||||
}
|
||||
|
||||
db.readOpts.Close()
|
||||
db.writeOpts.Close()
|
||||
db.iteratorOpts.Close()
|
||||
|
||||
db.db.Close()
|
||||
db.db = nil
|
||||
}
|
||||
|
||||
func (db *DB) Destroy() {
|
||||
db.Close()
|
||||
opts := levigo.NewOptions()
|
||||
defer opts.Close()
|
||||
|
||||
levigo.DestroyDatabase(db.cfg.Path, opts)
|
||||
}
|
||||
|
||||
func (db *DB) Put(key, value []byte) error {
|
||||
return db.db.Put(db.writeOpts, key, value)
|
||||
}
|
||||
|
||||
func (db *DB) Get(key []byte) ([]byte, error) {
|
||||
return db.db.Get(db.readOpts, key)
|
||||
}
|
||||
|
||||
func (db *DB) Delete(key []byte) error {
|
||||
return db.db.Delete(db.writeOpts, key)
|
||||
}
|
||||
|
||||
func (db *DB) NewWriteBatch() *WriteBatch {
|
||||
wb := new(WriteBatch)
|
||||
wb.wb = levigo.NewWriteBatch()
|
||||
wb.db = db
|
||||
return wb
|
||||
}
|
||||
|
||||
//like c++ iterator, [begin, end)
|
||||
//begin should less than end
|
||||
//if begin is nil, we will seek to first
|
||||
//if end is nil, we will next until read last
|
||||
//limit <= 0, no limit
|
||||
func (db *DB) Iterator(begin []byte, end []byte, limit int) *Iterator {
|
||||
return newIterator(db, db.iteratorOpts, begin, end, limit, forward)
|
||||
}
|
||||
|
||||
//like c++ reverse_iterator, [rbegin, rend)
|
||||
//rbegin should bigger than rend
|
||||
//if rbegin is nil, we will seek to last
|
||||
//if end is nil, we will next until read first
|
||||
//limit <= 0, no limit
|
||||
func (db *DB) ReverseIterator(rbegin []byte, rend []byte, limit int) *Iterator {
|
||||
return newIterator(db, db.iteratorOpts, rbegin, rend, limit, backward)
|
||||
}
|
||||
|
||||
func (db *DB) NewSnapshot() *Snapshot {
|
||||
return newSnapshot(db)
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package leveldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/jmhodges/levigo"
|
||||
)
|
||||
|
||||
const forward uint8 = 0
|
||||
const backward uint8 = 1
|
||||
|
||||
type Iterator struct {
|
||||
it *levigo.Iterator
|
||||
|
||||
start []byte
|
||||
stop []byte
|
||||
limit int
|
||||
|
||||
step int
|
||||
|
||||
//0 for forward, 1 for backward
|
||||
direction uint8
|
||||
}
|
||||
|
||||
func newIterator(db *DB, opts *levigo.ReadOptions, start []byte, stop []byte, limit int, direction uint8) *Iterator {
|
||||
it := new(Iterator)
|
||||
it.it = db.db.NewIterator(opts)
|
||||
|
||||
it.start = start
|
||||
it.stop = stop
|
||||
it.limit = limit
|
||||
it.direction = direction
|
||||
it.step = 0
|
||||
|
||||
if start == nil {
|
||||
if direction == forward {
|
||||
it.it.SeekToFirst()
|
||||
} else {
|
||||
it.it.SeekToLast()
|
||||
}
|
||||
} else {
|
||||
it.it.Seek(start)
|
||||
|
||||
if it.Valid() && !bytes.Equal(it.Key(), start) {
|
||||
//for forward, key is the next bigger than start
|
||||
//for backward, key is the next bigger than start, so must go prev
|
||||
if direction == backward {
|
||||
it.it.Prev()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
func (it *Iterator) Valid() bool {
|
||||
if !it.it.Valid() {
|
||||
return false
|
||||
}
|
||||
|
||||
if it.limit > 0 && it.step >= it.limit {
|
||||
return false
|
||||
}
|
||||
|
||||
if it.direction == forward {
|
||||
if it.stop != nil && bytes.Compare(it.Key(), it.stop) >= 0 {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if it.stop != nil && bytes.Compare(it.Key(), it.stop) <= 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (it *Iterator) GetError() error {
|
||||
return it.it.GetError()
|
||||
}
|
||||
|
||||
func (it *Iterator) Next() {
|
||||
it.step++
|
||||
|
||||
if it.direction == forward {
|
||||
it.it.Next()
|
||||
} else {
|
||||
it.it.Prev()
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Iterator) Key() []byte {
|
||||
return it.it.Key()
|
||||
}
|
||||
|
||||
func (it *Iterator) Value() []byte {
|
||||
return it.it.Value()
|
||||
}
|
||||
|
||||
func (it *Iterator) Close() {
|
||||
it.it.Close()
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
package leveldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testConfigJson = []byte(`
|
||||
{
|
||||
"path" : "./testdb",
|
||||
"compression":true,
|
||||
"block_size" : 32,
|
||||
"write_buffer_size" : 2,
|
||||
"cache_size" : 20
|
||||
}
|
||||
`)
|
||||
|
||||
var testOnce sync.Once
|
||||
var testDB *DB
|
||||
|
||||
func getTestDB() *DB {
|
||||
f := func() {
|
||||
var err error
|
||||
testDB, err = Open(testConfigJson)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
testOnce.Do(f)
|
||||
return testDB
|
||||
}
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
key := []byte("key")
|
||||
value := []byte("hello world")
|
||||
if err := db.Put(key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, err := db.Get(key); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !bytes.Equal(v, value) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
|
||||
if err := db.Delete(key); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, err := db.Get(key); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if v != nil {
|
||||
t.Fatal("must nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatch(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
key1 := []byte("key1")
|
||||
key2 := []byte("key2")
|
||||
|
||||
value := []byte("hello world")
|
||||
|
||||
db.Put(key1, value)
|
||||
db.Put(key2, value)
|
||||
|
||||
wb := db.NewWriteBatch()
|
||||
wb.Delete(key2)
|
||||
wb.Put(key1, []byte("hello world2"))
|
||||
|
||||
if err := wb.Commit(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, err := db.Get(key2); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if v != nil {
|
||||
t.Fatal("must nil")
|
||||
}
|
||||
|
||||
if v, err := db.Get(key1); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(v) != "hello world2" {
|
||||
t.Fatal(string(v))
|
||||
}
|
||||
|
||||
wb = db.NewWriteBatch()
|
||||
wb.Delete(key1)
|
||||
|
||||
wb.Rollback()
|
||||
|
||||
if v, err := db.Get(key1); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(v) != "hello world2" {
|
||||
t.Fatal(string(v))
|
||||
}
|
||||
|
||||
db.Delete(key1)
|
||||
}
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
db := getTestDB()
|
||||
for it := db.Iterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
db.Delete(it.Key())
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
key := []byte(fmt.Sprintf("key_%d", i))
|
||||
value := []byte(fmt.Sprintf("value_%d", i))
|
||||
db.Put(key, value)
|
||||
}
|
||||
|
||||
step := 0
|
||||
var it *Iterator
|
||||
for it = db.Iterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
|
||||
if string(key) != fmt.Sprintf("key_%d", step) {
|
||||
t.Fatal(string(key), step)
|
||||
}
|
||||
|
||||
if string(value) != fmt.Sprintf("value_%d", step) {
|
||||
t.Fatal(string(value), step)
|
||||
}
|
||||
|
||||
step++
|
||||
}
|
||||
|
||||
it.Close()
|
||||
|
||||
step = 2
|
||||
for it = db.Iterator([]byte("key_2"), nil, 3); it.Valid(); it.Next() {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
|
||||
if string(key) != fmt.Sprintf("key_%d", step) {
|
||||
t.Fatal(string(key), step)
|
||||
}
|
||||
|
||||
if string(value) != fmt.Sprintf("value_%d", step) {
|
||||
t.Fatal(string(value), step)
|
||||
}
|
||||
|
||||
step++
|
||||
}
|
||||
it.Close()
|
||||
|
||||
if step != 5 {
|
||||
t.Fatal("invalid step", step)
|
||||
}
|
||||
|
||||
step = 2
|
||||
for it = db.Iterator([]byte("key_2"), []byte("key_5"), 0); it.Valid(); it.Next() {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
|
||||
if string(key) != fmt.Sprintf("key_%d", step) {
|
||||
t.Fatal(string(key), step)
|
||||
}
|
||||
|
||||
if string(value) != fmt.Sprintf("value_%d", step) {
|
||||
t.Fatal(string(value), step)
|
||||
}
|
||||
|
||||
step++
|
||||
}
|
||||
it.Close()
|
||||
|
||||
if step != 5 {
|
||||
t.Fatal("invalid step", step)
|
||||
}
|
||||
|
||||
step = 2
|
||||
for it = db.Iterator([]byte("key_5"), []byte("key_2"), 0); it.Valid(); it.Next() {
|
||||
step++
|
||||
}
|
||||
it.Close()
|
||||
|
||||
if step != 2 {
|
||||
t.Fatal("must 0")
|
||||
}
|
||||
|
||||
step = 9
|
||||
for it = db.ReverseIterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
|
||||
if string(key) != fmt.Sprintf("key_%d", step) {
|
||||
t.Fatal(string(key), step)
|
||||
}
|
||||
|
||||
if string(value) != fmt.Sprintf("value_%d", step) {
|
||||
t.Fatal(string(value), step)
|
||||
}
|
||||
|
||||
step--
|
||||
}
|
||||
it.Close()
|
||||
|
||||
step = 5
|
||||
for it = db.ReverseIterator([]byte("key_5"), nil, 3); it.Valid(); it.Next() {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
|
||||
if string(key) != fmt.Sprintf("key_%d", step) {
|
||||
t.Fatal(string(key), step)
|
||||
}
|
||||
|
||||
if string(value) != fmt.Sprintf("value_%d", step) {
|
||||
t.Fatal(string(value), step)
|
||||
}
|
||||
|
||||
step--
|
||||
}
|
||||
it.Close()
|
||||
|
||||
if step != 2 {
|
||||
t.Fatal("invalid step", step)
|
||||
}
|
||||
|
||||
step = 5
|
||||
for it = db.ReverseIterator([]byte("key_5"), []byte("key_2"), 0); it.Valid(); it.Next() {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
|
||||
if string(key) != fmt.Sprintf("key_%d", step) {
|
||||
t.Fatal(string(key), step)
|
||||
}
|
||||
|
||||
if string(value) != fmt.Sprintf("value_%d", step) {
|
||||
t.Fatal(string(value), step)
|
||||
}
|
||||
|
||||
step--
|
||||
}
|
||||
it.Close()
|
||||
|
||||
if step != 2 {
|
||||
t.Fatal("invalid step", step)
|
||||
}
|
||||
|
||||
step = 5
|
||||
for it = db.ReverseIterator([]byte("key_2"), []byte("key_5"), 0); it.Valid(); it.Next() {
|
||||
step--
|
||||
}
|
||||
it.Close()
|
||||
|
||||
if step != 5 {
|
||||
t.Fatal("must 5")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIterator_2(t *testing.T) {
|
||||
db := getTestDB()
|
||||
for it := db.Iterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
db.Delete(it.Key())
|
||||
}
|
||||
|
||||
db.Put([]byte("key_1"), []byte("value_1"))
|
||||
db.Put([]byte("key_7"), []byte("value_9"))
|
||||
db.Put([]byte("key_9"), []byte("value_9"))
|
||||
|
||||
it := db.Iterator([]byte("key_0"), []byte("key_8"), 0)
|
||||
if !it.Valid() {
|
||||
t.Fatal("must valid")
|
||||
}
|
||||
|
||||
if string(it.Key()) != "key_1" {
|
||||
t.Fatal(string(it.Key()))
|
||||
}
|
||||
|
||||
it = db.ReverseIterator([]byte("key_8"), []byte("key_0"), 0)
|
||||
if !it.Valid() {
|
||||
t.Fatal("must valid")
|
||||
}
|
||||
|
||||
if string(it.Key()) != "key_7" {
|
||||
t.Fatal(string(it.Key()))
|
||||
}
|
||||
|
||||
for it := db.Iterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
db.Delete(it.Key())
|
||||
}
|
||||
|
||||
it = db.Iterator([]byte("key_0"), []byte("key_8"), 0)
|
||||
if it.Valid() {
|
||||
t.Fatal("must not valid")
|
||||
}
|
||||
|
||||
it = db.ReverseIterator([]byte("key_8"), []byte("key_0"), 0)
|
||||
if it.Valid() {
|
||||
t.Fatal("must not valid")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSnapshot(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
key := []byte("key")
|
||||
value := []byte("hello world")
|
||||
|
||||
db.Put(key, value)
|
||||
|
||||
s := db.NewSnapshot()
|
||||
defer s.Close()
|
||||
|
||||
db.Put(key, []byte("hello world2"))
|
||||
|
||||
if v, err := s.Get(key); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(v) != string(value) {
|
||||
t.Fatal(string(v))
|
||||
}
|
||||
|
||||
found := false
|
||||
for it := s.Iterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
if string(it.Key()) == string(key) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("must found")
|
||||
}
|
||||
|
||||
found = false
|
||||
for it := s.ReverseIterator(nil, nil, 0); it.Valid(); it.Next() {
|
||||
if string(it.Key()) == string(key) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("must found")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDestroy(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
db.Destroy()
|
||||
|
||||
if _, err := os.Stat(db.cfg.Path); !os.IsNotExist(err) {
|
||||
t.Fatal("must not exist")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
a leveldb wrapper for levigo
|
||||
|
||||
simplify use leveldb in go
|
||||
|
||||
# Install
|
||||
|
||||
+ download leveldb and snappy source, uncompress and set source directory in build_deps.sh
|
||||
+ . ./build_deps.sh
|
|
@ -0,0 +1,47 @@
|
|||
package leveldb
|
||||
|
||||
import (
|
||||
"github.com/jmhodges/levigo"
|
||||
)
|
||||
|
||||
type Snapshot struct {
|
||||
db *DB
|
||||
s *levigo.Snapshot
|
||||
readOpts *levigo.ReadOptions
|
||||
iteratorOpts *levigo.ReadOptions
|
||||
}
|
||||
|
||||
func newSnapshot(db *DB) *Snapshot {
|
||||
s := new(Snapshot)
|
||||
s.db = db
|
||||
s.s = db.db.NewSnapshot()
|
||||
|
||||
s.readOpts = levigo.NewReadOptions()
|
||||
s.readOpts.SetSnapshot(s.s)
|
||||
|
||||
s.iteratorOpts = levigo.NewReadOptions()
|
||||
s.iteratorOpts.SetSnapshot(s.s)
|
||||
s.iteratorOpts.SetFillCache(false)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Snapshot) Close() {
|
||||
s.db.db.ReleaseSnapshot(s.s)
|
||||
|
||||
s.iteratorOpts.Close()
|
||||
s.readOpts.Close()
|
||||
}
|
||||
|
||||
func (s *Snapshot) Get(key []byte) ([]byte, error) {
|
||||
return s.db.db.Get(s.readOpts, key)
|
||||
}
|
||||
|
||||
//same as db iterator and reverse iterator
|
||||
func (s *Snapshot) Iterator(begin []byte, end []byte, limit int) *Iterator {
|
||||
return newIterator(s.db, s.iteratorOpts, begin, end, limit, forward)
|
||||
}
|
||||
|
||||
func (s *Snapshot) ReverseIterator(rbegin []byte, rend []byte, limit int) *Iterator {
|
||||
return newIterator(s.db, s.iteratorOpts, rbegin, rend, limit, backward)
|
||||
}
|
Loading…
Reference in New Issue