Skip to content

Commit b919347

Browse files
Add New GetItemsAndFlush method
1 parent 46f4078 commit b919347

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

cache.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,35 @@ func (c *cache) Items() map[string]Item {
10521052
return m
10531053
}
10541054

1055+
// Copies all unexpired items from the cache into a new map,
1056+
// then deletes all items from the cache and returns the original map. This
1057+
// approach ensures that no items are lost due to race conditions that could
1058+
// occur if the operations of copying and deleting were performed separately.
1059+
// For example, a race condition might occur if an item is added to the cache
1060+
// between the calls to Items() and Flush(), resulting in data loss. By
1061+
// combining these operations within a single lock, we maintain data integrity
1062+
// during the cache cleanup process.
1063+
func (c *cache) GetItemsAndFlush() map[string]Item {
1064+
c.mu.Lock()
1065+
defer c.mu.Unlock()
1066+
1067+
m := make(map[string]Item, len(c.items))
1068+
now := time.Now().UnixNano()
1069+
for k, v := range c.items {
1070+
// "Inlining" of Expired
1071+
if v.Expiration > 0 {
1072+
if now > v.Expiration {
1073+
continue
1074+
}
1075+
}
1076+
m[k] = v
1077+
}
1078+
1079+
c.items = map[string]Item{}
1080+
1081+
return m
1082+
}
1083+
10551084
// Returns the number of items in the cache. This may include items that have
10561085
// expired, but have not yet been cleaned up.
10571086
func (c *cache) ItemCount() int {

cache_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,3 +1769,68 @@ func TestGetWithExpiration(t *testing.T) {
17691769
t.Error("expiration for e is in the past")
17701770
}
17711771
}
1772+
1773+
func TestGetItemsAndFlush(t *testing.T) {
1774+
tc := New(DefaultExpiration, 0)
1775+
tc.Set("foo", "bar", DefaultExpiration)
1776+
tc.Set("baz", "yes", DefaultExpiration)
1777+
m := tc.GetItemsAndFlush()
1778+
1779+
// Assert get items was executed
1780+
x, found := m["foo"]
1781+
if !found {
1782+
t.Error("foo was not found in the map, but it should be returned")
1783+
}
1784+
1785+
if x.Expiration != int64(DefaultExpiration) {
1786+
t.Errorf("foo was found, but its expiration is %v, which is different than the setted one", x.Expiration)
1787+
}
1788+
1789+
v, ok := x.Object.(string)
1790+
if !ok {
1791+
t.Error("foo was found, but its value can't be parsed to string")
1792+
}
1793+
1794+
if v != "bar" {
1795+
t.Errorf("foo was found, but its actual value is %s, different than the original one", v)
1796+
}
1797+
1798+
x, found = m["baz"]
1799+
if !found {
1800+
t.Error("baz was not found in the map, but it should be returned")
1801+
}
1802+
1803+
if x.Expiration != int64(DefaultExpiration) {
1804+
t.Errorf("baz was found, but its expiration is %v, which is different than the setted one", x.Expiration)
1805+
}
1806+
1807+
v, ok = x.Object.(string)
1808+
if !ok {
1809+
t.Error("baz was found, but its value can't be parsed to string")
1810+
}
1811+
1812+
if v != "yes" {
1813+
t.Errorf("baz was found, but its actual value is %s, different than the original one", v)
1814+
}
1815+
1816+
x, found = m["baz"]
1817+
if !found {
1818+
t.Error("baz was not found in the map, but it should be returned")
1819+
}
1820+
1821+
// Assert flush was executed
1822+
y, found := tc.Get("foo")
1823+
if found {
1824+
t.Error("foo was found, but it should have been deleted")
1825+
}
1826+
if y != nil {
1827+
t.Error("x is not nil:", x)
1828+
}
1829+
y, found = tc.Get("baz")
1830+
if found {
1831+
t.Error("baz was found, but it should have been deleted")
1832+
}
1833+
if y != nil {
1834+
t.Error("x is not nil:", x)
1835+
}
1836+
}

0 commit comments

Comments
 (0)