Skip to content

Commit e05d1a3

Browse files
committed
removed last-key from lrange if there is no limit needed
1 parent 9347699 commit e05d1a3

File tree

3 files changed

+219
-9
lines changed

3 files changed

+219
-9
lines changed

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Features
1919
=========
2020
- Core data structure: `KV`, `List`, `Hashmap` with advanced implementations.
2121
- Advanced Publish/Subscribe using webhook and websocket!
22-
- Pluggable Storage Engine (`badgerdb`, `boltdb`, `leveldb`)
22+
- Pluggable Storage Engine (`badgerdb`, `boltdb`, `leveldb`, `null`, `sqlite`)
2323
- Very compatible with any `redis client` including `redis-cli`
2424
- Standalone with no external dependencies
2525
- Helpers commands for `Time`, `Encode <hex|md5|sha1|sha256|sha512> <payload>`, `RANDINT`, `RANDSTR`
@@ -202,11 +202,12 @@ TODO
202202
- [x] PubSub Commands
203203
- [x] Utils Commands
204204
- [x] Adding BoltDB engine
205+
- [x] Adding LevelDB engine
206+
- [x] Adding Null engine
207+
- [x] Adding SQLite engine
208+
- [x] Adding TiKV engine
205209
- [ ] Adding RAM engine
206210
- [ ] Writing MyOwn DB Engine
207-
- [ ] Writing Test Cases
208-
- [ ] Document/JSON Commands
209-
- [ ] GIS Commands
210211

211212
License
212213
=======

commands_list.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,14 @@ func lrangeCommand(c Context) {
137137

138138
lastKey = hex.EncodeToString([]byte(lastKey))
139139

140-
c.WriteArray(2)
140+
if limit > 0 {
141+
c.WriteArray(2)
141142

142-
if lastKey != "" {
143-
c.WriteBulkString(lastKey)
144-
} else {
145-
c.WriteNull()
143+
if lastKey != "" {
144+
c.WriteBulkString(lastKey)
145+
} else {
146+
c.WriteNull()
147+
}
146148
}
147149

148150
c.WriteArray(len(data) / 2)

kvstore/tikv/leveldb.go

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

0 commit comments

Comments
 (0)