fix nasty bug

This needs to be the opposite check of what we do in Get(). Introduced
in a fixup commit.
This commit is contained in:
Mike Danese 2019-11-15 17:07:40 -08:00
parent d16dde36a3
commit cc76b1a8a7
2 changed files with 103 additions and 2 deletions

View File

@ -70,7 +70,7 @@ func (c *Expiring) Get(key interface{}) (val interface{}, ok bool) {
c.mu.RLock()
defer c.mu.RUnlock()
e, ok := c.cache[key]
if !ok || c.clock.Now().After(e.expiry) {
if !ok || !c.clock.Now().Before(e.expiry) {
return nil, false
}
return e.val, true
@ -148,7 +148,7 @@ func (c *Expiring) gc(now time.Time) {
// from looking at the (*expiringHeap).Pop() implmentation below.
// heap.Pop() swaps the first entry with the last entry of the heap, then
// calls (*expiringHeap).Pop() which returns the last element.
if len(c.heap) == 0 || now.After(c.heap[0].expiry) {
if len(c.heap) == 0 || now.Before(c.heap[0].expiry) {
return
}
cleanup := heap.Pop(&c.heap).(*expiringHeapEntry)

View File

@ -103,6 +103,107 @@ func TestExpiration(t *testing.T) {
}
}
func TestGarbageCollection(t *testing.T) {
fc := &utilclock.FakeClock{}
type entry struct {
key, val string
ttl time.Duration
}
tests := []struct {
name string
now time.Time
set []entry
want map[string]string
}{
{
name: "two entries just set",
now: fc.Now().Add(0 * time.Second),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"a": "aa",
"b": "bb",
},
},
{
name: "first entry expired now",
now: fc.Now().Add(1 * time.Second),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"b": "bb",
},
},
{
name: "first entry expired half a second ago",
now: fc.Now().Add(1500 * time.Millisecond),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"b": "bb",
},
},
{
name: "three entries weird order",
now: fc.Now().Add(1 * time.Second),
set: []entry{
{"c", "cc", 3 * time.Second},
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"b": "bb",
"c": "cc",
},
},
{
name: "expire multiple entries in one cycle",
now: fc.Now().Add(2500 * time.Millisecond),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
{"c", "cc", 3 * time.Second},
},
want: map[string]string{
"c": "cc",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewExpiringWithClock(fc)
for _, e := range test.set {
c.Set(e.key, e.val, e.ttl)
}
c.gc(test.now)
for k, want := range test.want {
got, ok := c.Get(k)
if !ok {
t.Errorf("expected cache to have entry for key=%q but found none", k)
continue
}
if got != want {
t.Errorf("unexpected value for key=%q: got=%q, want=%q", k, got, want)
}
}
if got, want := c.Len(), len(test.want); got != want {
t.Errorf("unexpected cache size: got=%d, want=%d", got, want)
}
})
}
}
func BenchmarkExpiringCacheContention(b *testing.B) {
b.Run("evict_probablility=100%", func(b *testing.B) {
benchmarkExpiringCacheContention(b, 1)