Skip to content

Commit 72bd026

Browse files
Add remove method and tests in LRUCache for cmab service
1 parent fd0930c commit 72bd026

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

optimizely/odp/lru_cache.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ def peek(self, key: K) -> Optional[V]:
9191
element = self.map.get(key)
9292
return element.value if element is not None else None
9393

94+
def remove(self, key: K) -> None:
95+
"""Remove the element associated with the provided key from the cache."""
96+
if self.capacity <= 0:
97+
return
98+
99+
with self.lock:
100+
self.map.pop(key, None)
101+
94102

95103
@dataclass
96104
class CacheElement(Generic[V]):

tests/test_lru_cache.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,87 @@ def test_reset(self):
130130
cache.save('cow', 'crate')
131131
self.assertEqual(cache.lookup('cow'), 'crate')
132132

133+
def test_remove_non_existent_key(self):
134+
cache = LRUCache(3, 1000)
135+
cache.save("1", 100)
136+
cache.save("2", 200)
137+
138+
cache.remove("3") # Doesn't exist
139+
140+
self.assertEqual(cache.lookup("1"), 100)
141+
self.assertEqual(cache.lookup("2"), 200)
142+
self.assertEqual(len(cache.map), 2)
143+
144+
def test_remove_existing_key(self):
145+
cache = LRUCache(3, 1000)
146+
147+
cache.save("1", 100)
148+
cache.save("2", 200)
149+
cache.save("3", 300)
150+
151+
self.assertEqual(cache.lookup("1"), 100)
152+
self.assertEqual(cache.lookup("2"), 200)
153+
self.assertEqual(cache.lookup("3"), 300)
154+
self.assertEqual(len(cache.map), 3)
155+
156+
cache.remove("2")
157+
158+
self.assertEqual(cache.lookup("1"), 100)
159+
self.assertIsNone(cache.lookup("2"))
160+
self.assertEqual(cache.lookup("3"), 300)
161+
self.assertEqual(len(cache.map), 2)
162+
163+
def test_remove_from_zero_sized_cache(self):
164+
cache = LRUCache(0, 1000)
165+
cache.save("1", 100)
166+
cache.remove("1")
167+
168+
self.assertIsNone(cache.lookup("1"))
169+
self.assertEqual(len(cache.map), 0)
170+
171+
def test_remove_and_add_back(self):
172+
cache = LRUCache(3, 1000)
173+
cache.save("1", 100)
174+
cache.save("2", 200)
175+
cache.save("3", 300)
176+
177+
cache.remove("2")
178+
cache.save("2", 201)
179+
180+
self.assertEqual(cache.lookup("1"), 100)
181+
self.assertEqual(cache.lookup("2"), 201)
182+
self.assertEqual(cache.lookup("3"), 300)
183+
self.assertEqual(len(cache.map), 3)
184+
185+
def test_thread_safety(self):
186+
import threading
187+
188+
max_size = 100
189+
cache = LRUCache(max_size, 1000)
190+
191+
for i in range(1, max_size + 1):
192+
cache.save(str(i), i * 100)
193+
194+
def remove_key(k):
195+
cache.remove(str(k))
196+
197+
threads = []
198+
for i in range(1, (max_size // 2) + 1):
199+
thread = threading.Thread(target=remove_key, args=(i,))
200+
threads.append(thread)
201+
thread.start()
202+
203+
for thread in threads:
204+
thread.join()
205+
206+
for i in range(1, max_size + 1):
207+
if i <= max_size // 2:
208+
self.assertIsNone(cache.lookup(str(i)))
209+
else:
210+
self.assertEqual(cache.lookup(str(i)), i * 100)
211+
212+
self.assertEqual(len(cache.map), max_size // 2)
213+
133214
# type checker test
134215
# confirm that LRUCache matches OptimizelySegmentsCache protocol
135216
_: OptimizelySegmentsCache = LRUCache(0, 0)

0 commit comments

Comments
 (0)