diff --git a/buntdb.go b/buntdb.go index 244748e..45fa741 100644 --- a/buntdb.go +++ b/buntdb.go @@ -913,16 +913,16 @@ func (db *DB) readLoad(rd io.Reader, modTime time.Time) (n int64, err error) { } else { exat = time.Unix(ex, 0) } - if exat.After(now) { - db.insertIntoDatabase(&dbItem{ - key: parts[1], - val: parts[2], - opts: &dbItemOpts{ - ex: true, - exat: exat, - }, - }) - } + // just insert there and update in case of the TTL changed + // even if the key expired already, we still have enough infomation to get with ignoreExpired before any shrink. + db.insertIntoDatabase(&dbItem{ + key: parts[1], + val: parts[2], + opts: &dbItemOpts{ + ex: true, + exat: exat, + }, + }) } else { db.insertIntoDatabase(&dbItem{key: parts[1], val: parts[2]}) } diff --git a/buntdb_test.go b/buntdb_test.go index 4b37db2..a9f2e44 100644 --- a/buntdb_test.go +++ b/buntdb_test.go @@ -2499,10 +2499,10 @@ func Benchmark_Descend_10000(t *testing.B) { } /* -func Benchmark_Spatial_2D(t *testing.B) { - N := 100000 - db, _, _ := benchOpenFillData(t, N, true, true, false, true, 100) - defer benchClose(t, false, db) + func Benchmark_Spatial_2D(t *testing.B) { + N := 100000 + db, _, _ := benchOpenFillData(t, N, true, true, false, true, 100) + defer benchClose(t, false, db) } */ @@ -2918,3 +2918,52 @@ func TestWrappedError(t *testing.T) { }() panicErr(errors.New("my fake error")) } + +func TestKeyExpiredAfterCloseAndBeforeReopen(t *testing.T) { + // This tests an bug identified in Issue #112. + // A SET without TTL followed by another SET with TTL on the same key, + // and accidentally the key expired after the db closed therefor the key wasn't + // DEL by backgroundManager. On the next time we load the db, Oops, + // db.readLoad just ignore the second OP and remains the first one. + + f, err := os.CreateTemp("", "") + if err != nil { + panicErr(err) + } + defer os.Remove(f.Name()) + defer f.Close() + + db, err := Open(f.Name()) + if err != nil { + panicErr(err) + } + err = db.Update(func(tx *Tx) error { + _, _, err := tx.Set("key:112", "value:112-old", nil) + return err + }) + if err != nil { + panicErr(err) + } + err = db.Update(func(tx *Tx) error { + _, _, err := tx.Set("key:112", "value:112", &SetOptions{Expires: true, TTL: time.Second}) + return err + }) + if err != nil { + panicErr(err) + } + db.Close() + + // make sure the key expired + time.Sleep(time.Second) + + db, err = Open(f.Name()) + if err != nil { + panicErr(err) + } + err = db.View(func(tx *Tx) error { + _, err := tx.Get("key:112") + return err + }) + assert.Assert(err == ErrNotFound) + +}