Skip to content

Commit cdea8a6

Browse files
committed
move key expiry management from item to redisdb
1 parent 6f2c645 commit cdea8a6

File tree

9 files changed

+134
-146
lines changed

9 files changed

+134
-146
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ go get -u github.com/redis-go/redis
6060
- [ ] Benchmarks
6161
- [ ] master slaves
6262
- [ ] cluster
63-
- [ ] ...x
63+
- [ ] ...

del.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ func DelCommand(c *Client, cmd redcon.Command) {
1212
keys = append(keys, &k)
1313
}
1414
dels := db.Delete(keys...)
15-
c.Conn().WriteInt64(dels)
15+
c.Conn().WriteInt(dels)
1616
}

get.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func GetCommand(c *Client, cmd redcon.Command) {
1313

1414
}
1515

16-
i := db.GetOrExpired(&key, true)
16+
i := db.GetOrExpire(&key, true)
1717
if i == nil {
1818
c.Conn().WriteNull()
1919
return

keyexpire.go

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (e *Expirer) do(randomKeys, againPercentage int) {
5858

5959
dbs := make(map[*RedisDb]struct{})
6060
for _, db := range e.Redis().RedisDbs() {
61-
if db.IsEmptyExpire() {
61+
if !db.HasExpiringKeys() {
6262
continue
6363
}
6464
dbs[db] = struct{}{}
@@ -78,48 +78,29 @@ func (e *Expirer) do(randomKeys, againPercentage int) {
7878
}()
7979

8080
// get random key
81-
k, i := func() (*string, Item) {
82-
for k, i := range db.ExpiringKeys() {
83-
return &k, i
81+
k := func() *string {
82+
for k := range db.ExpiringKeys() {
83+
return &k
8484
}
85-
return nil, nil
85+
return nil
8686
}()
8787

8888
if k == nil {
8989
continue
9090
}
9191

9292
// del if expired
93-
if ItemExpired(i) {
94-
if db.Delete(k) != 0 {
95-
deletedKeys++
96-
}
93+
if db.DeleteExpired(k) != 0 {
94+
deletedKeys++
9795
}
9896
}
9997

100-
// Start again in new goroutine so many keys are fast deleted
98+
// Start again in new goroutine so keys are deleted fast
10199
if againPercentage > 0 && deletedKeys/randomKeys*100 > againPercentage {
102100
go e.do(randomKeys, againPercentage)
103101
}
104102
}
105103

106-
// Deletes random expiring keys from db.
107-
// num is the number of keys to delete.
108-
// Returns number of left deletions so >0 if db is empty.
109-
func delRandFromDb(num int, db *RedisDb) int {
110-
var c int
111-
for k, i := range db.ExpiringKeys() {
112-
if c == num {
113-
return 0
114-
}
115-
if ItemExpired(i) {
116-
db.Delete(&k)
117-
}
118-
c++
119-
}
120-
return num - c
121-
}
122-
123104
// Redis gets the redis instance.
124105
func (e *Expirer) Redis() *Redis {
125106
return e.redis

redisdb.go

Lines changed: 112 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type RedisDb struct {
1919
// All keys in this db.
2020
keys Keys
2121

22-
// Keys with a timeout set.
23-
expiringKeys Keys
22+
// Keys with expire timestamp.
23+
expiringKeys ExpiringKeys
2424

2525
// TODO long long avg_ttl; /* Average TTL, just for stats */
2626

@@ -36,6 +36,9 @@ type DatabaseId uint
3636
// Key-Item map
3737
type Keys map[string]Item
3838

39+
// Keys with expire timestamp.
40+
type ExpiringKeys map[string]time.Time
41+
3942
// The item interface. An item is the value of a key.
4043
type Item interface {
4144
// The pointer to the value.
@@ -45,14 +48,9 @@ type Item interface {
4548
// This need to be constant for the type because it is
4649
// used when de-/serializing item from/to disk.
4750
ValueType() uint64
48-
// The type of the Item as string.
51+
// The type of the Item as readable string.
4952
ValueTypeFancy() string
5053

51-
// GetCommand timestamp when the item expires.
52-
Expiry() time.Time
53-
// Expiry is set.
54-
Expires() bool
55-
5654
// OnDelete is triggered before the key of the item is deleted.
5755
// db is the affected database.
5856
OnDelete(key *string, db *RedisDb)
@@ -64,7 +62,7 @@ func NewRedisDb(id DatabaseId, r *Redis) *RedisDb {
6462
id: id,
6563
redis: r,
6664
keys: make(Keys, keysMapSize),
67-
expiringKeys: make(Keys, keysMapSize),
65+
expiringKeys: make(ExpiringKeys, keysMapSize),
6866
}
6967
}
7068

@@ -98,6 +96,7 @@ func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
9896
return r.redisDbs[dbId]
9997
}
10098

99+
// RedisDbs gets all redis databases.
101100
func (r *Redis) RedisDbs() RedisDbs {
102101
r.Mu().RLock()
103102
defer r.Mu().RUnlock()
@@ -119,41 +118,13 @@ func (db *RedisDb) Id() DatabaseId {
119118
return db.id
120119
}
121120

122-
// IsEmpty checks db is empty.
123-
func (db *RedisDb) IsEmpty() bool {
124-
db.Mu().RLock()
125-
defer db.Mu().RUnlock()
126-
return len(db.keys) == 0
127-
}
128-
129-
// IsEmptyExpire checks db has any expiring keys.
130-
func (db *RedisDb) IsEmptyExpire() bool {
131-
db.Mu().RLock()
132-
defer db.Mu().RUnlock()
133-
return len(db.expiringKeys) == 0
134-
}
135-
136-
// Keys gets all keys in this db.
137-
func (db *RedisDb) Keys() Keys {
138-
db.Mu().RLock()
139-
defer db.Mu().RUnlock()
140-
return db.keys
141-
}
142-
143-
// ExpiringKeys gets keys with an expiry set.
144-
func (db *RedisDb) ExpiringKeys() Keys {
145-
db.Mu().RLock()
146-
defer db.Mu().RUnlock()
147-
return db.expiringKeys
148-
}
149-
150-
// Sets a key with an item.
151-
func (db *RedisDb) Set(key *string, i Item) {
121+
// Sets a key with an item which can have an expiration time.
122+
func (db *RedisDb) Set(key *string, i Item, expires bool, expiry time.Time) {
152123
db.Mu().Lock()
153124
defer db.Mu().Unlock()
154125
db.keys[*key] = i
155-
if i.Expires() {
156-
db.expiringKeys[*key] = i
126+
if expires {
127+
db.expiringKeys[*key] = expiry
157128
}
158129
}
159130

@@ -169,30 +140,79 @@ func (db *RedisDb) get(key *string) Item {
169140
return i
170141
}
171142

172-
// Deletes a key, returns true if key existed.
173-
func (db *RedisDb) Delete(keys ...*string) int64 {
174-
// TODO if it makes a difference, check keys exists with RLock and then if exists RWLock
143+
// Deletes a key, returns number of deleted keys.
144+
func (db *RedisDb) Delete(keys ...*string) int {
175145
db.Mu().Lock()
176146
defer db.Mu().Unlock()
177-
var c int64
147+
return db.delete(keys...)
148+
}
149+
150+
// If checkExists is false, then return bool is reprehensible.
151+
func (db *RedisDb) delete(keys ...*string) int {
152+
do := func(k *string) bool {
153+
if k == nil {
154+
return false
155+
}
156+
i := db.get(k)
157+
if i == nil {
158+
return false
159+
}
160+
i.OnDelete(k, db)
161+
delete(db.keys, *k)
162+
delete(db.expiringKeys, *k)
163+
return true
164+
}
165+
166+
var c int
178167
for _, k := range keys {
179-
if k != nil && db.delete(k) {
168+
if do(k) {
180169
c++
181170
}
182171
}
172+
183173
return c
184174
}
185175

186-
// If checkExists is false, then return bool is reprehensible.
187-
func (db *RedisDb) delete(key *string) bool {
188-
i := db.get(key)
189-
if i == nil {
190-
return false
176+
func (db *RedisDb) DeleteExpired(keys ...*string) int {
177+
var c int
178+
for _, k := range keys {
179+
if k != nil && db.Expired(k) && db.Delete(k) > 0 {
180+
c++
181+
}
182+
}
183+
return c
184+
}
185+
186+
// GetOrExpire gets the item or nil if expired or not exists. If 'deleteIfExpired' is true the key will be deleted.
187+
func (db *RedisDb) GetOrExpire(key *string, deleteIfExpired bool) Item {
188+
// TODO mutex optimize this func so that a RLock is mainly first opened
189+
db.Mu().Lock()
190+
defer db.Mu().Unlock()
191+
i, ok := db.keys[*key]
192+
if !ok {
193+
return nil
194+
}
195+
if db.expired(key) {
196+
if deleteIfExpired {
197+
db.delete(key)
198+
}
199+
return nil
191200
}
192-
i.OnDelete(key, db)
193-
delete(db.keys, *key)
194-
delete(db.expiringKeys, *key)
195-
return true
201+
return i
202+
}
203+
204+
// IsEmpty checks if db is empty.
205+
func (db *RedisDb) IsEmpty() bool {
206+
db.Mu().RLock()
207+
defer db.Mu().RUnlock()
208+
return len(db.keys) == 0
209+
}
210+
211+
// HasExpiringKeys checks if db has any expiring keys.
212+
func (db *RedisDb) HasExpiringKeys() bool {
213+
db.Mu().RLock()
214+
defer db.Mu().RUnlock()
215+
return len(db.expiringKeys) != 0
196216
}
197217

198218
// Check if key exists.
@@ -206,39 +226,53 @@ func (db *RedisDb) exists(key *string) bool {
206226
return ok
207227
}
208228

209-
// Check if key can expire.
229+
// Check if key has an expiry set.
210230
func (db *RedisDb) Expires(key *string) bool {
211231
db.Mu().RLock()
212232
defer db.Mu().RUnlock()
233+
return db.expires(key)
234+
}
235+
func (db *RedisDb) expires(key *string) bool {
213236
_, ok := db.expiringKeys[*key]
214237
return ok
215238
}
216239

217-
// GetOrExpire gets the item or nil if expired or not exists.
218-
func (db *RedisDb) GetOrExpired(key *string, deleteIfExpired bool) Item {
219-
// TODO mutex optimize this func so that a RLock is mainly first opened
240+
// Expired only check if a key can and is expired.
241+
func (db *RedisDb) Expired(key *string) bool {
242+
db.Mu().RLock()
243+
defer db.Mu().RUnlock()
244+
return db.expired(key)
245+
}
246+
func (db *RedisDb) expired(key *string) bool {
247+
return db.expires(key) && TimeExpired(db.expiry(key))
248+
}
220249

221-
db.Mu().Lock()
222-
defer db.Mu().Unlock()
223-
i, ok := db.keys[*key]
224-
if !ok {
225-
return nil
226-
}
227-
if ItemExpired(i) {
228-
if deleteIfExpired {
229-
db.delete(key)
230-
}
231-
return nil
232-
}
233-
return i
250+
// Expiry gets the expiry of the key has one.
251+
func (db *RedisDb) Expiry(key *string) time.Time {
252+
db.Mu().RLock()
253+
defer db.Mu().RUnlock()
254+
return db.expiry(key)
234255
}
235256

236-
// Expired check if a timestamp is expired.
237-
func Expired(expireAt time.Time) bool {
238-
return time.Now().After(expireAt)
257+
func (db *RedisDb) expiry(key *string) time.Time {
258+
return db.expiringKeys[*key]
259+
}
260+
261+
// Keys gets all keys in this db.
262+
func (db *RedisDb) Keys() Keys {
263+
db.Mu().RLock()
264+
defer db.Mu().RUnlock()
265+
return db.keys
239266
}
240267

241-
// ItemExpired check if an item can and is expired
242-
func ItemExpired(i Item) bool {
243-
return i.Expires() && Expired(i.Expiry())
268+
// ExpiringKeys gets keys with an expiry set and their timeout.
269+
func (db *RedisDb) ExpiringKeys() ExpiringKeys {
270+
db.Mu().RLock()
271+
defer db.Mu().RUnlock()
272+
return db.expiringKeys
273+
}
274+
275+
// TimeExpired check if a timestamp is older than now.
276+
func TimeExpired(expireAt time.Time) bool {
277+
return time.Now().After(expireAt)
244278
}

set.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,6 @@ func SetCommand(c *Client, cmd redcon.Command) {
120120
return
121121
}
122122

123-
db.Set(key, NewString(&value, yesExpire, expire))
123+
db.Set(key, NewString(&value), yesExpire, expire)
124124
c.Conn().WriteString("OK")
125125
}

t_list.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package redis
22

3-
import (
4-
"time"
5-
)
6-
73
const ListType = uint64(0)
84
const ListTypeFancy = "list"
95

@@ -25,14 +21,6 @@ func (l *List) ValueTypeFancy() string {
2521
return ListTypeFancy
2622
}
2723

28-
func (l *List) Expiry() time.Time {
29-
panic("implement me")
30-
}
31-
32-
func (l *List) Expires() bool {
33-
panic("implement me")
34-
}
35-
3624
func (l *List) OnDelete(key *string, db *RedisDb) {
3725
panic("implement me")
3826
}

0 commit comments

Comments
 (0)