Skip to content

Commit 8c11fe2

Browse files
committed
Add GetWithExpiration
1 parent e7a9def commit 8c11fe2

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

CONTRIBUTORS

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ code was contributed.)
66
Dustin Sallings <[email protected]>
77
Jason Mooberry <[email protected]>
88
Sergey Shepelev <[email protected]>
9+
Alex Edwards <[email protected]>

cache.go

+30
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,36 @@ func (c *cache) Get(k string) (interface{}, bool) {
135135
return item.Object, true
136136
}
137137

138+
// GetWithExpiration returns an item and its expiration time from the cache.
139+
// It returns the item or nil, the expiration time if one is set (if the item
140+
// never expires a zero value for time.Time is returned), and a bool indicating
141+
// whether the key was found.
142+
func (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) {
143+
c.mu.RLock()
144+
// "Inlining" of get and Expired
145+
item, found := c.items[k]
146+
if !found {
147+
c.mu.RUnlock()
148+
return nil, time.Time{}, false
149+
}
150+
151+
if item.Expiration > 0 {
152+
if time.Now().UnixNano() > item.Expiration {
153+
c.mu.RUnlock()
154+
return nil, time.Time{}, false
155+
}
156+
157+
// Return the item and the expiration time
158+
c.mu.RUnlock()
159+
return item.Object, time.Unix(0, item.Expiration), true
160+
}
161+
162+
// If expiration <= 0 (i.e. no expiration time set) then return the item
163+
// and a zeroed time.Time
164+
c.mu.RUnlock()
165+
return item.Object, time.Time{}, true
166+
}
167+
138168
func (c *cache) get(k string) (interface{}, bool) {
139169
item, found := c.items[k]
140170
if !found {

cache_test.go

+94-1
Original file line numberDiff line numberDiff line change
@@ -1459,7 +1459,7 @@ func BenchmarkRWMutexMapGet(b *testing.B) {
14591459

14601460
func BenchmarkRWMutexInterfaceMapGetStruct(b *testing.B) {
14611461
b.StopTimer()
1462-
s := struct{name string}{name: "foo"}
1462+
s := struct{ name string }{name: "foo"}
14631463
m := map[interface{}]string{
14641464
s: "bar",
14651465
}
@@ -1676,3 +1676,96 @@ func BenchmarkDeleteExpiredLoop(b *testing.B) {
16761676
tc.DeleteExpired()
16771677
}
16781678
}
1679+
1680+
func TestGetWithExpiration(t *testing.T) {
1681+
tc := New(DefaultExpiration, 0)
1682+
1683+
a, expiration, found := tc.GetWithExpiration("a")
1684+
if found || a != nil || !expiration.IsZero() {
1685+
t.Error("Getting A found value that shouldn't exist:", a)
1686+
}
1687+
1688+
b, expiration, found := tc.GetWithExpiration("b")
1689+
if found || b != nil || !expiration.IsZero() {
1690+
t.Error("Getting B found value that shouldn't exist:", b)
1691+
}
1692+
1693+
c, expiration, found := tc.GetWithExpiration("c")
1694+
if found || c != nil || !expiration.IsZero() {
1695+
t.Error("Getting C found value that shouldn't exist:", c)
1696+
}
1697+
1698+
tc.Set("a", 1, DefaultExpiration)
1699+
tc.Set("b", "b", DefaultExpiration)
1700+
tc.Set("c", 3.5, DefaultExpiration)
1701+
tc.Set("d", 1, NoExpiration)
1702+
tc.Set("e", 1, 50*time.Millisecond)
1703+
1704+
x, expiration, found := tc.GetWithExpiration("a")
1705+
if !found {
1706+
t.Error("a was not found while getting a2")
1707+
}
1708+
if x == nil {
1709+
t.Error("x for a is nil")
1710+
} else if a2 := x.(int); a2+2 != 3 {
1711+
t.Error("a2 (which should be 1) plus 2 does not equal 3; value:", a2)
1712+
}
1713+
if !expiration.IsZero() {
1714+
t.Error("expiration for a is not a zeroed time")
1715+
}
1716+
1717+
x, expiration, found = tc.GetWithExpiration("b")
1718+
if !found {
1719+
t.Error("b was not found while getting b2")
1720+
}
1721+
if x == nil {
1722+
t.Error("x for b is nil")
1723+
} else if b2 := x.(string); b2+"B" != "bB" {
1724+
t.Error("b2 (which should be b) plus B does not equal bB; value:", b2)
1725+
}
1726+
if !expiration.IsZero() {
1727+
t.Error("expiration for b is not a zeroed time")
1728+
}
1729+
1730+
x, expiration, found = tc.GetWithExpiration("c")
1731+
if !found {
1732+
t.Error("c was not found while getting c2")
1733+
}
1734+
if x == nil {
1735+
t.Error("x for c is nil")
1736+
} else if c2 := x.(float64); c2+1.2 != 4.7 {
1737+
t.Error("c2 (which should be 3.5) plus 1.2 does not equal 4.7; value:", c2)
1738+
}
1739+
if !expiration.IsZero() {
1740+
t.Error("expiration for c is not a zeroed time")
1741+
}
1742+
1743+
x, expiration, found = tc.GetWithExpiration("d")
1744+
if !found {
1745+
t.Error("d was not found while getting d2")
1746+
}
1747+
if x == nil {
1748+
t.Error("x for d is nil")
1749+
} else if d2 := x.(int); d2+2 != 3 {
1750+
t.Error("d (which should be 1) plus 2 does not equal 3; value:", d2)
1751+
}
1752+
if !expiration.IsZero() {
1753+
t.Error("expiration for d is not a zeroed time")
1754+
}
1755+
1756+
x, expiration, found = tc.GetWithExpiration("e")
1757+
if !found {
1758+
t.Error("e was not found while getting e2")
1759+
}
1760+
if x == nil {
1761+
t.Error("x for e is nil")
1762+
} else if e2 := x.(int); e2+2 != 3 {
1763+
t.Error("e (which should be 1) plus 2 does not equal 3; value:", e2)
1764+
}
1765+
if expiration.UnixNano() != tc.items["e"].Expiration {
1766+
t.Error("expiration for e is not the correct time")
1767+
}
1768+
if expiration.UnixNano() < time.Now().UnixNano() {
1769+
t.Error("expiration for e is in the past")
1770+
}
1771+
}

0 commit comments

Comments
 (0)