forked from mirror/ledisdb
Merge branch 'storage-feature' into develop
This commit is contained in:
commit
efa8e850f5
|
@ -2,3 +2,4 @@ build
|
||||||
*.pyc
|
*.pyc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
nohup.out
|
nohup.out
|
||||||
|
build_config.mk
|
11
Makefile
11
Makefile
|
@ -1,11 +1,16 @@
|
||||||
|
$(shell ./bootstrap.sh)
|
||||||
|
|
||||||
|
$(shell ./build_config.sh build_config.mk ./)
|
||||||
|
|
||||||
|
include build_config.mk
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go install ./...
|
go install -tags '$(GO_BUILD_TAGS)' ./...
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean -i ./...
|
go clean -i ./...
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test ./...
|
go test -tags $(GO_BUILD_TAGS) ./...
|
||||||
go test -race ./...
|
|
||||||
|
|
52
README.md
52
README.md
|
@ -1,11 +1,14 @@
|
||||||
# LedisDB
|
# LedisDB
|
||||||
|
|
||||||
Ledisdb is a high performance NoSQL like Redis based on LevelDB written by go. It supports some advanced data structure like kv, list, hash and zset, and may be alternative for Redis.
|
Ledisdb is a high performance NoSQL like Redis written by go. It supports some advanced data structure like kv, list, hash and zset, and may be alternative for Redis.
|
||||||
|
|
||||||
|
LedisDB now supports multi database as backend to store data, you can test and choose the proper one for you.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
+ Rich advanced data structure: KV, List, Hash, ZSet, Bit.
|
+ Rich advanced data structure: KV, List, Hash, ZSet, Bit.
|
||||||
+ Uses leveldb to store lots of data, over the memory limit.
|
+ Stores lots of data, over the memory limit.
|
||||||
|
+ Various backend database to use: LevelDB, goleveldb, LMDB.
|
||||||
+ Supports expiration and ttl.
|
+ Supports expiration and ttl.
|
||||||
+ Redis clients, like redis-cli, are supported directly.
|
+ Redis clients, like redis-cli, are supported directly.
|
||||||
+ Multi client API supports, including Golang, Python, Lua(Openresty).
|
+ Multi client API supports, including Golang, Python, Lua(Openresty).
|
||||||
|
@ -15,7 +18,7 @@ Ledisdb is a high performance NoSQL like Redis based on LevelDB written by go. I
|
||||||
|
|
||||||
## Build and Install
|
## Build and Install
|
||||||
|
|
||||||
+ Create a workspace and checkout ledisdb source
|
Create a workspace and checkout ledisdb source
|
||||||
|
|
||||||
mkdir $WORKSPACE
|
mkdir $WORKSPACE
|
||||||
cd $WORKSPACE
|
cd $WORKSPACE
|
||||||
|
@ -23,7 +26,11 @@ Ledisdb is a high performance NoSQL like Redis based on LevelDB written by go. I
|
||||||
|
|
||||||
cd src/github.com/siddontang/ledisdb
|
cd src/github.com/siddontang/ledisdb
|
||||||
|
|
||||||
+ Install leveldb and snappy, if you have installed, skip.
|
make
|
||||||
|
|
||||||
|
## LevelDB support
|
||||||
|
|
||||||
|
+ Install leveldb and snappy.
|
||||||
|
|
||||||
LedisDB supplies a simple shell to install leveldb and snappy:
|
LedisDB supplies a simple shell to install leveldb and snappy:
|
||||||
|
|
||||||
|
@ -35,16 +42,43 @@ Ledisdb is a high performance NoSQL like Redis based on LevelDB written by go. I
|
||||||
|
|
||||||
+ Set LEVELDB_DIR and SNAPPY_DIR to the actual install path in dev.sh.
|
+ Set LEVELDB_DIR and SNAPPY_DIR to the actual install path in dev.sh.
|
||||||
|
|
||||||
+ Then:
|
+ ```make```
|
||||||
|
|
||||||
. ./bootstap.sh
|
## RocksDB support
|
||||||
. ./dev.sh
|
|
||||||
|
|
||||||
go install ./...
|
+ Install rocksdb and snappy first.
|
||||||
|
|
||||||
|
LedisDB has not supplied a simple shell to install, maybe it will later.
|
||||||
|
|
||||||
|
+ Set ROCKSDB_DIR and SNAPPY_DIR to the actual install path in dev.sh.
|
||||||
|
|
||||||
|
+ ```make```
|
||||||
|
|
||||||
|
## Choose store database
|
||||||
|
|
||||||
|
LedisDB now supports goleveldb, lmdb, leveldb, rocksdb, it will choose goleveldb as default to store data if you not set.
|
||||||
|
|
||||||
|
Choosing a store database to use is very simple, you have two ways:
|
||||||
|
|
||||||
|
+ Set in server config file
|
||||||
|
|
||||||
|
"db" : {
|
||||||
|
"name" : "leveldb"
|
||||||
|
}
|
||||||
|
|
||||||
|
+ Set in command flag
|
||||||
|
|
||||||
|
ledis-server -config=/etc/ledis.json -db_name=leveldb
|
||||||
|
|
||||||
|
Flag command set will overwrite config set.
|
||||||
|
|
||||||
|
**Caveat**
|
||||||
|
|
||||||
|
You must known that changing store database runtime is very dangerous, LedisDB will not guarantee the data validation if you do it.
|
||||||
|
|
||||||
## Server Example
|
## Server Example
|
||||||
|
|
||||||
./ledis-server -config=/etc/ledis.json
|
ledis-server -config=/etc/ledis.json
|
||||||
|
|
||||||
//another shell
|
//another shell
|
||||||
ledis-cli -p 6380
|
ledis-cli -p 6380
|
||||||
|
|
|
@ -5,3 +5,6 @@
|
||||||
go get github.com/siddontang/go-log/log
|
go get github.com/siddontang/go-log/log
|
||||||
go get github.com/siddontang/go-snappy/snappy
|
go get github.com/siddontang/go-snappy/snappy
|
||||||
go get github.com/siddontang/copier
|
go get github.com/siddontang/copier
|
||||||
|
|
||||||
|
go get github.com/syndtr/goleveldb/leveldb
|
||||||
|
go get github.com/influxdb/gomdb
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
OUTPUT=$1
|
||||||
|
PREFIX=$2
|
||||||
|
if test -z "$OUTPUT" || test -z "$PREFIX"; then
|
||||||
|
echo "usage: $0 <output-filename> <directory_prefix>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete existing output, if it exists
|
||||||
|
rm -f $OUTPUT
|
||||||
|
touch $OUTPUT
|
||||||
|
|
||||||
|
source ./dev.sh
|
||||||
|
|
||||||
|
echo "GO_BUILD_TAGS=$GO_BUILD_TAGS" >> $OUTPUT
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = leveldb.Repair(cfg.NewDBConfig()); err != nil {
|
if err = store.Repair(cfg.NewDBConfig()); err != nil {
|
||||||
println("repair error: ", err.Error())
|
println("repair error: ", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var configFile = flag.String("config", "/etc/ledis.json", "ledisdb config file")
|
var configFile = flag.String("config", "/etc/ledis.json", "ledisdb config file")
|
||||||
|
var dbName = flag.String("db_name", "", "select a db to use, it will overwrite the config's db name")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
@ -27,6 +28,10 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(*dbName) > 0 {
|
||||||
|
cfg.DB.Name = *dbName
|
||||||
|
}
|
||||||
|
|
||||||
var app *server.App
|
var app *server.App
|
||||||
app, err = server.NewApp(cfg)
|
app, err = server.NewApp(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
62
dev.sh
62
dev.sh
|
@ -1,19 +1,18 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
export VTTOP=$(pwd)
|
export LEDISTOP=$(pwd)
|
||||||
export VTROOT="${VTROOT:-${VTTOP/\/src\/github.com\/siddontang\/ledisdb/}}"
|
export LEDISROOT="${LEDISROOT:-${LEDISTOP/\/src\/github.com\/siddontang\/ledisdb/}}"
|
||||||
# VTTOP sanity check
|
# LEDISTOP sanity check
|
||||||
if [[ "$VTTOP" == "${VTTOP/\/src\/github.com\/siddontang\/ledisdb/}" ]]; then
|
if [[ "$LEDISTOP" == "${LEDISTOP/\/src\/github.com\/siddontang\/ledisdb/}" ]]; then
|
||||||
echo "WARNING: VTTOP($VTTOP) does not contain src/github.com/siddontang/ledisdb"
|
echo "WARNING: LEDISTOP($LEDISTOP) does not contain src/github.com/siddontang/ledisdb"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
#default snappy and leveldb install path
|
#default snappy and leveldb install path
|
||||||
#you may change yourself
|
#you may change yourself
|
||||||
|
|
||||||
SNAPPY_DIR=/usr/local/snappy
|
SNAPPY_DIR=/usr/local/snappy
|
||||||
LEVELDB_DIR=/usr/local/leveldb
|
LEVELDB_DIR=/usr/local/leveldb
|
||||||
|
ROCKSDB_DIR=/usr/local/rocksdb
|
||||||
|
|
||||||
function add_path()
|
function add_path()
|
||||||
{
|
{
|
||||||
|
@ -26,16 +25,45 @@ function add_path()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
export GOPATH=$(add_path $GOPATH $VTROOT)
|
export GOPATH=$(add_path $GOPATH $LEDISROOT)
|
||||||
|
|
||||||
export CGO_CFLAGS="-I$LEVELDB_DIR/include -I$SNAPPY_DIR/include"
|
GO_BUILD_TAGS=
|
||||||
export CGO_CXXFLAGS="-I$LEVELDB_DIR/include -I$SNAPPY_DIR/include"
|
CGO_CFLAGS=
|
||||||
export CGO_LDFLAGS="-L$LEVELDB_DIR/lib -L$SNAPPY_DIR/lib -lsnappy"
|
CGO_CXXFLAGS=
|
||||||
|
CGO_LDFLAGS=
|
||||||
|
|
||||||
#for linux, use LD_LIBRARY_PATH
|
# check snappy
|
||||||
export LD_LIBRARY_PATH=$(add_path $LD_LIBRARY_PATH $SNAPPY_DIR/lib)
|
if [ -f $SNAPPY_DIR/lib/libsnappy.a ]; then
|
||||||
export LD_LIBRARY_PATH=$(add_path $LD_LIBRARY_PATH $LEVELDB_DIR/lib)
|
CGO_CFLAGS="$CGO_CFLAGS -I$SNAPPY_DIR/include"
|
||||||
|
CGO_CXXFLAGS="$CGO_CXXFLAGS -I$SNAPPY_DIR/include"
|
||||||
|
CGO_LDFLAGS="$CGO_LDFLAGS -L$SNAPPY_DIR/lib -lsnappy"
|
||||||
|
LD_LIBRARY_PATH=$(add_path $LD_LIBRARY_PATH $SNAPPY_DIR/lib)
|
||||||
|
DYLD_LIBRARY_PATH=$(add_path $DYLD_LIBRARY_PATH $SNAPPY_DIR/lib)
|
||||||
|
fi
|
||||||
|
|
||||||
#for macos, use DYLD_LIBRARY_PATH
|
# check leveldb
|
||||||
export DYLD_LIBRARY_PATH=$(add_path $DYLD_LIBRARY_PATH $SNAPPY_DIR/lib)
|
if [ -f $LEVELDB_DIR/lib/libleveldb.a ]; then
|
||||||
export DYLD_LIBRARY_PATH=$(add_path $DYLD_LIBRARY_PATH $LEVELDB_DIR/lib)
|
CGO_CFLAGS="$CGO_CFLAGS -I$LEVELDB_DIR/include"
|
||||||
|
CGO_CXXFLAGS="$CGO_CXXFLAGS -I$LEVELDB_DIR/include"
|
||||||
|
CGO_LDFLAGS="$CGO_LDFLAGS -L$LEVELDB_DIR/lib -lleveldb"
|
||||||
|
LD_LIBRARY_PATH=$(add_path $LD_LIBRARY_PATH $LEVELDB_DIR/lib)
|
||||||
|
DYLD_LIBRARY_PATH=$(add_path $DYLD_LIBRARY_PATH $LEVELDB_DIR/lib)
|
||||||
|
GO_BUILD_TAGS="$GO_BUILD_TAGS leveldb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check rocksdb
|
||||||
|
if [ -f $ROCKSDB_DIR/lib/librocksdb.a ]; then
|
||||||
|
CGO_CFLAGS="$CGO_CFLAGS -I$ROCKSDB_DIR/include"
|
||||||
|
CGO_CXXFLAGS="$CGO_CXXFLAGS -I$ROCKSDB_DIR/include"
|
||||||
|
CGO_LDFLAGS="$CGO_LDFLAGS -L$ROCKSDB_DIR/lib -lrocksdb"
|
||||||
|
LD_LIBRARY_PATH=$(add_path $LD_LIBRARY_PATH $ROCKSDB_DIR/lib)
|
||||||
|
DYLD_LIBRARY_PATH=$(add_path $DYLD_LIBRARY_PATH $ROCKSDB_DIR/lib)
|
||||||
|
GO_BUILD_TAGS="$GO_BUILD_TAGS rocksdb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export CGO_CFLAGS
|
||||||
|
export CGO_CXXFLAGS
|
||||||
|
export CGO_LDFLAGS
|
||||||
|
export LD_LIBRARY_PATH
|
||||||
|
export DYLD_LIBRARY_PATH
|
||||||
|
export GO_BUILD_TAGS
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
{
|
{
|
||||||
"addr": "127.0.0.1:6380",
|
"addr": "127.0.0.1:6380",
|
||||||
"data_dir": "/tmp/ledis_server",
|
"data_dir": "/tmp/ledis_server",
|
||||||
|
|
||||||
"db": {
|
"db": {
|
||||||
|
"name" : "leveldb",
|
||||||
|
|
||||||
"compression": false,
|
"compression": false,
|
||||||
"block_size": 32768,
|
"block_size": 32768,
|
||||||
"write_buffer_size": 67108864,
|
"write_buffer_size": 67108864,
|
||||||
"cache_size": 524288000,
|
"cache_size": 524288000,
|
||||||
"max_open_files":1024
|
"max_open_files":1024,
|
||||||
|
"map_size" : 524288000
|
||||||
},
|
},
|
||||||
|
|
||||||
"access_log" : "access.log"
|
"access_log" : "access.log"
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
package ledis
|
package ledis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/siddontang/copier"
|
"github.com/siddontang/copier"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DataDir string `json:"data_dir"`
|
DataDir string `json:"data_dir"`
|
||||||
|
|
||||||
DB struct {
|
DB struct {
|
||||||
|
Name string `json:"name"`
|
||||||
Compression bool `json:"compression"`
|
Compression bool `json:"compression"`
|
||||||
BlockSize int `json:"block_size"`
|
BlockSize int `json:"block_size"`
|
||||||
WriteBufferSize int `json:"write_buffer_size"`
|
WriteBufferSize int `json:"write_buffer_size"`
|
||||||
CacheSize int `json:"cache_size"`
|
CacheSize int `json:"cache_size"`
|
||||||
MaxOpenFiles int `json:"max_open_files"`
|
MaxOpenFiles int `json:"max_open_files"`
|
||||||
|
MapSize int `json:"map_size"`
|
||||||
} `json:"db"`
|
} `json:"db"`
|
||||||
|
|
||||||
BinLog struct {
|
BinLog struct {
|
||||||
|
@ -24,11 +28,19 @@ type Config struct {
|
||||||
} `json:"binlog"`
|
} `json:"binlog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) NewDBConfig() *leveldb.Config {
|
func (cfg *Config) NewDBConfig() *store.Config {
|
||||||
dbPath := path.Join(cfg.DataDir, "data")
|
if len(cfg.DB.Name) == 0 {
|
||||||
|
fmt.Printf("no store set, use default %s\n", store.DefaultStoreName)
|
||||||
|
cfg.DB.Name = store.DefaultStoreName
|
||||||
|
}
|
||||||
|
|
||||||
dbCfg := new(leveldb.Config)
|
cfg.DB.Name = strings.ToLower(cfg.DB.Name)
|
||||||
|
|
||||||
|
dbCfg := new(store.Config)
|
||||||
copier.Copy(dbCfg, &cfg.DB)
|
copier.Copy(dbCfg, &cfg.DB)
|
||||||
|
|
||||||
|
dbPath := path.Join(cfg.DataDir, fmt.Sprintf("%s_data", cfg.DB.Name))
|
||||||
|
|
||||||
dbCfg.Path = dbPath
|
dbCfg.Path = dbPath
|
||||||
return dbCfg
|
return dbCfg
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Package ledis is a high performance embedded NoSQL based on leveldb.
|
// Package ledis is a high performance embedded NoSQL.
|
||||||
|
//
|
||||||
// Ledis supports various advanced data structure like kv, list, hash and zset like redis.
|
// Ledis supports various advanced data structure like kv, list, hash and zset like redis.
|
||||||
//
|
//
|
||||||
// Other features include binlog replication, data with a limited time-to-live.
|
// Other features include binlog replication, data with a limited time-to-live.
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"github.com/siddontang/go-snappy/snappy"
|
"github.com/siddontang/go-snappy/snappy"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
@ -57,16 +56,13 @@ func (l *Ledis) DumpFile(path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Ledis) Dump(w io.Writer) error {
|
func (l *Ledis) Dump(w io.Writer) error {
|
||||||
var sp *leveldb.Snapshot
|
|
||||||
var m *MasterInfo = new(MasterInfo)
|
var m *MasterInfo = new(MasterInfo)
|
||||||
if l.binlog == nil {
|
|
||||||
sp = l.ldb.NewSnapshot()
|
|
||||||
} else {
|
|
||||||
l.Lock()
|
l.Lock()
|
||||||
sp = l.ldb.NewSnapshot()
|
defer l.Unlock()
|
||||||
|
|
||||||
|
if l.binlog != nil {
|
||||||
m.LogFileIndex = l.binlog.LogFileIndex()
|
m.LogFileIndex = l.binlog.LogFileIndex()
|
||||||
m.LogPos = l.binlog.LogFilePos()
|
m.LogPos = l.binlog.LogFilePos()
|
||||||
l.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -76,7 +72,7 @@ func (l *Ledis) Dump(w io.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
it := sp.NewIterator()
|
it := l.ldb.NewIterator()
|
||||||
it.SeekToFirst()
|
it.SeekToFirst()
|
||||||
|
|
||||||
compressBuf := make([]byte, 4096)
|
compressBuf := make([]byte, 4096)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package ledis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ func TestDump(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
it := master.ldb.RangeLimitIterator(nil, nil, leveldb.RangeClose, 0, -1)
|
it := master.ldb.RangeLimitIterator(nil, nil, store.RangeClose, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
key := it.Key()
|
key := it.Key()
|
||||||
value := it.Value()
|
value := it.Value()
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/go-log/log"
|
"github.com/siddontang/go-log/log"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,7 @@ import (
|
||||||
type DB struct {
|
type DB struct {
|
||||||
l *Ledis
|
l *Ledis
|
||||||
|
|
||||||
db *leveldb.DB
|
db *store.DB
|
||||||
|
|
||||||
index uint8
|
index uint8
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ type Ledis struct {
|
||||||
|
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
|
||||||
ldb *leveldb.DB
|
ldb *store.DB
|
||||||
dbs [MaxDBNumber]*DB
|
dbs [MaxDBNumber]*DB
|
||||||
|
|
||||||
binlog *BinLog
|
binlog *BinLog
|
||||||
|
@ -52,7 +52,7 @@ func Open(cfg *Config) (*Ledis, error) {
|
||||||
return nil, fmt.Errorf("must set correct data_dir")
|
return nil, fmt.Errorf("must set correct data_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
ldb, err := leveldb.Open(cfg.NewDBConfig())
|
ldb, err := store.Open(cfg.NewDBConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ func (l *Ledis) FlushAll() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// very dangerous to use
|
// very dangerous to use
|
||||||
func (l *Ledis) DataDB() *leveldb.DB {
|
func (l *Ledis) DataDB() *store.DB {
|
||||||
return l.ldb
|
return l.ldb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ledis
|
package ledis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *DB) FlushAll() (drop int64, err error) {
|
func (db *DB) FlushAll() (drop int64, err error) {
|
||||||
|
@ -36,7 +36,7 @@ func (db *DB) newEliminator() *elimination {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) flushRegion(t *tx, minKey []byte, maxKey []byte) (drop int64, err error) {
|
func (db *DB) flushRegion(t *tx, minKey []byte, maxKey []byte) (drop int64, err error) {
|
||||||
it := db.db.RangeIterator(minKey, maxKey, leveldb.RangeROpen)
|
it := db.db.RangeIterator(minKey, maxKey, store.RangeROpen)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
t.Delete(it.RawKey())
|
t.Delete(it.RawKey())
|
||||||
drop++
|
drop++
|
||||||
|
|
|
@ -3,14 +3,14 @@ package ledis
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkLedisEqual(master *Ledis, slave *Ledis) error {
|
func checkLedisEqual(master *Ledis, slave *Ledis) error {
|
||||||
it := master.ldb.RangeLimitIterator(nil, nil, leveldb.RangeClose, 0, -1)
|
it := master.ldb.RangeLimitIterator(nil, nil, store.RangeClose, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
key := it.Key()
|
key := it.Key()
|
||||||
value := it.Value()
|
value := it.Value()
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ledis
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -253,7 +253,7 @@ func (db *DB) bDelete(t *tx, key []byte) (drop int64) {
|
||||||
|
|
||||||
minKey := db.bEncodeBinKey(key, minSeq)
|
minKey := db.bEncodeBinKey(key, minSeq)
|
||||||
maxKey := db.bEncodeBinKey(key, maxSeq)
|
maxKey := db.bEncodeBinKey(key, maxSeq)
|
||||||
it := db.db.RangeIterator(minKey, maxKey, leveldb.RangeClose)
|
it := db.db.RangeIterator(minKey, maxKey, store.RangeClose)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
t.Delete(it.RawKey())
|
t.Delete(it.RawKey())
|
||||||
drop++
|
drop++
|
||||||
|
@ -280,10 +280,10 @@ func (db *DB) bAllocateSegment(key []byte, seq uint32) ([]byte, []byte, error) {
|
||||||
return bk, segment, err
|
return bk, segment, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) bIterator(key []byte) *leveldb.RangeLimitIterator {
|
func (db *DB) bIterator(key []byte) *store.RangeLimitIterator {
|
||||||
sk := db.bEncodeBinKey(key, minSeq)
|
sk := db.bEncodeBinKey(key, minSeq)
|
||||||
ek := db.bEncodeBinKey(key, maxSeq)
|
ek := db.bEncodeBinKey(key, maxSeq)
|
||||||
return db.db.RangeIterator(sk, ek, leveldb.RangeClose)
|
return db.db.RangeIterator(sk, ek, store.RangeClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) bSegAnd(a []byte, b []byte, res *[]byte) {
|
func (db *DB) bSegAnd(a []byte, b []byte, res *[]byte) {
|
||||||
|
@ -446,7 +446,7 @@ func (db *DB) BGet(key []byte) (data []byte, err error) {
|
||||||
|
|
||||||
minKey := db.bEncodeBinKey(key, minSeq)
|
minKey := db.bEncodeBinKey(key, minSeq)
|
||||||
maxKey := db.bEncodeBinKey(key, tailSeq)
|
maxKey := db.bEncodeBinKey(key, tailSeq)
|
||||||
it := db.db.RangeIterator(minKey, maxKey, leveldb.RangeClose)
|
it := db.db.RangeIterator(minKey, maxKey, store.RangeClose)
|
||||||
|
|
||||||
var seq, s, e uint32
|
var seq, s, e uint32
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
|
@ -662,7 +662,7 @@ func (db *DB) BCount(key []byte, start int32, end int32) (cnt int32, err error)
|
||||||
skey := db.bEncodeBinKey(key, sseq)
|
skey := db.bEncodeBinKey(key, sseq)
|
||||||
ekey := db.bEncodeBinKey(key, eseq)
|
ekey := db.bEncodeBinKey(key, eseq)
|
||||||
|
|
||||||
it := db.db.RangeIterator(skey, ekey, leveldb.RangeOpen)
|
it := db.db.RangeIterator(skey, ekey, store.RangeOpen)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
segment = it.RawValue()
|
segment = it.RawValue()
|
||||||
for _, bt := range segment {
|
for _, bt := range segment {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ledis
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ func (db *DB) hDelete(t *tx, key []byte) int64 {
|
||||||
stop := db.hEncodeStopKey(key)
|
stop := db.hEncodeStopKey(key)
|
||||||
|
|
||||||
var num int64 = 0
|
var num int64 = 0
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
t.Delete(it.Key())
|
t.Delete(it.Key())
|
||||||
num++
|
num++
|
||||||
|
@ -354,7 +354,7 @@ func (db *DB) HGetAll(key []byte) ([]FVPair, error) {
|
||||||
|
|
||||||
v := make([]FVPair, 0, 16)
|
v := make([]FVPair, 0, 16)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
_, f, err := db.hDecodeHashKey(it.Key())
|
_, f, err := db.hDecodeHashKey(it.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -379,7 +379,7 @@ func (db *DB) HKeys(key []byte) ([][]byte, error) {
|
||||||
|
|
||||||
v := make([][]byte, 0, 16)
|
v := make([][]byte, 0, 16)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
_, f, err := db.hDecodeHashKey(it.Key())
|
_, f, err := db.hDecodeHashKey(it.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -403,7 +403,7 @@ func (db *DB) HValues(key []byte) ([][]byte, error) {
|
||||||
|
|
||||||
v := make([][]byte, 0, 16)
|
v := make([][]byte, 0, 16)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
_, _, err := db.hDecodeHashKey(it.Key())
|
_, _, err := db.hDecodeHashKey(it.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -491,9 +491,9 @@ func (db *DB) HScan(key []byte, field []byte, count int, inclusive bool) ([]FVPa
|
||||||
|
|
||||||
v := make([]FVPair, 0, count)
|
v := make([]FVPair, 0, count)
|
||||||
|
|
||||||
rangeType := leveldb.RangeROpen
|
rangeType := store.RangeROpen
|
||||||
if !inclusive {
|
if !inclusive {
|
||||||
rangeType = leveldb.RangeOpen
|
rangeType = store.RangeOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package ledis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -343,9 +343,9 @@ func (db *DB) Scan(key []byte, count int, inclusive bool) ([]KVPair, error) {
|
||||||
|
|
||||||
v := make([]KVPair, 0, 2*count)
|
v := make([]KVPair, 0, 2*count)
|
||||||
|
|
||||||
rangeType := leveldb.RangeROpen
|
rangeType := store.RangeROpen
|
||||||
if !inclusive {
|
if !inclusive {
|
||||||
rangeType = leveldb.RangeOpen
|
rangeType = store.RangeOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ledis
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ func (db *DB) lDelete(t *tx, key []byte) int64 {
|
||||||
startKey := db.lEncodeListKey(key, headSeq)
|
startKey := db.lEncodeListKey(key, headSeq)
|
||||||
stopKey := db.lEncodeListKey(key, tailSeq)
|
stopKey := db.lEncodeListKey(key, tailSeq)
|
||||||
|
|
||||||
rit := leveldb.NewRangeIterator(it, &leveldb.Range{startKey, stopKey, leveldb.RangeClose})
|
rit := store.NewRangeIterator(it, &store.Range{startKey, stopKey, store.RangeClose})
|
||||||
for ; rit.Valid(); rit.Next() {
|
for ; rit.Valid(); rit.Next() {
|
||||||
t.Delete(rit.RawKey())
|
t.Delete(rit.RawKey())
|
||||||
num++
|
num++
|
||||||
|
@ -214,7 +214,7 @@ func (db *DB) lDelete(t *tx, key []byte) int64 {
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) lGetMeta(it *leveldb.Iterator, ek []byte) (headSeq int32, tailSeq int32, size int32, err error) {
|
func (db *DB) lGetMeta(it *store.Iterator, ek []byte) (headSeq int32, tailSeq int32, size int32, err error) {
|
||||||
var v []byte
|
var v []byte
|
||||||
if it != nil {
|
if it != nil {
|
||||||
v = it.Find(ek)
|
v = it.Find(ek)
|
||||||
|
@ -364,12 +364,12 @@ func (db *DB) LRange(key []byte, start int32, stop int32) ([][]byte, error) {
|
||||||
v := make([][]byte, 0, limit)
|
v := make([][]byte, 0, limit)
|
||||||
|
|
||||||
startKey := db.lEncodeListKey(key, headSeq)
|
startKey := db.lEncodeListKey(key, headSeq)
|
||||||
rit := leveldb.NewRangeLimitIterator(it,
|
rit := store.NewRangeLimitIterator(it,
|
||||||
&leveldb.Range{
|
&store.Range{
|
||||||
Min: startKey,
|
Min: startKey,
|
||||||
Max: nil,
|
Max: nil,
|
||||||
Type: leveldb.RangeClose},
|
Type: store.RangeClose},
|
||||||
&leveldb.Limit{
|
&store.Limit{
|
||||||
Offset: 0,
|
Offset: 0,
|
||||||
Count: int(limit)})
|
Count: int(limit)})
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ledis
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ func (eli *elimination) active() {
|
||||||
minKey := db.expEncodeTimeKey(NoneType, nil, 0)
|
minKey := db.expEncodeTimeKey(NoneType, nil, 0)
|
||||||
maxKey := db.expEncodeTimeKey(maxDataType, nil, now)
|
maxKey := db.expEncodeTimeKey(maxDataType, nil, now)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(minKey, maxKey, store.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
tk := it.RawKey()
|
tk := it.RawKey()
|
||||||
mk := it.RawValue()
|
mk := it.RawValue()
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ func (db *DB) ZCount(key []byte, min int64, max int64) (int64, error) {
|
||||||
minKey := db.zEncodeStartScoreKey(key, min)
|
minKey := db.zEncodeStartScoreKey(key, min)
|
||||||
maxKey := db.zEncodeStopScoreKey(key, max)
|
maxKey := db.zEncodeStopScoreKey(key, max)
|
||||||
|
|
||||||
rangeType := leveldb.RangeROpen
|
rangeType := store.RangeROpen
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, -1)
|
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, -1)
|
||||||
var n int64 = 0
|
var n int64 = 0
|
||||||
|
@ -460,17 +460,17 @@ func (db *DB) zrank(key []byte, member []byte, reverse bool) (int64, error) {
|
||||||
if s, err := Int64(v, nil); err != nil {
|
if s, err := Int64(v, nil); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else {
|
} else {
|
||||||
var rit *leveldb.RangeLimitIterator
|
var rit *store.RangeLimitIterator
|
||||||
|
|
||||||
sk := db.zEncodeScoreKey(key, member, s)
|
sk := db.zEncodeScoreKey(key, member, s)
|
||||||
|
|
||||||
if !reverse {
|
if !reverse {
|
||||||
minKey := db.zEncodeStartScoreKey(key, MinScore)
|
minKey := db.zEncodeStartScoreKey(key, MinScore)
|
||||||
|
|
||||||
rit = leveldb.NewRangeIterator(it, &leveldb.Range{minKey, sk, leveldb.RangeClose})
|
rit = store.NewRangeIterator(it, &store.Range{minKey, sk, store.RangeClose})
|
||||||
} else {
|
} else {
|
||||||
maxKey := db.zEncodeStopScoreKey(key, MaxScore)
|
maxKey := db.zEncodeStopScoreKey(key, MaxScore)
|
||||||
rit = leveldb.NewRevRangeIterator(it, &leveldb.Range{sk, maxKey, leveldb.RangeClose})
|
rit = store.NewRevRangeIterator(it, &store.Range{sk, maxKey, store.RangeClose})
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastKey []byte = nil
|
var lastKey []byte = nil
|
||||||
|
@ -492,14 +492,14 @@ func (db *DB) zrank(key []byte, member []byte, reverse bool) (int64, error) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) zIterator(key []byte, min int64, max int64, offset int, count int, reverse bool) *leveldb.RangeLimitIterator {
|
func (db *DB) zIterator(key []byte, min int64, max int64, offset int, count int, reverse bool) *store.RangeLimitIterator {
|
||||||
minKey := db.zEncodeStartScoreKey(key, min)
|
minKey := db.zEncodeStartScoreKey(key, min)
|
||||||
maxKey := db.zEncodeStopScoreKey(key, max)
|
maxKey := db.zEncodeStopScoreKey(key, max)
|
||||||
|
|
||||||
if !reverse {
|
if !reverse {
|
||||||
return db.db.RangeLimitIterator(minKey, maxKey, leveldb.RangeClose, offset, count)
|
return db.db.RangeLimitIterator(minKey, maxKey, store.RangeClose, offset, count)
|
||||||
} else {
|
} else {
|
||||||
return db.db.RevRangeLimitIterator(minKey, maxKey, leveldb.RangeClose, offset, count)
|
return db.db.RevRangeLimitIterator(minKey, maxKey, store.RangeClose, offset, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,10 +550,10 @@ func (db *DB) zRange(key []byte, min int64, max int64, offset int, count int, re
|
||||||
|
|
||||||
v := make([]ScorePair, 0, nv)
|
v := make([]ScorePair, 0, nv)
|
||||||
|
|
||||||
var it *leveldb.RangeLimitIterator
|
var it *store.RangeLimitIterator
|
||||||
|
|
||||||
//if reverse and offset is 0, count < 0, we may use forward iterator then reverse
|
//if reverse and offset is 0, count < 0, we may use forward iterator then reverse
|
||||||
//because leveldb iterator prev is slower than next
|
//because store iterator prev is slower than next
|
||||||
if !reverse || (offset == 0 && count < 0) {
|
if !reverse || (offset == 0 && count < 0) {
|
||||||
it = db.zIterator(key, min, max, offset, count, false)
|
it = db.zIterator(key, min, max, offset, count, false)
|
||||||
} else {
|
} else {
|
||||||
|
@ -740,7 +740,7 @@ func (db *DB) zFlush() (drop int64, err error) {
|
||||||
maxKey[0] = db.index
|
maxKey[0] = db.index
|
||||||
maxKey[1] = ZScoreType + 1
|
maxKey[1] = ZScoreType + 1
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(minKey, maxKey, store.RangeROpen, 0, -1)
|
||||||
defer it.Close()
|
defer it.Close()
|
||||||
|
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
|
@ -779,9 +779,9 @@ func (db *DB) ZScan(key []byte, member []byte, count int, inclusive bool) ([]Sco
|
||||||
|
|
||||||
v := make([]ScorePair, 0, 2*count)
|
v := make([]ScorePair, 0, 2*count)
|
||||||
|
|
||||||
rangeType := leveldb.RangeROpen
|
rangeType := store.RangeROpen
|
||||||
if !inclusive {
|
if !inclusive {
|
||||||
rangeType = leveldb.RangeOpen
|
rangeType = store.RangeOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ledis
|
package ledis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ type tx struct {
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
|
|
||||||
l *Ledis
|
l *Ledis
|
||||||
wb *leveldb.WriteBatch
|
wb *store.WriteBatch
|
||||||
|
|
||||||
binlog *BinLog
|
binlog *BinLog
|
||||||
batch [][]byte
|
batch [][]byte
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
package leveldb
|
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lleveldb
|
|
||||||
// #include <stdint.h>
|
|
||||||
// #include "leveldb/c.h"
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type Snapshot struct {
|
|
||||||
db *DB
|
|
||||||
|
|
||||||
snap *C.leveldb_snapshot_t
|
|
||||||
|
|
||||||
readOpts *ReadOptions
|
|
||||||
iteratorOpts *ReadOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Snapshot) Close() {
|
|
||||||
C.leveldb_release_snapshot(s.db.db, s.snap)
|
|
||||||
|
|
||||||
s.iteratorOpts.Close()
|
|
||||||
s.readOpts.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Snapshot) Get(key []byte) ([]byte, error) {
|
|
||||||
return s.db.get(nil, s.readOpts, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Snapshot) BufGet(r []byte, key []byte) ([]byte, error) {
|
|
||||||
return s.db.get(r, s.readOpts, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Snapshot) NewIterator() *Iterator {
|
|
||||||
it := new(Iterator)
|
|
||||||
|
|
||||||
it.it = C.leveldb_create_iterator(s.db.db, s.iteratorOpts.Opt)
|
|
||||||
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Snapshot) RangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
|
||||||
return NewRangeLimitIterator(s.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Snapshot) RevRangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
|
||||||
return NewRevRangeLimitIterator(s.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
|
||||||
}
|
|
||||||
|
|
||||||
//count < 0, unlimit.
|
|
||||||
//
|
|
||||||
//offset must >= 0, if < 0, will get nothing.
|
|
||||||
func (s *Snapshot) RangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator {
|
|
||||||
return NewRangeLimitIterator(s.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count})
|
|
||||||
}
|
|
||||||
|
|
||||||
//count < 0, unlimit.
|
|
||||||
//
|
|
||||||
//offset must >= 0, if < 0, will get nothing.
|
|
||||||
func (s *Snapshot) RevRangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator {
|
|
||||||
return NewRevRangeLimitIterator(s.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count})
|
|
||||||
}
|
|
|
@ -3,14 +3,14 @@ package server
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/store"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkDataEqual(master *App, slave *App) error {
|
func checkDataEqual(master *App, slave *App) error {
|
||||||
it := master.ldb.DataDB().RangeLimitIterator(nil, nil, leveldb.RangeClose, 0, -1)
|
it := master.ldb.DataDB().RangeLimitIterator(nil, nil, store.RangeClose, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
key := it.Key()
|
key := it.Key()
|
||||||
value := it.Value()
|
value := it.Value()
|
||||||
|
|
|
@ -13,11 +13,13 @@ type Config struct {
|
||||||
DataDir string `json:"data_dir"`
|
DataDir string `json:"data_dir"`
|
||||||
|
|
||||||
DB struct {
|
DB struct {
|
||||||
|
Name string `json:"name"`
|
||||||
Compression bool `json:"compression"`
|
Compression bool `json:"compression"`
|
||||||
BlockSize int `json:"block_size"`
|
BlockSize int `json:"block_size"`
|
||||||
WriteBufferSize int `json:"write_buffer_size"`
|
WriteBufferSize int `json:"write_buffer_size"`
|
||||||
CacheSize int `json:"cache_size"`
|
CacheSize int `json:"cache_size"`
|
||||||
MaxOpenFiles int `json:"max_open_files"`
|
MaxOpenFiles int `json:"max_open_files"`
|
||||||
|
MapSize int `json:"map_size"`
|
||||||
} `json:"db"`
|
} `json:"db"`
|
||||||
|
|
||||||
BinLog struct {
|
BinLog struct {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
Path string `json:"path"`
|
||||||
|
|
||||||
|
//for leveldb, goleveldb
|
||||||
|
Compression bool `json:"compression"`
|
||||||
|
BlockSize int `json:"block_size"`
|
||||||
|
WriteBufferSize int `json:"write_buffer_size"`
|
||||||
|
CacheSize int `json:"cache_size"`
|
||||||
|
MaxOpenFiles int `json:"max_open_files"`
|
||||||
|
|
||||||
|
//for lmdb
|
||||||
|
MapSize int `json:"map_size"`
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
db driver.IDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close database
|
||||||
|
//
|
||||||
|
// Caveat
|
||||||
|
// Any other DB operations like Get, Put, etc... may cause a panic after Close
|
||||||
|
//
|
||||||
|
func (db *DB) Close() error {
|
||||||
|
if db.db == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.db.Close()
|
||||||
|
db.db = nil
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Value with Key
|
||||||
|
func (db *DB) Get(key []byte) ([]byte, error) {
|
||||||
|
return db.db.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put value with key
|
||||||
|
func (db *DB) Put(key []byte, value []byte) error {
|
||||||
|
err := db.db.Put(key, value)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete by key
|
||||||
|
func (db *DB) Delete(key []byte) error {
|
||||||
|
err := db.db.Delete(key)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) NewIterator() *Iterator {
|
||||||
|
it := new(Iterator)
|
||||||
|
it.it = db.db.NewIterator()
|
||||||
|
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) NewWriteBatch() *WriteBatch {
|
||||||
|
return &WriteBatch{db.db.NewWriteBatch()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) RangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
||||||
|
return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) RevRangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
||||||
|
return NewRevRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
||||||
|
}
|
||||||
|
|
||||||
|
//count < 0, unlimit.
|
||||||
|
//
|
||||||
|
//offset must >= 0, if < 0, will get nothing.
|
||||||
|
func (db *DB) RangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator {
|
||||||
|
return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count})
|
||||||
|
}
|
||||||
|
|
||||||
|
//count < 0, unlimit.
|
||||||
|
//
|
||||||
|
//offset must >= 0, if < 0, will get nothing.
|
||||||
|
func (db *DB) RevRangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator {
|
||||||
|
return NewRevRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count})
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
type IDB interface {
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
Get(key []byte) ([]byte, error)
|
||||||
|
|
||||||
|
Put(key []byte, value []byte) error
|
||||||
|
Delete(key []byte) error
|
||||||
|
|
||||||
|
NewIterator() IIterator
|
||||||
|
|
||||||
|
NewWriteBatch() IWriteBatch
|
||||||
|
}
|
||||||
|
|
||||||
|
type IIterator interface {
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
First()
|
||||||
|
Last()
|
||||||
|
Seek(key []byte)
|
||||||
|
|
||||||
|
Next()
|
||||||
|
Prev()
|
||||||
|
|
||||||
|
Valid() bool
|
||||||
|
|
||||||
|
Key() []byte
|
||||||
|
Value() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type IWriteBatch interface {
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
Put(key []byte, value []byte)
|
||||||
|
Delete(key []byte)
|
||||||
|
Commit() error
|
||||||
|
Rollback() error
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/copier"
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"github.com/siddontang/ledisdb/store/goleveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const GoLevelDBName = "goleveldb"
|
||||||
|
|
||||||
|
type GoLevelDBStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s GoLevelDBStore) Open(cfg *Config) (driver.IDB, error) {
|
||||||
|
c := &goleveldb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return goleveldb.Open(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s GoLevelDBStore) Repair(cfg *Config) error {
|
||||||
|
c := &goleveldb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return goleveldb.Repair(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(GoLevelDBName, GoLevelDBStore{})
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package goleveldb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WriteBatch struct {
|
||||||
|
db *DB
|
||||||
|
wbatch *leveldb.Batch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Put(key, value []byte) {
|
||||||
|
w.wbatch.Put(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Delete(key []byte) {
|
||||||
|
w.wbatch.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Commit() error {
|
||||||
|
return w.db.db.Write(w.wbatch, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Rollback() error {
|
||||||
|
w.wbatch.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package goleveldb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/cache"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"`
|
||||||
|
MaxOpenFiles int `json:"max_open_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
cfg *Config
|
||||||
|
|
||||||
|
db *leveldb.DB
|
||||||
|
|
||||||
|
opts *opt.Options
|
||||||
|
|
||||||
|
iteratorOpts *opt.ReadOptions
|
||||||
|
|
||||||
|
cache cache.Cache
|
||||||
|
|
||||||
|
filter filter.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(cfg *Config) (*DB, error) {
|
||||||
|
if err := os.MkdirAll(cfg.Path, os.ModePerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db := new(DB)
|
||||||
|
db.cfg = cfg
|
||||||
|
|
||||||
|
if err := db.open(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Repair(cfg *Config) error {
|
||||||
|
db, err := leveldb.RecoverFile(cfg.Path, newOptions(cfg))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) open() error {
|
||||||
|
db.opts = newOptions(db.cfg)
|
||||||
|
|
||||||
|
db.iteratorOpts = &opt.ReadOptions{}
|
||||||
|
db.iteratorOpts.DontFillCache = true
|
||||||
|
|
||||||
|
var err error
|
||||||
|
db.db, err = leveldb.OpenFile(db.cfg.Path, db.opts)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOptions(cfg *Config) *opt.Options {
|
||||||
|
opts := &opt.Options{}
|
||||||
|
opts.ErrorIfMissing = false
|
||||||
|
|
||||||
|
if cfg.CacheSize > 0 {
|
||||||
|
opts.BlockCache = cache.NewLRUCache(cfg.CacheSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
//we must use bloomfilter
|
||||||
|
opts.Filter = filter.NewBloomFilter(defaultFilterBits)
|
||||||
|
|
||||||
|
if !cfg.Compression {
|
||||||
|
opts.Compression = opt.NoCompression
|
||||||
|
} else {
|
||||||
|
opts.Compression = opt.SnappyCompression
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.BlockSize > 0 {
|
||||||
|
opts.BlockSize = cfg.BlockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.WriteBufferSize > 0 {
|
||||||
|
opts.WriteBuffer = cfg.WriteBufferSize
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Close() error {
|
||||||
|
return db.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Put(key, value []byte) error {
|
||||||
|
return db.db.Put(key, value, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Get(key []byte) ([]byte, error) {
|
||||||
|
v, err := db.db.Get(key, nil)
|
||||||
|
if err == leveldb.ErrNotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Delete(key []byte) error {
|
||||||
|
return db.db.Delete(key, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) NewWriteBatch() driver.IWriteBatch {
|
||||||
|
wb := &WriteBatch{
|
||||||
|
db: db,
|
||||||
|
wbatch: new(leveldb.Batch),
|
||||||
|
}
|
||||||
|
return wb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) NewIterator() driver.IIterator {
|
||||||
|
it := &Iterator{
|
||||||
|
db.db.NewIterator(nil, db.iteratorOpts),
|
||||||
|
}
|
||||||
|
|
||||||
|
return it
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package goleveldb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Iterator struct {
|
||||||
|
it iterator.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Key() []byte {
|
||||||
|
return it.it.Key()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Value() []byte {
|
||||||
|
return it.it.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Close() error {
|
||||||
|
if it.it != nil {
|
||||||
|
it.it.Release()
|
||||||
|
it.it = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Valid() bool {
|
||||||
|
return it.it.Valid()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Next() {
|
||||||
|
it.it.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Prev() {
|
||||||
|
it.it.Prev()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) First() {
|
||||||
|
it.it.First()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Last() {
|
||||||
|
it.it.Last()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Seek(key []byte) {
|
||||||
|
it.it.Seek(key)
|
||||||
|
}
|
|
@ -1,14 +1,8 @@
|
||||||
package leveldb
|
package store
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lleveldb
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #include "leveldb/c.h"
|
|
||||||
// #include "leveldb_ext.h"
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"unsafe"
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -45,54 +39,39 @@ type Limit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Iterator struct {
|
type Iterator struct {
|
||||||
it *C.leveldb_iterator_t
|
it driver.IIterator
|
||||||
isValid C.uchar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a copy of key.
|
// Returns a copy of key.
|
||||||
func (it *Iterator) Key() []byte {
|
func (it *Iterator) Key() []byte {
|
||||||
var klen C.size_t
|
k := it.it.Key()
|
||||||
kdata := C.leveldb_iter_key(it.it, &klen)
|
if k == nil {
|
||||||
if kdata == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return C.GoBytes(unsafe.Pointer(kdata), C.int(klen))
|
return append([]byte{}, k...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a copy of value.
|
// Returns a copy of value.
|
||||||
func (it *Iterator) Value() []byte {
|
func (it *Iterator) Value() []byte {
|
||||||
var vlen C.size_t
|
v := it.it.Value()
|
||||||
vdata := C.leveldb_iter_value(it.it, &vlen)
|
if v == nil {
|
||||||
if vdata == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return C.GoBytes(unsafe.Pointer(vdata), C.int(vlen))
|
return append([]byte{}, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a reference of key.
|
// Returns a reference of key.
|
||||||
// you must be careful that it will be changed after next iterate.
|
// you must be careful that it will be changed after next iterate.
|
||||||
func (it *Iterator) RawKey() []byte {
|
func (it *Iterator) RawKey() []byte {
|
||||||
var klen C.size_t
|
return it.it.Key()
|
||||||
kdata := C.leveldb_iter_key(it.it, &klen)
|
|
||||||
if kdata == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return slice(unsafe.Pointer(kdata), int(C.int(klen)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a reference of value.
|
// Returns a reference of value.
|
||||||
// you must be careful that it will be changed after next iterate.
|
// you must be careful that it will be changed after next iterate.
|
||||||
func (it *Iterator) RawValue() []byte {
|
func (it *Iterator) RawValue() []byte {
|
||||||
var vlen C.size_t
|
return it.it.Value()
|
||||||
vdata := C.leveldb_iter_value(it.it, &vlen)
|
|
||||||
if vdata == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return slice(unsafe.Pointer(vdata), int(C.int(vlen)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy key to b, if b len is small or nil, returns a new one.
|
// Copy key to b, if b len is small or nil, returns a new one.
|
||||||
|
@ -126,33 +105,33 @@ func (it *Iterator) BufValue(b []byte) []byte {
|
||||||
|
|
||||||
func (it *Iterator) Close() {
|
func (it *Iterator) Close() {
|
||||||
if it.it != nil {
|
if it.it != nil {
|
||||||
C.leveldb_iter_destroy(it.it)
|
it.it.Close()
|
||||||
it.it = nil
|
it.it = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Iterator) Valid() bool {
|
func (it *Iterator) Valid() bool {
|
||||||
return ucharToBool(it.isValid)
|
return it.it.Valid()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Iterator) Next() {
|
func (it *Iterator) Next() {
|
||||||
it.isValid = C.leveldb_iter_next_ext(it.it)
|
it.it.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Iterator) Prev() {
|
func (it *Iterator) Prev() {
|
||||||
it.isValid = C.leveldb_iter_prev_ext(it.it)
|
it.it.Prev()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Iterator) SeekToFirst() {
|
func (it *Iterator) SeekToFirst() {
|
||||||
it.isValid = C.leveldb_iter_seek_to_first_ext(it.it)
|
it.it.First()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Iterator) SeekToLast() {
|
func (it *Iterator) SeekToLast() {
|
||||||
it.isValid = C.leveldb_iter_seek_to_last_ext(it.it)
|
it.it.Last()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *Iterator) Seek(key []byte) {
|
func (it *Iterator) Seek(key []byte) {
|
||||||
it.isValid = C.leveldb_iter_seek_ext(it.it, (*C.char)(unsafe.Pointer(&key[0])), C.size_t(len(key)))
|
it.it.Seek(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds by key, if not found, nil returns.
|
// Finds by key, if not found, nil returns.
|
|
@ -0,0 +1,32 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/copier"
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"github.com/siddontang/ledisdb/store/leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LevelDBName = "leveldb"
|
||||||
|
|
||||||
|
type LevelDBStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s LevelDBStore) Open(cfg *Config) (driver.IDB, error) {
|
||||||
|
c := &leveldb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return leveldb.Open(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s LevelDBStore) Repair(cfg *Config) error {
|
||||||
|
c := &leveldb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return leveldb.Repair(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(LevelDBName, LevelDBStore{})
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lleveldb
|
// #cgo LDFLAGS: -lleveldb
|
||||||
|
@ -13,8 +15,9 @@ type WriteBatch struct {
|
||||||
wbatch *C.leveldb_writebatch_t
|
wbatch *C.leveldb_writebatch_t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WriteBatch) Close() {
|
func (w *WriteBatch) Close() error {
|
||||||
C.leveldb_writebatch_destroy(w.wbatch)
|
C.leveldb_writebatch_destroy(w.wbatch)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WriteBatch) Put(key, value []byte) {
|
func (w *WriteBatch) Put(key, value []byte) {
|
||||||
|
@ -41,12 +44,9 @@ func (w *WriteBatch) Commit() error {
|
||||||
return w.commit(w.db.writeOpts)
|
return w.commit(w.db.writeOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WriteBatch) SyncCommit() error {
|
func (w *WriteBatch) Rollback() error {
|
||||||
return w.commit(w.db.syncWriteOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WriteBatch) Rollback() {
|
|
||||||
C.leveldb_writebatch_clear(w.wbatch)
|
C.leveldb_writebatch_clear(w.wbatch)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WriteBatch) commit(wb *WriteOptions) error {
|
func (w *WriteBatch) commit(wb *WriteOptions) error {
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lleveldb
|
// #cgo LDFLAGS: -lleveldb
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
// Package leveldb is a wrapper for c++ leveldb
|
// Package leveldb is a wrapper for c++ leveldb
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
|
@ -9,7 +11,7 @@ package leveldb
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
"os"
|
"os"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -26,35 +28,6 @@ type Config struct {
|
||||||
MaxOpenFiles int `json:"max_open_files"`
|
MaxOpenFiles int `json:"max_open_files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DB struct {
|
|
||||||
cfg *Config
|
|
||||||
|
|
||||||
db *C.leveldb_t
|
|
||||||
|
|
||||||
opts *Options
|
|
||||||
|
|
||||||
//for default read and write options
|
|
||||||
readOpts *ReadOptions
|
|
||||||
writeOpts *WriteOptions
|
|
||||||
iteratorOpts *ReadOptions
|
|
||||||
|
|
||||||
syncWriteOpts *WriteOptions
|
|
||||||
|
|
||||||
cache *Cache
|
|
||||||
|
|
||||||
filter *FilterPolicy
|
|
||||||
}
|
|
||||||
|
|
||||||
func OpenWithJsonConfig(configJson json.RawMessage) (*DB, error) {
|
|
||||||
cfg := new(Config)
|
|
||||||
err := json.Unmarshal(configJson, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Open(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(cfg *Config) (*DB, error) {
|
func Open(cfg *Config) (*DB, error) {
|
||||||
if err := os.MkdirAll(cfg.Path, os.ModePerm); err != nil {
|
if err := os.MkdirAll(cfg.Path, os.ModePerm); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -70,21 +43,6 @@ func Open(cfg *Config) (*DB, error) {
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) open() error {
|
|
||||||
db.initOptions(db.cfg)
|
|
||||||
|
|
||||||
var errStr *C.char
|
|
||||||
ldbname := C.CString(db.cfg.Path)
|
|
||||||
defer C.leveldb_free(unsafe.Pointer(ldbname))
|
|
||||||
|
|
||||||
db.db = C.leveldb_open(db.opts.Opt, ldbname, &errStr)
|
|
||||||
if errStr != nil {
|
|
||||||
db.db = nil
|
|
||||||
return saveError(errStr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Repair(cfg *Config) error {
|
func Repair(cfg *Config) error {
|
||||||
db := new(DB)
|
db := new(DB)
|
||||||
db.cfg = cfg
|
db.cfg = cfg
|
||||||
|
@ -108,6 +66,38 @@ func Repair(cfg *Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
cfg *Config
|
||||||
|
|
||||||
|
db *C.leveldb_t
|
||||||
|
|
||||||
|
opts *Options
|
||||||
|
|
||||||
|
//for default read and write options
|
||||||
|
readOpts *ReadOptions
|
||||||
|
writeOpts *WriteOptions
|
||||||
|
iteratorOpts *ReadOptions
|
||||||
|
|
||||||
|
cache *Cache
|
||||||
|
|
||||||
|
filter *FilterPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) open() error {
|
||||||
|
db.initOptions(db.cfg)
|
||||||
|
|
||||||
|
var errStr *C.char
|
||||||
|
ldbname := C.CString(db.cfg.Path)
|
||||||
|
defer C.leveldb_free(unsafe.Pointer(ldbname))
|
||||||
|
|
||||||
|
db.db = C.leveldb_open(db.opts.Opt, ldbname, &errStr)
|
||||||
|
if errStr != nil {
|
||||||
|
db.db = nil
|
||||||
|
return saveError(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) initOptions(cfg *Config) {
|
func (db *DB) initOptions(cfg *Config) {
|
||||||
opts := NewOptions()
|
opts := NewOptions()
|
||||||
|
|
||||||
|
@ -126,6 +116,8 @@ func (db *DB) initOptions(cfg *Config) {
|
||||||
|
|
||||||
if !cfg.Compression {
|
if !cfg.Compression {
|
||||||
opts.SetCompression(NoCompression)
|
opts.SetCompression(NoCompression)
|
||||||
|
} else {
|
||||||
|
opts.SetCompression(SnappyCompression)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.BlockSize <= 0 {
|
if cfg.BlockSize <= 0 {
|
||||||
|
@ -153,9 +145,6 @@ func (db *DB) initOptions(cfg *Config) {
|
||||||
|
|
||||||
db.iteratorOpts = NewReadOptions()
|
db.iteratorOpts = NewReadOptions()
|
||||||
db.iteratorOpts.SetFillCache(false)
|
db.iteratorOpts.SetFillCache(false)
|
||||||
|
|
||||||
db.syncWriteOpts = NewWriteOptions()
|
|
||||||
db.syncWriteOpts.SetSync(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Close() error {
|
func (db *DB) Close() error {
|
||||||
|
@ -177,80 +166,23 @@ func (db *DB) Close() error {
|
||||||
db.readOpts.Close()
|
db.readOpts.Close()
|
||||||
db.writeOpts.Close()
|
db.writeOpts.Close()
|
||||||
db.iteratorOpts.Close()
|
db.iteratorOpts.Close()
|
||||||
db.syncWriteOpts.Close()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Destroy() error {
|
|
||||||
path := db.cfg.Path
|
|
||||||
|
|
||||||
db.Close()
|
|
||||||
|
|
||||||
opts := NewOptions()
|
|
||||||
defer opts.Close()
|
|
||||||
|
|
||||||
var errStr *C.char
|
|
||||||
ldbname := C.CString(path)
|
|
||||||
defer C.leveldb_free(unsafe.Pointer(ldbname))
|
|
||||||
|
|
||||||
C.leveldb_destroy_db(opts.Opt, ldbname, &errStr)
|
|
||||||
if errStr != nil {
|
|
||||||
return saveError(errStr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) Clear() error {
|
|
||||||
bc := db.NewWriteBatch()
|
|
||||||
defer bc.Close()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
it := db.NewIterator()
|
|
||||||
it.SeekToFirst()
|
|
||||||
|
|
||||||
num := 0
|
|
||||||
for ; it.Valid(); it.Next() {
|
|
||||||
bc.Delete(it.RawKey())
|
|
||||||
num++
|
|
||||||
if num == 1000 {
|
|
||||||
num = 0
|
|
||||||
if err = bc.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bc.Commit()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) Put(key, value []byte) error {
|
func (db *DB) Put(key, value []byte) error {
|
||||||
return db.put(db.writeOpts, key, value)
|
return db.put(db.writeOpts, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) SyncPut(key, value []byte) error {
|
|
||||||
return db.put(db.syncWriteOpts, key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) Get(key []byte) ([]byte, error) {
|
func (db *DB) Get(key []byte) ([]byte, error) {
|
||||||
return db.get(nil, db.readOpts, key)
|
return db.get(db.readOpts, key)
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) BufGet(r []byte, key []byte) ([]byte, error) {
|
|
||||||
return db.get(r, db.readOpts, key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Delete(key []byte) error {
|
func (db *DB) Delete(key []byte) error {
|
||||||
return db.delete(db.writeOpts, key)
|
return db.delete(db.writeOpts, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) SyncDelete(key []byte) error {
|
func (db *DB) NewWriteBatch() driver.IWriteBatch {
|
||||||
return db.delete(db.syncWriteOpts, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) NewWriteBatch() *WriteBatch {
|
|
||||||
wb := &WriteBatch{
|
wb := &WriteBatch{
|
||||||
db: db,
|
db: db,
|
||||||
wbatch: C.leveldb_writebatch_create(),
|
wbatch: C.leveldb_writebatch_create(),
|
||||||
|
@ -258,22 +190,7 @@ func (db *DB) NewWriteBatch() *WriteBatch {
|
||||||
return wb
|
return wb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) NewSnapshot() *Snapshot {
|
func (db *DB) NewIterator() driver.IIterator {
|
||||||
s := &Snapshot{
|
|
||||||
db: db,
|
|
||||||
snap: C.leveldb_create_snapshot(db.db),
|
|
||||||
readOpts: NewReadOptions(),
|
|
||||||
iteratorOpts: NewReadOptions(),
|
|
||||||
}
|
|
||||||
|
|
||||||
s.readOpts.SetSnapshot(s)
|
|
||||||
s.iteratorOpts.SetSnapshot(s)
|
|
||||||
s.iteratorOpts.SetFillCache(false)
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) NewIterator() *Iterator {
|
|
||||||
it := new(Iterator)
|
it := new(Iterator)
|
||||||
|
|
||||||
it.it = C.leveldb_create_iterator(db.db, db.iteratorOpts.Opt)
|
it.it = C.leveldb_create_iterator(db.db, db.iteratorOpts.Opt)
|
||||||
|
@ -281,28 +198,6 @@ func (db *DB) NewIterator() *Iterator {
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) RangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
|
||||||
return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) RevRangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
|
||||||
return NewRevRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
|
||||||
}
|
|
||||||
|
|
||||||
//count < 0, unlimit.
|
|
||||||
//
|
|
||||||
//offset must >= 0, if < 0, will get nothing.
|
|
||||||
func (db *DB) RangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator {
|
|
||||||
return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count})
|
|
||||||
}
|
|
||||||
|
|
||||||
//count < 0, unlimit.
|
|
||||||
//
|
|
||||||
//offset must >= 0, if < 0, will get nothing.
|
|
||||||
func (db *DB) RevRangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator {
|
|
||||||
return NewRevRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) put(wo *WriteOptions, key, value []byte) error {
|
func (db *DB) put(wo *WriteOptions, key, value []byte) error {
|
||||||
var errStr *C.char
|
var errStr *C.char
|
||||||
var k, v *C.char
|
var k, v *C.char
|
||||||
|
@ -324,7 +219,7 @@ func (db *DB) put(wo *WriteOptions, key, value []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) get(r []byte, ro *ReadOptions, key []byte) ([]byte, error) {
|
func (db *DB) get(ro *ReadOptions, key []byte) ([]byte, error) {
|
||||||
var errStr *C.char
|
var errStr *C.char
|
||||||
var vallen C.size_t
|
var vallen C.size_t
|
||||||
var k *C.char
|
var k *C.char
|
||||||
|
@ -347,13 +242,7 @@ func (db *DB) get(r []byte, ro *ReadOptions, key []byte) ([]byte, error) {
|
||||||
|
|
||||||
defer C.leveldb_get_free_ext(unsafe.Pointer(c))
|
defer C.leveldb_get_free_ext(unsafe.Pointer(c))
|
||||||
|
|
||||||
if r == nil {
|
return C.GoBytes(unsafe.Pointer(value), C.int(vallen)), nil
|
||||||
r = []byte{}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = r[0:0]
|
|
||||||
b := slice(unsafe.Pointer(value), int(C.int(vallen)))
|
|
||||||
return append(r, b...), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) delete(wo *WriteOptions, key []byte) error {
|
func (db *DB) delete(wo *WriteOptions, key []byte) error {
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lleveldb
|
// #cgo LDFLAGS: -lleveldb
|
|
@ -0,0 +1,70 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
|
package leveldb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lleveldb
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include "leveldb/c.h"
|
||||||
|
// #include "leveldb_ext.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Iterator struct {
|
||||||
|
it *C.leveldb_iterator_t
|
||||||
|
isValid C.uchar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Key() []byte {
|
||||||
|
var klen C.size_t
|
||||||
|
kdata := C.leveldb_iter_key(it.it, &klen)
|
||||||
|
if kdata == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice(unsafe.Pointer(kdata), int(C.int(klen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Value() []byte {
|
||||||
|
var vlen C.size_t
|
||||||
|
vdata := C.leveldb_iter_value(it.it, &vlen)
|
||||||
|
if vdata == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice(unsafe.Pointer(vdata), int(C.int(vlen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Close() error {
|
||||||
|
if it.it != nil {
|
||||||
|
C.leveldb_iter_destroy(it.it)
|
||||||
|
it.it = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Valid() bool {
|
||||||
|
return ucharToBool(it.isValid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Next() {
|
||||||
|
it.isValid = C.leveldb_iter_next_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Prev() {
|
||||||
|
it.isValid = C.leveldb_iter_prev_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) First() {
|
||||||
|
it.isValid = C.leveldb_iter_seek_to_first_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Last() {
|
||||||
|
it.isValid = C.leveldb_iter_seek_to_last_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Seek(key []byte) {
|
||||||
|
it.isValid = C.leveldb_iter_seek_ext(it.it, (*C.char)(unsafe.Pointer(&key[0])), C.size_t(len(key)))
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
#include "leveldb_ext.h"
|
#include "leveldb_ext.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
#ifndef LEVELDB_EXT_H
|
#ifndef LEVELDB_EXT_H
|
||||||
#define LEVELDB_EXT_H
|
#define LEVELDB_EXT_H
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lleveldb
|
// #cgo LDFLAGS: -lleveldb
|
||||||
|
@ -103,14 +105,6 @@ func (ro *ReadOptions) SetFillCache(b bool) {
|
||||||
C.leveldb_readoptions_set_fill_cache(ro.Opt, boolToUchar(b))
|
C.leveldb_readoptions_set_fill_cache(ro.Opt, boolToUchar(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ro *ReadOptions) SetSnapshot(snap *Snapshot) {
|
|
||||||
var s *C.leveldb_snapshot_t
|
|
||||||
if snap != nil {
|
|
||||||
s = snap.snap
|
|
||||||
}
|
|
||||||
C.leveldb_readoptions_set_snapshot(ro.Opt, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wo *WriteOptions) Close() {
|
func (wo *WriteOptions) Close() {
|
||||||
C.leveldb_writeoptions_destroy(wo.Opt)
|
C.leveldb_writeoptions_destroy(wo.Opt)
|
||||||
}
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build leveldb
|
||||||
|
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
// #include "leveldb/c.h"
|
// #include "leveldb/c.h"
|
|
@ -0,0 +1,30 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/copier"
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"github.com/siddontang/ledisdb/store/mdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LMDBName = "lmdb"
|
||||||
|
|
||||||
|
type LMDBStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s LMDBStore) Open(cfg *Config) (driver.IDB, error) {
|
||||||
|
c := &mdb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return mdb.Open(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s LMDBStore) Repair(cfg *Config) error {
|
||||||
|
c := &mdb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return mdb.Repair(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(LMDBName, LMDBStore{})
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package mdb
|
||||||
|
|
||||||
|
type Write struct {
|
||||||
|
Key []byte
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type WriteBatch struct {
|
||||||
|
db *MDB
|
||||||
|
wb []Write
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Put(key, value []byte) {
|
||||||
|
w.wb = append(w.wb, Write{key, value})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Delete(key []byte) {
|
||||||
|
w.wb = append(w.wb, Write{key, nil})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Commit() error {
|
||||||
|
return w.db.BatchPut(w.wb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Rollback() error {
|
||||||
|
w.wb = w.wb[0:0]
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2014 Errplane Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,255 @@
|
||||||
|
package mdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
mdb "github.com/influxdb/gomdb"
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
MapSize int `json:"map_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MDB struct {
|
||||||
|
env *mdb.Env
|
||||||
|
db mdb.DBI
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(c *Config) (MDB, error) {
|
||||||
|
path := c.Path
|
||||||
|
if c.MapSize == 0 {
|
||||||
|
c.MapSize = 1024 * 1024 * 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
env, err := mdb.NewEnv()
|
||||||
|
if err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: max dbs should be configurable
|
||||||
|
if err := env.SetMaxDBs(1); err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
if err := env.SetMapSize(uint64(c.MapSize)); err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
err = os.MkdirAll(path, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = env.Open(path, mdb.NOSYNC|mdb.NOMETASYNC|mdb.WRITEMAP|mdb.MAPASYNC|mdb.CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := env.BeginTxn(nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbi, err := tx.DBIOpen(nil, mdb.CREATE)
|
||||||
|
if err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return MDB{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db := MDB{
|
||||||
|
env: env,
|
||||||
|
db: dbi,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Repair(c *Config) error {
|
||||||
|
println("llmd not supports repair")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) Put(key, value []byte) error {
|
||||||
|
itr := db.iterator(false)
|
||||||
|
defer itr.Close()
|
||||||
|
|
||||||
|
itr.err = itr.c.Put(key, value, 0)
|
||||||
|
itr.setState()
|
||||||
|
return itr.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) BatchPut(writes []Write) error {
|
||||||
|
itr := db.iterator(false)
|
||||||
|
|
||||||
|
for _, w := range writes {
|
||||||
|
if w.Value == nil {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(w.Key, mdb.SET)
|
||||||
|
if itr.err == nil {
|
||||||
|
itr.err = itr.c.Del(0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itr.err = itr.c.Put(w.Key, w.Value, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if itr.err != nil && itr.err != mdb.NotFound {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itr.setState()
|
||||||
|
|
||||||
|
return itr.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) Get(key []byte) ([]byte, error) {
|
||||||
|
tx, err := db.env.BeginTxn(nil, mdb.RDONLY)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tx.Commit()
|
||||||
|
|
||||||
|
v, err := tx.Get(db.db, key)
|
||||||
|
if err == mdb.NotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) Delete(key []byte) error {
|
||||||
|
itr := db.iterator(false)
|
||||||
|
defer itr.Close()
|
||||||
|
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(key, mdb.SET)
|
||||||
|
if itr.err == nil {
|
||||||
|
itr.err = itr.c.Del(0)
|
||||||
|
}
|
||||||
|
itr.setState()
|
||||||
|
return itr.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
type MDBIterator struct {
|
||||||
|
key []byte
|
||||||
|
value []byte
|
||||||
|
c *mdb.Cursor
|
||||||
|
tx *mdb.Txn
|
||||||
|
valid bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Key() []byte {
|
||||||
|
return itr.key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Value() []byte {
|
||||||
|
return itr.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Valid() bool {
|
||||||
|
return itr.valid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Error() error {
|
||||||
|
return itr.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) getCurrent() {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.GET_CURRENT)
|
||||||
|
itr.setState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Seek(key []byte) {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(key, mdb.SET_RANGE)
|
||||||
|
itr.setState()
|
||||||
|
}
|
||||||
|
func (itr *MDBIterator) Next() {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.NEXT)
|
||||||
|
itr.setState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Prev() {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.PREV)
|
||||||
|
itr.setState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) First() {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.FIRST)
|
||||||
|
itr.setState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Last() {
|
||||||
|
itr.key, itr.value, itr.err = itr.c.Get(nil, mdb.LAST)
|
||||||
|
itr.setState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) setState() {
|
||||||
|
if itr.err != nil {
|
||||||
|
if itr.err == mdb.NotFound {
|
||||||
|
itr.err = nil
|
||||||
|
}
|
||||||
|
itr.valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (itr *MDBIterator) Close() error {
|
||||||
|
if err := itr.c.Close(); err != nil {
|
||||||
|
itr.tx.Abort()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if itr.err != nil {
|
||||||
|
itr.tx.Abort()
|
||||||
|
return itr.err
|
||||||
|
}
|
||||||
|
return itr.tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ MDB) Name() string {
|
||||||
|
return "lmdb"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) Path() string {
|
||||||
|
return db.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) Compact() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) iterator(rdonly bool) *MDBIterator {
|
||||||
|
flags := uint(0)
|
||||||
|
if rdonly {
|
||||||
|
flags = mdb.RDONLY
|
||||||
|
}
|
||||||
|
tx, err := db.env.BeginTxn(nil, flags)
|
||||||
|
if err != nil {
|
||||||
|
return &MDBIterator{nil, nil, nil, nil, false, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := tx.CursorOpen(db.db)
|
||||||
|
if err != nil {
|
||||||
|
tx.Abort()
|
||||||
|
return &MDBIterator{nil, nil, nil, nil, false, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MDBIterator{nil, nil, c, tx, true, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) Close() error {
|
||||||
|
db.env.DBIClose(db.db)
|
||||||
|
if err := db.env.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) NewIterator() driver.IIterator {
|
||||||
|
return db.iterator(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db MDB) NewWriteBatch() driver.IWriteBatch {
|
||||||
|
return &WriteBatch{&db, []Write{}}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/copier"
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"github.com/siddontang/ledisdb/store/rocksdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RocksDBName = "rocksdb"
|
||||||
|
|
||||||
|
type RocksDBStore struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RocksDBStore) Open(cfg *Config) (driver.IDB, error) {
|
||||||
|
c := &rocksdb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return rocksdb.Open(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RocksDBStore) Repair(cfg *Config) error {
|
||||||
|
c := &rocksdb.Config{}
|
||||||
|
copier.Copy(c, cfg)
|
||||||
|
|
||||||
|
return rocksdb.Repair(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(RocksDBName, RocksDBStore{})
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lrocksdb
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WriteBatch struct {
|
||||||
|
db *DB
|
||||||
|
wbatch *C.rocksdb_writebatch_t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Close() error {
|
||||||
|
C.rocksdb_writebatch_destroy(w.wbatch)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Put(key, value []byte) {
|
||||||
|
var k, v *C.char
|
||||||
|
if len(key) != 0 {
|
||||||
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
||||||
|
}
|
||||||
|
if len(value) != 0 {
|
||||||
|
v = (*C.char)(unsafe.Pointer(&value[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
lenk := len(key)
|
||||||
|
lenv := len(value)
|
||||||
|
|
||||||
|
C.rocksdb_writebatch_put(w.wbatch, k, C.size_t(lenk), v, C.size_t(lenv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Delete(key []byte) {
|
||||||
|
C.rocksdb_writebatch_delete(w.wbatch,
|
||||||
|
(*C.char)(unsafe.Pointer(&key[0])), C.size_t(len(key)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Commit() error {
|
||||||
|
return w.commit(w.db.writeOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) Rollback() error {
|
||||||
|
C.rocksdb_writebatch_clear(w.wbatch)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WriteBatch) commit(wb *WriteOptions) error {
|
||||||
|
var errStr *C.char
|
||||||
|
C.rocksdb_write(w.db.db, wb.Opt, w.wbatch, &errStr)
|
||||||
|
if errStr != nil {
|
||||||
|
return saveError(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lrocksdb
|
||||||
|
// #include <stdint.h>
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
Cache *C.rocksdb_cache_t
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLRUCache(capacity int) *Cache {
|
||||||
|
return &Cache{C.rocksdb_cache_create_lru(C.size_t(capacity))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Close() {
|
||||||
|
C.rocksdb_cache_destroy(c.Cache)
|
||||||
|
}
|
|
@ -0,0 +1,279 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
// Package rocksdb is a wrapper for c++ rocksdb
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -lrocksdb
|
||||||
|
#include <rocksdb/c.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "rocksdb_ext.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"`
|
||||||
|
MaxOpenFiles int `json:"max_open_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(cfg *Config) (*DB, error) {
|
||||||
|
if err := os.MkdirAll(cfg.Path, os.ModePerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db := new(DB)
|
||||||
|
db.cfg = cfg
|
||||||
|
|
||||||
|
if err := db.open(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Repair(cfg *Config) error {
|
||||||
|
db := new(DB)
|
||||||
|
db.cfg = cfg
|
||||||
|
|
||||||
|
err := db.open()
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
//open ok, do not need repair
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errStr *C.char
|
||||||
|
ldbname := C.CString(db.cfg.Path)
|
||||||
|
defer C.free(unsafe.Pointer(ldbname))
|
||||||
|
|
||||||
|
C.rocksdb_repair_db(db.opts.Opt, ldbname, &errStr)
|
||||||
|
if errStr != nil {
|
||||||
|
return saveError(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
cfg *Config
|
||||||
|
|
||||||
|
db *C.rocksdb_t
|
||||||
|
|
||||||
|
env *Env
|
||||||
|
|
||||||
|
opts *Options
|
||||||
|
|
||||||
|
//for default read and write options
|
||||||
|
readOpts *ReadOptions
|
||||||
|
writeOpts *WriteOptions
|
||||||
|
iteratorOpts *ReadOptions
|
||||||
|
|
||||||
|
cache *Cache
|
||||||
|
|
||||||
|
filter *FilterPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) open() error {
|
||||||
|
db.initOptions(db.cfg)
|
||||||
|
|
||||||
|
var errStr *C.char
|
||||||
|
ldbname := C.CString(db.cfg.Path)
|
||||||
|
defer C.free(unsafe.Pointer(ldbname))
|
||||||
|
|
||||||
|
db.db = C.rocksdb_open(db.opts.Opt, ldbname, &errStr)
|
||||||
|
if errStr != nil {
|
||||||
|
db.db = nil
|
||||||
|
return saveError(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) initOptions(cfg *Config) {
|
||||||
|
opts := NewOptions()
|
||||||
|
|
||||||
|
opts.SetCreateIfMissing(true)
|
||||||
|
|
||||||
|
if cfg.CacheSize <= 0 {
|
||||||
|
cfg.CacheSize = 4 * 1024 * 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
db.env = NewDefaultEnv()
|
||||||
|
db.env.SetBackgroundThreads(runtime.NumCPU() * 2)
|
||||||
|
db.env.SetHighPriorityBackgroundThreads(1)
|
||||||
|
opts.SetEnv(db.env)
|
||||||
|
|
||||||
|
db.cache = NewLRUCache(cfg.CacheSize)
|
||||||
|
opts.SetCache(db.cache)
|
||||||
|
|
||||||
|
//we must use bloomfilter
|
||||||
|
db.filter = NewBloomFilter(defaultFilterBits)
|
||||||
|
opts.SetFilterPolicy(db.filter)
|
||||||
|
|
||||||
|
if !cfg.Compression {
|
||||||
|
opts.SetCompression(NoCompression)
|
||||||
|
} else {
|
||||||
|
opts.SetCompression(SnappyCompression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.BlockSize <= 0 {
|
||||||
|
cfg.BlockSize = 4 * 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.SetBlockSize(cfg.BlockSize)
|
||||||
|
|
||||||
|
if cfg.WriteBufferSize <= 0 {
|
||||||
|
cfg.WriteBufferSize = 4 * 1024 * 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.SetWriteBufferSize(cfg.WriteBufferSize)
|
||||||
|
|
||||||
|
if cfg.MaxOpenFiles < 1024 {
|
||||||
|
cfg.MaxOpenFiles = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.SetMaxOpenFiles(cfg.MaxOpenFiles)
|
||||||
|
|
||||||
|
opts.SetMaxBackgroundCompactions(runtime.NumCPU()*2 - 1)
|
||||||
|
opts.SetMaxBackgroundFlushes(1)
|
||||||
|
|
||||||
|
opts.SetLevel0SlowdownWritesTrigger(16)
|
||||||
|
opts.SetLevel0StopWritesTrigger(64)
|
||||||
|
opts.SetTargetFileSizeBase(32 * 1024 * 1024)
|
||||||
|
|
||||||
|
db.opts = opts
|
||||||
|
|
||||||
|
db.readOpts = NewReadOptions()
|
||||||
|
db.writeOpts = NewWriteOptions()
|
||||||
|
|
||||||
|
db.iteratorOpts = NewReadOptions()
|
||||||
|
db.iteratorOpts.SetFillCache(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Close() error {
|
||||||
|
if db.db != nil {
|
||||||
|
C.rocksdb_close(db.db)
|
||||||
|
db.db = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
db.opts.Close()
|
||||||
|
|
||||||
|
if db.cache != nil {
|
||||||
|
db.cache.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.filter != nil {
|
||||||
|
db.filter.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.env != nil {
|
||||||
|
db.env.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
db.readOpts.Close()
|
||||||
|
db.writeOpts.Close()
|
||||||
|
db.iteratorOpts.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Put(key, value []byte) error {
|
||||||
|
return db.put(db.writeOpts, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Get(key []byte) ([]byte, error) {
|
||||||
|
return db.get(db.readOpts, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Delete(key []byte) error {
|
||||||
|
return db.delete(db.writeOpts, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) NewWriteBatch() driver.IWriteBatch {
|
||||||
|
wb := &WriteBatch{
|
||||||
|
db: db,
|
||||||
|
wbatch: C.rocksdb_writebatch_create(),
|
||||||
|
}
|
||||||
|
return wb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) NewIterator() driver.IIterator {
|
||||||
|
it := new(Iterator)
|
||||||
|
|
||||||
|
it.it = C.rocksdb_create_iterator(db.db, db.iteratorOpts.Opt)
|
||||||
|
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) put(wo *WriteOptions, key, value []byte) error {
|
||||||
|
var errStr *C.char
|
||||||
|
var k, v *C.char
|
||||||
|
if len(key) != 0 {
|
||||||
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
||||||
|
}
|
||||||
|
if len(value) != 0 {
|
||||||
|
v = (*C.char)(unsafe.Pointer(&value[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
lenk := len(key)
|
||||||
|
lenv := len(value)
|
||||||
|
C.rocksdb_put(
|
||||||
|
db.db, wo.Opt, k, C.size_t(lenk), v, C.size_t(lenv), &errStr)
|
||||||
|
|
||||||
|
if errStr != nil {
|
||||||
|
return saveError(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) get(ro *ReadOptions, key []byte) ([]byte, error) {
|
||||||
|
var errStr *C.char
|
||||||
|
var vallen C.size_t
|
||||||
|
var k *C.char
|
||||||
|
if len(key) != 0 {
|
||||||
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
value := C.rocksdb_get(
|
||||||
|
db.db, ro.Opt, k, C.size_t(len(key)), &vallen, &errStr)
|
||||||
|
|
||||||
|
if errStr != nil {
|
||||||
|
return nil, saveError(errStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer C.free(unsafe.Pointer(value))
|
||||||
|
return C.GoBytes(unsafe.Pointer(value), C.int(vallen)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) delete(wo *WriteOptions, key []byte) error {
|
||||||
|
var errStr *C.char
|
||||||
|
var k *C.char
|
||||||
|
if len(key) != 0 {
|
||||||
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
C.rocksdb_delete(
|
||||||
|
db.db, wo.Opt, k, C.size_t(len(key)), &errStr)
|
||||||
|
|
||||||
|
if errStr != nil {
|
||||||
|
return saveError(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lrocksdb
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
Env *C.rocksdb_env_t
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultEnv() *Env {
|
||||||
|
return &Env{C.rocksdb_create_default_env()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Env) SetHighPriorityBackgroundThreads(n int) {
|
||||||
|
C.rocksdb_env_set_high_priority_background_threads(env.Env, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Env) SetBackgroundThreads(n int) {
|
||||||
|
C.rocksdb_env_set_background_threads(env.Env, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Env) Close() {
|
||||||
|
C.rocksdb_env_destroy(env.Env)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lrocksdb
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type FilterPolicy struct {
|
||||||
|
Policy *C.rocksdb_filterpolicy_t
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBloomFilter(bitsPerKey int) *FilterPolicy {
|
||||||
|
policy := C.rocksdb_filterpolicy_create_bloom(C.int(bitsPerKey))
|
||||||
|
return &FilterPolicy{policy}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fp *FilterPolicy) Close() {
|
||||||
|
C.rocksdb_filterpolicy_destroy(fp.Policy)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lrocksdb
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
// #include "rocksdb_ext.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Iterator struct {
|
||||||
|
it *C.rocksdb_iterator_t
|
||||||
|
isValid C.uchar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Key() []byte {
|
||||||
|
var klen C.size_t
|
||||||
|
kdata := C.rocksdb_iter_key(it.it, &klen)
|
||||||
|
if kdata == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice(unsafe.Pointer(kdata), int(C.int(klen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Value() []byte {
|
||||||
|
var vlen C.size_t
|
||||||
|
vdata := C.rocksdb_iter_value(it.it, &vlen)
|
||||||
|
if vdata == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice(unsafe.Pointer(vdata), int(C.int(vlen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Close() error {
|
||||||
|
if it.it != nil {
|
||||||
|
C.rocksdb_iter_destroy(it.it)
|
||||||
|
it.it = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Valid() bool {
|
||||||
|
return ucharToBool(it.isValid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Next() {
|
||||||
|
it.isValid = C.rocksdb_iter_next_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Prev() {
|
||||||
|
it.isValid = C.rocksdb_iter_prev_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) First() {
|
||||||
|
it.isValid = C.rocksdb_iter_seek_to_first_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Last() {
|
||||||
|
it.isValid = C.rocksdb_iter_seek_to_last_ext(it.it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator) Seek(key []byte) {
|
||||||
|
it.isValid = C.rocksdb_iter_seek_ext(it.it, (*C.char)(unsafe.Pointer(&key[0])), C.size_t(len(key)))
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lrocksdb
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type CompressionOpt int
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoCompression = CompressionOpt(0)
|
||||||
|
SnappyCompression = CompressionOpt(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
Opt *C.rocksdb_options_t
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReadOptions struct {
|
||||||
|
Opt *C.rocksdb_readoptions_t
|
||||||
|
}
|
||||||
|
|
||||||
|
type WriteOptions struct {
|
||||||
|
Opt *C.rocksdb_writeoptions_t
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOptions() *Options {
|
||||||
|
opt := C.rocksdb_options_create()
|
||||||
|
return &Options{opt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReadOptions() *ReadOptions {
|
||||||
|
opt := C.rocksdb_readoptions_create()
|
||||||
|
return &ReadOptions{opt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriteOptions() *WriteOptions {
|
||||||
|
opt := C.rocksdb_writeoptions_create()
|
||||||
|
return &WriteOptions{opt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) Close() {
|
||||||
|
C.rocksdb_options_destroy(o.Opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetComparator(cmp *C.rocksdb_comparator_t) {
|
||||||
|
C.rocksdb_options_set_comparator(o.Opt, cmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetErrorIfExists(error_if_exists bool) {
|
||||||
|
eie := boolToUchar(error_if_exists)
|
||||||
|
C.rocksdb_options_set_error_if_exists(o.Opt, eie)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetCache(cache *Cache) {
|
||||||
|
C.rocksdb_options_set_cache(o.Opt, cache.Cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetEnv(env *Env) {
|
||||||
|
C.rocksdb_options_set_env(o.Opt, env.Env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetWriteBufferSize(s int) {
|
||||||
|
C.rocksdb_options_set_write_buffer_size(o.Opt, C.size_t(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetParanoidChecks(pc bool) {
|
||||||
|
C.rocksdb_options_set_paranoid_checks(o.Opt, boolToUchar(pc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetMaxOpenFiles(n int) {
|
||||||
|
C.rocksdb_options_set_max_open_files(o.Opt, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetBlockSize(s int) {
|
||||||
|
C.rocksdb_options_set_block_size(o.Opt, C.size_t(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetBlockRestartInterval(n int) {
|
||||||
|
C.rocksdb_options_set_block_restart_interval(o.Opt, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetCompression(t CompressionOpt) {
|
||||||
|
C.rocksdb_options_set_compression(o.Opt, C.int(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetCreateIfMissing(b bool) {
|
||||||
|
C.rocksdb_options_set_create_if_missing(o.Opt, boolToUchar(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetFilterPolicy(fp *FilterPolicy) {
|
||||||
|
var policy *C.rocksdb_filterpolicy_t
|
||||||
|
if fp != nil {
|
||||||
|
policy = fp.Policy
|
||||||
|
}
|
||||||
|
C.rocksdb_options_set_filter_policy(o.Opt, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetMaxBackgroundCompactions(n int) {
|
||||||
|
C.rocksdb_options_set_max_background_compactions(o.Opt, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetMaxBackgroundFlushes(n int) {
|
||||||
|
C.rocksdb_options_set_max_background_flushes(o.Opt, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetLevel0SlowdownWritesTrigger(n int) {
|
||||||
|
C.rocksdb_options_set_level0_slowdown_writes_trigger(o.Opt, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetLevel0StopWritesTrigger(n int) {
|
||||||
|
C.rocksdb_options_set_level0_stop_writes_trigger(o.Opt, C.int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetTargetFileSizeBase(n int) {
|
||||||
|
C.rocksdb_options_set_target_file_size_base(o.Opt, C.uint64_t(uint64(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ro *ReadOptions) Close() {
|
||||||
|
C.rocksdb_readoptions_destroy(ro.Opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ro *ReadOptions) SetVerifyChecksums(b bool) {
|
||||||
|
C.rocksdb_readoptions_set_verify_checksums(ro.Opt, boolToUchar(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ro *ReadOptions) SetFillCache(b bool) {
|
||||||
|
C.rocksdb_readoptions_set_fill_cache(ro.Opt, boolToUchar(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wo *WriteOptions) Close() {
|
||||||
|
C.rocksdb_writeoptions_destroy(wo.Opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wo *WriteOptions) SetSync(b bool) {
|
||||||
|
C.rocksdb_writeoptions_set_sync(wo.Opt, boolToUchar(b))
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
#include "rocksdb_ext.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
unsigned char rocksdb_iter_seek_to_first_ext(rocksdb_iterator_t* iter) {
|
||||||
|
rocksdb_iter_seek_to_first(iter);
|
||||||
|
return rocksdb_iter_valid(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char rocksdb_iter_seek_to_last_ext(rocksdb_iterator_t* iter) {
|
||||||
|
rocksdb_iter_seek_to_last(iter);
|
||||||
|
return rocksdb_iter_valid(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char rocksdb_iter_seek_ext(rocksdb_iterator_t* iter, const char* k, size_t klen) {
|
||||||
|
rocksdb_iter_seek(iter, k, klen);
|
||||||
|
return rocksdb_iter_valid(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char rocksdb_iter_next_ext(rocksdb_iterator_t* iter) {
|
||||||
|
rocksdb_iter_next(iter);
|
||||||
|
return rocksdb_iter_valid(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char rocksdb_iter_prev_ext(rocksdb_iterator_t* iter) {
|
||||||
|
rocksdb_iter_prev(iter);
|
||||||
|
return rocksdb_iter_valid(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
#ifndef ROCKSDB_EXT_H
|
||||||
|
#define ROCKSDB_EXT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "rocksdb/c.h"
|
||||||
|
|
||||||
|
// Below iterator functions like rocksdb iterator but returns valid status for iterator
|
||||||
|
extern unsigned char rocksdb_iter_seek_to_first_ext(rocksdb_iterator_t*);
|
||||||
|
extern unsigned char rocksdb_iter_seek_to_last_ext(rocksdb_iterator_t*);
|
||||||
|
extern unsigned char rocksdb_iter_seek_ext(rocksdb_iterator_t*, const char* k, size_t klen);
|
||||||
|
extern unsigned char rocksdb_iter_next_ext(rocksdb_iterator_t*);
|
||||||
|
extern unsigned char rocksdb_iter_prev_ext(rocksdb_iterator_t*);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,46 @@
|
||||||
|
// +build rocksdb
|
||||||
|
|
||||||
|
package rocksdb
|
||||||
|
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include "rocksdb/c.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func boolToUchar(b bool) C.uchar {
|
||||||
|
uc := C.uchar(0)
|
||||||
|
if b {
|
||||||
|
uc = C.uchar(1)
|
||||||
|
}
|
||||||
|
return uc
|
||||||
|
}
|
||||||
|
|
||||||
|
func ucharToBool(uc C.uchar) bool {
|
||||||
|
if uc == C.uchar(0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveError(errStr *C.char) error {
|
||||||
|
if errStr != nil {
|
||||||
|
gs := C.GoString(errStr)
|
||||||
|
C.free(unsafe.Pointer(errStr))
|
||||||
|
return fmt.Errorf(gs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func slice(p unsafe.Pointer, n int) []byte {
|
||||||
|
var b []byte
|
||||||
|
pbyte := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
pbyte.Data = uintptr(p)
|
||||||
|
pbyte.Len = n
|
||||||
|
pbyte.Cap = n
|
||||||
|
return b
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultStoreName = "goleveldb"
|
||||||
|
|
||||||
|
type Store interface {
|
||||||
|
Open(cfg *Config) (driver.IDB, error)
|
||||||
|
Repair(cfg *Config) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbs = map[string]Store{}
|
||||||
|
|
||||||
|
func Register(name string, store Store) {
|
||||||
|
if _, ok := dbs[name]; ok {
|
||||||
|
panic(fmt.Errorf("store %s is registered", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
dbs[name] = store
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListStores() []string {
|
||||||
|
s := []string{}
|
||||||
|
for k, _ := range dbs {
|
||||||
|
s = append(s, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(cfg *Config) (*DB, error) {
|
||||||
|
if len(cfg.Name) == 0 {
|
||||||
|
cfg.Name = DefaultStoreName
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := dbs[cfg.Name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("store %s is not registered", cfg.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(cfg.Path, os.ModePerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idb, err := s.Open(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db := &DB{idb}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Repair(cfg *Config) error {
|
||||||
|
if len(cfg.Name) == 0 {
|
||||||
|
cfg.Name = DefaultStoreName
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := dbs[cfg.Name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("db %s is not registered", cfg.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Repair(cfg)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package leveldb
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -8,23 +8,17 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testConfigJson = []byte(`
|
|
||||||
{
|
|
||||||
"path" : "./testdb",
|
|
||||||
"compression":true,
|
|
||||||
"block_size" : 32768,
|
|
||||||
"write_buffer_size" : 2097152,
|
|
||||||
"cache_size" : 20971520
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
var testOnce sync.Once
|
var testOnce sync.Once
|
||||||
var testDB *DB
|
var testDB *DB
|
||||||
|
|
||||||
func getTestDB() *DB {
|
func getTestDB() *DB {
|
||||||
f := func() {
|
f := func() {
|
||||||
var err error
|
var err error
|
||||||
testDB, err = OpenWithJsonConfig(testConfigJson)
|
|
||||||
|
cfg := new(Config)
|
||||||
|
cfg.Path = "/tmp/testdb"
|
||||||
|
|
||||||
|
testDB, err = Open(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -131,7 +125,11 @@ func checkIterator(it *RangeLimitIterator, cv ...int) error {
|
||||||
func TestIterator(t *testing.T) {
|
func TestIterator(t *testing.T) {
|
||||||
db := getTestDB()
|
db := getTestDB()
|
||||||
|
|
||||||
db.Clear()
|
i := db.NewIterator()
|
||||||
|
for i.SeekToFirst(); i.Valid(); i.Next() {
|
||||||
|
db.Delete(i.Key())
|
||||||
|
}
|
||||||
|
i.Close()
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
key := []byte(fmt.Sprintf("key_%d", i))
|
key := []byte(fmt.Sprintf("key_%d", i))
|
||||||
|
@ -139,6 +137,16 @@ func TestIterator(t *testing.T) {
|
||||||
db.Put(key, value)
|
db.Put(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i = db.NewIterator()
|
||||||
|
i.SeekToFirst()
|
||||||
|
|
||||||
|
if !i.Valid() {
|
||||||
|
t.Fatal("must valid")
|
||||||
|
} else if string(i.Key()) != "key_0" {
|
||||||
|
t.Fatal(string(i.Key()))
|
||||||
|
}
|
||||||
|
i.Close()
|
||||||
|
|
||||||
var it *RangeLimitIterator
|
var it *RangeLimitIterator
|
||||||
|
|
||||||
k := func(i int) []byte {
|
k := func(i int) []byte {
|
||||||
|
@ -149,96 +157,67 @@ func TestIterator(t *testing.T) {
|
||||||
if err := checkIterator(it, 1, 2, 3, 4, 5); err != nil {
|
if err := checkIterator(it, 1, 2, 3, 4, 5); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
|
it = db.RangeLimitIterator(k(1), k(5), RangeClose, 0, -1)
|
||||||
|
if err := checkIterator(it, 1, 2, 3, 4, 5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RangeLimitIterator(k(1), k(5), RangeClose, 1, 3)
|
it = db.RangeLimitIterator(k(1), k(5), RangeClose, 1, 3)
|
||||||
if err := checkIterator(it, 2, 3, 4); err != nil {
|
if err := checkIterator(it, 2, 3, 4); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RangeLimitIterator(k(1), k(5), RangeLOpen, 0, -1)
|
it = db.RangeLimitIterator(k(1), k(5), RangeLOpen, 0, -1)
|
||||||
if err := checkIterator(it, 2, 3, 4, 5); err != nil {
|
if err := checkIterator(it, 2, 3, 4, 5); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RangeLimitIterator(k(1), k(5), RangeROpen, 0, -1)
|
it = db.RangeLimitIterator(k(1), k(5), RangeROpen, 0, -1)
|
||||||
if err := checkIterator(it, 1, 2, 3, 4); err != nil {
|
if err := checkIterator(it, 1, 2, 3, 4); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RangeLimitIterator(k(1), k(5), RangeOpen, 0, -1)
|
it = db.RangeLimitIterator(k(1), k(5), RangeOpen, 0, -1)
|
||||||
if err := checkIterator(it, 2, 3, 4); err != nil {
|
if err := checkIterator(it, 2, 3, 4); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RevRangeLimitIterator(k(1), k(5), RangeClose, 0, -1)
|
it = db.RevRangeLimitIterator(k(1), k(5), RangeClose, 0, -1)
|
||||||
if err := checkIterator(it, 5, 4, 3, 2, 1); err != nil {
|
if err := checkIterator(it, 5, 4, 3, 2, 1); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RevRangeLimitIterator(k(1), k(5), RangeClose, 1, 3)
|
it = db.RevRangeLimitIterator(k(1), k(5), RangeClose, 1, 3)
|
||||||
if err := checkIterator(it, 4, 3, 2); err != nil {
|
if err := checkIterator(it, 4, 3, 2); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RevRangeLimitIterator(k(1), k(5), RangeLOpen, 0, -1)
|
it = db.RevRangeLimitIterator(k(1), k(5), RangeLOpen, 0, -1)
|
||||||
if err := checkIterator(it, 5, 4, 3, 2); err != nil {
|
if err := checkIterator(it, 5, 4, 3, 2); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RevRangeLimitIterator(k(1), k(5), RangeROpen, 0, -1)
|
it = db.RevRangeLimitIterator(k(1), k(5), RangeROpen, 0, -1)
|
||||||
if err := checkIterator(it, 4, 3, 2, 1); err != nil {
|
if err := checkIterator(it, 4, 3, 2, 1); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
it.Close()
|
||||||
|
|
||||||
it = db.RevRangeLimitIterator(k(1), k(5), RangeOpen, 0, -1)
|
it = db.RevRangeLimitIterator(k(1), k(5), RangeOpen, 0, -1)
|
||||||
if err := checkIterator(it, 4, 3, 2); err != nil {
|
if err := checkIterator(it, 4, 3, 2); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
it.Close()
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDestroy(t *testing.T) {
|
|
||||||
db := getTestDB()
|
|
||||||
|
|
||||||
db.Put([]byte("a"), []byte("1"))
|
|
||||||
if err := db.Clear(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(db.cfg.Path); err != nil {
|
|
||||||
t.Fatal("must exist ", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := db.Get([]byte("a")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if string(v) == "1" {
|
|
||||||
t.Fatal(string(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Destroy()
|
|
||||||
|
|
||||||
if _, err := os.Stat(db.cfg.Path); !os.IsNotExist(err) {
|
|
||||||
t.Fatal("must not exist")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCloseMore(t *testing.T) {
|
func TestCloseMore(t *testing.T) {
|
||||||
|
@ -256,4 +235,6 @@ func TestCloseMore(t *testing.T) {
|
||||||
|
|
||||||
db.Close()
|
db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.RemoveAll(cfg.Path)
|
||||||
}
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/siddontang/ledisdb/store/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WriteBatch struct {
|
||||||
|
driver.IWriteBatch
|
||||||
|
}
|
Loading…
Reference in New Issue