Skip to content

Commit b210c55

Browse files
[FSSDK-11158] update: add remove method in LRU Cache for CMAB service (#366)
* update: Extend LRUCache with remove method and corresponding tests * update: Clean up whitespace in LRUCache implementation and tests * update: Extend copyright notice to include 2025
1 parent 95f9e35 commit b210c55

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

lib/optimizely/odp/lru_cache.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
#
4-
# Copyright 2022, Optimizely and contributors
4+
# Copyright 2022-2025, Optimizely and contributors
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -91,6 +91,19 @@ def peek(key)
9191

9292
@cache_mutex.synchronize { @map[key]&.value }
9393
end
94+
95+
# Remove the element associated with the provided key from the cache
96+
#
97+
# @param key - The key to remove
98+
99+
def remove(key)
100+
return if @capacity <= 0
101+
102+
@cache_mutex.synchronize do
103+
@map.delete(key)
104+
end
105+
nil
106+
end
94107
end
95108

96109
class CacheElement

spec/odp/lru_cache_spec.rb

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
#
4-
# Copyright 2022, Optimizely and contributors
4+
# Copyright 2022-2025, Optimizely and contributors
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -149,4 +149,84 @@
149149
cache.save('cow', 'crate')
150150
expect(cache.lookup('cow')).to eq 'crate'
151151
end
152+
153+
it 'should remove existing key' do
154+
cache = Optimizely::LRUCache.new(3, 1000)
155+
156+
cache.save('1', 100)
157+
cache.save('2', 200)
158+
cache.save('3', 300)
159+
160+
expect(cache.lookup('1')).to eq 100
161+
expect(cache.lookup('2')).to eq 200
162+
expect(cache.lookup('3')).to eq 300
163+
164+
cache.remove('2')
165+
166+
expect(cache.lookup('1')).to eq 100
167+
expect(cache.lookup('2')).to be_nil
168+
expect(cache.lookup('3')).to eq 300
169+
end
170+
171+
it 'should handle removing non-existent key' do
172+
cache = Optimizely::LRUCache.new(3, 1000)
173+
cache.save('1', 100)
174+
cache.save('2', 200)
175+
176+
cache.remove('3') # Doesn't exist
177+
178+
expect(cache.lookup('1')).to eq 100
179+
expect(cache.lookup('2')).to eq 200
180+
end
181+
182+
it 'should handle removing from zero sized cache' do
183+
cache = Optimizely::LRUCache.new(0, 1000)
184+
cache.save('1', 100)
185+
cache.remove('1')
186+
187+
expect(cache.lookup('1')).to be_nil
188+
end
189+
190+
it 'should handle removing and adding back a key' do
191+
cache = Optimizely::LRUCache.new(3, 1000)
192+
cache.save('1', 100)
193+
cache.save('2', 200)
194+
cache.save('3', 300)
195+
196+
cache.remove('2')
197+
cache.save('2', 201)
198+
199+
expect(cache.lookup('1')).to eq 100
200+
expect(cache.lookup('2')).to eq 201
201+
expect(cache.lookup('3')).to eq 300
202+
end
203+
204+
it 'should handle thread safety' do
205+
max_size = 100
206+
cache = Optimizely::LRUCache.new(max_size, 1000)
207+
208+
(1..max_size).each do |i|
209+
cache.save(i.to_s, i * 100)
210+
end
211+
212+
threads = []
213+
(1..(max_size / 2)).each do |i|
214+
thread = Thread.new do
215+
cache.remove(i.to_s)
216+
end
217+
threads << thread
218+
end
219+
220+
threads.each(&:join)
221+
222+
(1..max_size).each do |i|
223+
if i <= max_size / 2
224+
expect(cache.lookup(i.to_s)).to be_nil
225+
else
226+
expect(cache.lookup(i.to_s)).to eq(i * 100)
227+
end
228+
end
229+
230+
expect(cache.instance_variable_get('@map').size).to eq(max_size / 2)
231+
end
152232
end

0 commit comments

Comments
 (0)