Skip to content

Commit 40bc2af

Browse files
authored
Merge pull request #13 from feifeiiiiiiiiiii/feature/leveldb
feature: add leveldb engine for experiment
2 parents 285a07d + 5b713c6 commit 40bc2af

File tree

5 files changed

+247
-0
lines changed

5 files changed

+247
-0
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect
1010
github.com/dustin/go-humanize v1.0.0 // indirect
1111
github.com/golang/protobuf v1.2.0 // indirect
12+
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
1213
github.com/gorilla/websocket v1.4.0
1314
github.com/kr/pretty v0.1.0 // indirect
1415
github.com/kr/pty v1.1.3 // indirect
@@ -20,6 +21,7 @@ require (
2021
github.com/rs/xid v1.2.1
2122
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
2223
github.com/sirupsen/logrus v1.2.0
24+
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2
2325
github.com/tidwall/redcon v0.9.0
2426
github.com/valyala/bytebufferpool v1.0.0 // indirect
2527
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
1616
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
1717
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
1818
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
19+
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
20+
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
1921
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
2022
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
2123
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
@@ -47,6 +49,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
4749
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
4850
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
4951
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
52+
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2 h1:GnOzE5fEFN3b2zDhJJABEofdb51uMRNb8eqIVtdducs=
53+
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
5054
github.com/tidwall/redcon v0.9.0 h1:tiT9DLAoohsdNaFg9Si5dRsv9+FjvZYnhMOEtSFwBqA=
5155
github.com/tidwall/redcon v0.9.0/go.mod h1:bdYBm4rlcWpst2XMwKVzWDF9CoUxEbUmM7CQrKeOZas=
5256
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=

helpers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/alash3al/redix/kvstore/bolt"
12+
"github.com/alash3al/redix/kvstore/leveldb"
1213

1314
"github.com/alash3al/redix/kvstore/badger"
1415

@@ -41,5 +42,7 @@ func openDB(engine, dbpath string) (kvstore.DB, error) {
4142
return badger.OpenBadger(dbpath)
4243
case "bolt", "boltdb":
4344
return bolt.OpenBolt(dbpath)
45+
case "level", "leveldb":
46+
return leveldb.OpenLevelDB(dbpath)
4447
}
4548
}

kvstore/leveldb/leveldb.go

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Copyright 2018 The Redix Authors. All rights reserved.
2+
// Use of this source code is governed by a Apache 2.0
3+
// license that can be found in the LICENSE file.
4+
//
5+
// leveldb is a db engine based on leveldb
6+
package leveldb
7+
8+
import (
9+
"bytes"
10+
"errors"
11+
"fmt"
12+
"strconv"
13+
"strings"
14+
"sync"
15+
"time"
16+
17+
"github.com/alash3al/redix/kvstore"
18+
"github.com/syndtr/goleveldb/leveldb"
19+
"github.com/syndtr/goleveldb/leveldb/iterator"
20+
"github.com/syndtr/goleveldb/leveldb/util"
21+
)
22+
23+
// LevelDB - represents a leveldb db implementation
24+
type LevelDB struct {
25+
db *leveldb.DB
26+
sync.RWMutex
27+
}
28+
29+
// OpenLevelDB - Opens the specified path
30+
func OpenLevelDB(path string) (*LevelDB, error) {
31+
db, err := leveldb.OpenFile(path, nil)
32+
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
ldb := new(LevelDB)
38+
ldb.db = db
39+
40+
return ldb, nil
41+
}
42+
43+
// Size - returns the size of the database in bytes
44+
func (ldb *LevelDB) Size() int64 {
45+
ldb.RLock()
46+
defer ldb.RUnlock()
47+
48+
// TODO need to find efficiency method stat dbsize
49+
return int64(0)
50+
}
51+
52+
// GC - runs the garbage collector
53+
func (ldb *LevelDB) GC() error {
54+
return nil
55+
}
56+
57+
// Incr - increment the key by the specified value
58+
func (ldb *LevelDB) Incr(k string, by int64) (int64, error) {
59+
ldb.Lock()
60+
defer ldb.Unlock()
61+
62+
val, err := ldb.get(k)
63+
if err != nil {
64+
val = ""
65+
}
66+
67+
valFloat, _ := strconv.ParseInt(val, 10, 64)
68+
valFloat += by
69+
70+
err = ldb.set(k, fmt.Sprintf("%d", valFloat), -1)
71+
if err != nil {
72+
return 0, err
73+
}
74+
75+
return valFloat, nil
76+
}
77+
78+
func (ldb *LevelDB) set(k, v string, ttl int) error {
79+
var expires int64
80+
if ttl > 0 {
81+
expires = time.Now().Add(time.Duration(ttl) * time.Millisecond).Unix()
82+
}
83+
v = strconv.Itoa(int(expires)) + ";" + v
84+
return ldb.db.Put([]byte(k), []byte(v), nil)
85+
}
86+
87+
// Set - sets a key with the specified value and optional ttl
88+
func (ldb *LevelDB) Set(k, v string, ttl int) error {
89+
ldb.Lock()
90+
defer ldb.Unlock()
91+
92+
return ldb.set(k, v, ttl)
93+
}
94+
95+
// MSet - sets multiple key-value pairs
96+
func (ldb *LevelDB) MSet(data map[string]string) error {
97+
ldb.Lock()
98+
defer ldb.Unlock()
99+
100+
batch := new(leveldb.Batch)
101+
for k, v := range data {
102+
v = "0;" + v
103+
batch.Put([]byte(k), []byte(v))
104+
}
105+
106+
return ldb.db.Write(batch, nil)
107+
}
108+
109+
func (ldb *LevelDB) get(k string) (string, error) {
110+
var data string
111+
var err error
112+
113+
delete := false
114+
115+
item, err := ldb.db.Get([]byte(k), nil)
116+
if err != nil {
117+
return "", err
118+
}
119+
120+
parts := strings.SplitN(string(item), ";", 2)
121+
expires, actual := parts[0], parts[1]
122+
123+
if exp, _ := strconv.Atoi(expires); exp > 0 && int(time.Now().Unix()) >= exp {
124+
delete = true
125+
err = errors.New("key not found")
126+
} else {
127+
data = actual
128+
}
129+
130+
if delete {
131+
ldb.db.Delete([]byte(k), nil)
132+
return data, errors.New("key not found")
133+
}
134+
135+
return data, nil
136+
}
137+
138+
// Get - fetches the value of the specified k
139+
func (ldb *LevelDB) Get(k string) (string, error) {
140+
ldb.RLock()
141+
defer ldb.RUnlock()
142+
143+
return ldb.get(k)
144+
}
145+
146+
// MGet - fetch multiple values of the specified keys
147+
func (ldb *LevelDB) MGet(keys []string) (data []string) {
148+
ldb.RLock()
149+
defer ldb.RUnlock()
150+
151+
for _, key := range keys {
152+
val, err := ldb.get(key)
153+
if err != nil {
154+
data = append(data, "")
155+
continue
156+
}
157+
data = append(data, val)
158+
}
159+
return data
160+
}
161+
162+
// TTL - returns the time to live of the specified key's value
163+
func (ldb *LevelDB) TTL(key string) int64 {
164+
ldb.RLock()
165+
defer ldb.RUnlock()
166+
167+
item, err := ldb.db.Get([]byte(key), nil)
168+
if err != nil {
169+
return -2
170+
}
171+
172+
parts := strings.SplitN(string(item), ";", 2)
173+
exp, _ := strconv.Atoi(parts[0])
174+
if exp == 0 {
175+
return -1
176+
}
177+
178+
if int(time.Now().Unix()) >= exp {
179+
return -2
180+
}
181+
182+
return int64(exp)
183+
}
184+
185+
// Del - removes key(s) from the store
186+
func (ldb *LevelDB) Del(keys []string) error {
187+
ldb.Lock()
188+
defer ldb.Unlock()
189+
190+
batch := new(leveldb.Batch)
191+
for _, key := range keys {
192+
batch.Delete([]byte(key))
193+
}
194+
195+
return ldb.db.Write(batch, nil)
196+
}
197+
198+
// Scan - iterate over the whole store using the handler function
199+
func (ldb *LevelDB) Scan(scannerOpt kvstore.ScannerOptions) error {
200+
ldb.RLock()
201+
defer ldb.RUnlock()
202+
203+
var iter iterator.Iterator
204+
205+
if scannerOpt.Offset == "" {
206+
iter = ldb.db.NewIterator(nil, nil)
207+
} else {
208+
iter = ldb.db.NewIterator(&util.Range{Start: []byte(scannerOpt.Offset)}, nil)
209+
if !scannerOpt.IncludeOffset {
210+
iter.Next()
211+
}
212+
}
213+
214+
valid := func(k []byte) bool {
215+
if k == nil {
216+
return false
217+
}
218+
219+
if scannerOpt.Prefix != "" && !bytes.HasPrefix(k, []byte(scannerOpt.Prefix)) {
220+
return false
221+
}
222+
223+
return true
224+
}
225+
226+
for iter.Next() {
227+
key := iter.Key()
228+
val := strings.SplitN(string(iter.Value()), ";", 2)[1]
229+
if valid(key) && !scannerOpt.Handler(string(key), string(val)) {
230+
break
231+
}
232+
}
233+
iter.Release()
234+
235+
return iter.Error()
236+
}

vars.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ var (
9797
"badgerdb": true,
9898
"bolt": true,
9999
"boltdb": true,
100+
"level": true,
101+
"leveldb": true,
100102
}
101103

102104
redixBrand = `

0 commit comments

Comments
 (0)