mirror of https://github.com/tidwall/buntdb.git
track ttl between database reopens, fixes #11
This commit is contained in:
parent
93cc8f3fcb
commit
7fb2c48afb
27
buntdb.go
27
buntdb.go
|
@ -160,6 +160,7 @@ func (db *DB) Close() error {
|
|||
}
|
||||
db.closed = true
|
||||
if db.persist {
|
||||
db.file.Sync() // do a sync but ignore the error
|
||||
if err := db.file.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -657,6 +658,11 @@ var errValidEOF = errors.New("valid eof")
|
|||
// http://redis.io/topics/protocol. The only supported RESP commands are DEL and
|
||||
// SET.
|
||||
func (db *DB) load() error {
|
||||
fi, err := db.file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modTime := fi.ModTime()
|
||||
data := make([]byte, 4096)
|
||||
parts := make([]string, 0, 8)
|
||||
r := bufio.NewReader(db.file)
|
||||
|
@ -764,15 +770,18 @@ func (db *DB) load() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dur := time.Duration(ex) * time.Second
|
||||
db.insertIntoDatabase(&dbItem{
|
||||
key: parts[1],
|
||||
val: parts[2],
|
||||
opts: &dbItemOpts{
|
||||
ex: true,
|
||||
exat: time.Now().Add(dur),
|
||||
},
|
||||
})
|
||||
now := time.Now()
|
||||
dur := (time.Duration(ex) * time.Second) - now.Sub(modTime)
|
||||
if dur > 0 {
|
||||
db.insertIntoDatabase(&dbItem{
|
||||
key: parts[1],
|
||||
val: parts[2],
|
||||
opts: &dbItemOpts{
|
||||
ex: true,
|
||||
exat: now.Add(dur),
|
||||
},
|
||||
})
|
||||
}
|
||||
} else {
|
||||
db.insertIntoDatabase(&dbItem{key: parts[1], val: parts[2]})
|
||||
}
|
||||
|
|
|
@ -20,13 +20,17 @@ func testOpen(t testing.TB) *DB {
|
|||
}
|
||||
return testReOpen(t, nil)
|
||||
}
|
||||
|
||||
func testReOpen(t testing.TB, db *DB) *DB {
|
||||
return testReOpenDelay(t, db, 0)
|
||||
}
|
||||
|
||||
func testReOpenDelay(t testing.TB, db *DB, dur time.Duration) *DB {
|
||||
if db != nil {
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
time.Sleep(dur)
|
||||
db, err := Open("data.db")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1353,6 +1357,50 @@ func TestRectStrings(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestTTLReOpen test setting a TTL and then immediatelly closing the database and
|
||||
// then waiting the TTL before reopening. The key should not be accessible.
|
||||
func TestTTLReOpen(t *testing.T) {
|
||||
ttl := time.Second * 3
|
||||
db := testOpen(t)
|
||||
defer testClose(db)
|
||||
err := db.Update(func(tx *Tx) error {
|
||||
if _, _, err := tx.Set("key1", "val1", &SetOptions{Expires: true, TTL: ttl}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db = testReOpenDelay(t, db, ttl/4)
|
||||
err = db.View(func(tx *Tx) error {
|
||||
val, err := tx.Get("key1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val != "val1" {
|
||||
t.Fatalf("expecting '%v', got '%v'", "val1", val)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db = testReOpenDelay(t, db, ttl-ttl/4)
|
||||
defer testClose(db)
|
||||
err = db.View(func(tx *Tx) error {
|
||||
val, err := tx.Get("key1")
|
||||
if err == nil || err != ErrNotFound || val != "" {
|
||||
t.Fatal("expecting not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTL(t *testing.T) {
|
||||
db := testOpen(t)
|
||||
defer testClose(db)
|
||||
|
|
Loading…
Reference in New Issue