Skip to content

Commit e5ca4e1

Browse files
committed
add support for querying entry expiration.
1 parent ee73605 commit e5ca4e1

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

datastore.go

+22-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ func NewDatastore(path string, options *Options) (*Datastore, error) {
7373
}
7474

7575
return &Datastore{
76-
DB: kv,
77-
76+
DB: kv,
7877
gcDiscardRatio: gcDiscardRatio,
7978
}, nil
8079
}
@@ -125,6 +124,13 @@ func (d *Datastore) SetTTL(key ds.Key, ttl time.Duration) error {
125124
return txn.Commit()
126125
}
127126

127+
func (d *Datastore) GetExpiration(key ds.Key) (time.Time, error) {
128+
txn := d.newImplicitTransaction(false).(*txn)
129+
defer txn.Discard()
130+
131+
return txn.GetExpiration(key)
132+
}
133+
128134
func (d *Datastore) Get(key ds.Key) (value []byte, err error) {
129135
txn := d.newImplicitTransaction(true)
130136
defer txn.Discard()
@@ -192,6 +198,16 @@ func (t *txn) PutWithTTL(key ds.Key, value []byte, ttl time.Duration) error {
192198
return t.txn.SetWithTTL(key.Bytes(), value, ttl)
193199
}
194200

201+
func (t *txn) GetExpiration(key ds.Key) (time.Time, error) {
202+
item, err := t.txn.Get(key.Bytes())
203+
if err == badger.ErrKeyNotFound {
204+
return time.Time{}, ds.ErrNotFound
205+
} else if err != nil {
206+
return time.Time{}, err
207+
}
208+
return time.Unix(int64(item.ExpiresAt()), 0), nil
209+
}
210+
195211
func (t *txn) SetTTL(key ds.Key, ttl time.Duration) error {
196212
data, err := t.Get(key)
197213
if err != nil {
@@ -286,6 +302,10 @@ func (t *txn) Query(q dsq.Query) (dsq.Results, error) {
286302
result = dsq.Result{Entry: e}
287303
}
288304

305+
if q.ReturnExpirations {
306+
result.Expiration = time.Unix(int64(item.ExpiresAt()), 0)
307+
}
308+
289309
select {
290310
case qrb.Output <- result:
291311
case <-worker.Closing(): // client told us to close early

ds_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,76 @@ func TestTTL(t *testing.T) {
672672

673673
d.Close()
674674
}
675+
676+
func TestExpirations(t *testing.T) {
677+
var err error
678+
679+
d, done := newDS(t)
680+
defer done()
681+
682+
txn := d.NewTransaction(false)
683+
ttltxn := txn.(ds.TTLDatastore)
684+
defer txn.Discard()
685+
686+
key := ds.NewKey("/abc/def")
687+
val := make([]byte, 32)
688+
if n, err := rand.Read(val); n != 32 || err != nil {
689+
t.Fatal("source of randomness failed")
690+
}
691+
692+
ttl := time.Hour
693+
now := time.Now()
694+
tgt := now.Add(ttl)
695+
696+
if err = ttltxn.PutWithTTL(key, val, ttl); err != nil {
697+
t.Fatalf("adding with ttl failed: %v", err)
698+
}
699+
700+
if err = txn.Commit(); err != nil {
701+
t.Fatalf("commiting transaction failed: %v", err)
702+
}
703+
704+
// Second transaction to retrieve expirations.
705+
txn = d.NewTransaction(true)
706+
ttltxn = txn.(ds.TTLDatastore)
707+
defer txn.Discard()
708+
709+
// GetExpiration returns expected value.
710+
var dsExp time.Time
711+
if dsExp, err = ttltxn.GetExpiration(key); err != nil {
712+
t.Fatalf("getting expiration failed: %v", err)
713+
} else if tgt.Sub(dsExp) >= 5*time.Second {
714+
t.Fatal("expiration returned by datastore not within the expected range (tolerance: 5 seconds)")
715+
} else if tgt.Sub(dsExp) < 0 {
716+
t.Fatal("expiration returned by datastore was earlier than expected")
717+
}
718+
719+
// Iterator returns expected value.
720+
q := dsq.Query{
721+
ReturnExpirations: true,
722+
KeysOnly: true,
723+
}
724+
var ress dsq.Results
725+
if ress, err = txn.Query(q); err != nil {
726+
t.Fatalf("querying datastore failed: %v", err)
727+
}
728+
729+
defer ress.Close()
730+
if res, ok := ress.NextSync(); !ok {
731+
t.Fatal("expected 1 result in iterator")
732+
} else if res.Expiration != dsExp {
733+
t.Fatalf("expiration returned from iterator differs from GetExpiration, expected: %v, actual: %v", dsExp, res.Expiration)
734+
}
735+
736+
if _, ok := ress.NextSync(); ok {
737+
t.Fatal("expected no more results in iterator")
738+
}
739+
740+
// Datastore->GetExpiration()
741+
if exp, err := d.GetExpiration(key); err != nil {
742+
t.Fatalf("querying datastore failed: %v", err)
743+
} else if exp != dsExp {
744+
t.Fatalf("expiration returned from DB differs from that returned by txn, expected: %v, actual: %v", dsExp, exp)
745+
}
746+
747+
}

0 commit comments

Comments
 (0)