Skip to content

Commit 840c7ab

Browse files
committed
fix cache bubble up issue
1 parent cb31be2 commit 840c7ab

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

splitio/storage/adapters/cache_trait.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,41 +109,49 @@ def _bubble_up(self, node):
109109
if node is None:
110110
return None
111111

112+
# First item, just set lru & mru
113+
if not self._data:
114+
self._lru = node
115+
self._mru = node
116+
return node
117+
118+
# MRU, just return it
119+
if node is self._mru:
120+
return node
121+
122+
# LRU, update pointer and end-of-list
123+
if node is self._lru:
124+
self._lru = node.next
125+
self._lru.previous = None
126+
112127
if node.previous is not None:
113128
node.previous.next = node.next
114-
115129
if node.next is not None:
116130
node.next.previous = node.previous
117131

118-
if self._lru == node:
119-
if node.next is not None: #only update lru pointer if there are more than 1 elements.
120-
self._lru = node.next
121-
122-
if not self._data:
123-
# if there are no items, set the LRU to this node
124-
self._lru = node
125-
else:
126-
# if there is at least one item, update the MRU chain
127-
self._mru.next = node
128-
129-
node.next = None
130132
node.previous = self._mru
133+
node.previous.next = node
134+
node.next = None
131135
self._mru = node
136+
132137
return node
133138

134139
def _rollover(self):
135140
"""Check we're within the size limit. Otherwise drop the LRU."""
136141
if len(self._data) > self._max_size:
137142
next_item = self._lru.next
143+
if next_item is None:
144+
print self
138145
del self._data[self._lru.key]
139146
self._lru = next_item
147+
self._lru.previous = None
140148

141149
def __str__(self):
142150
"""User friendly representation of cache."""
143151
nodes = []
144152
node = self._mru
145153
while node is not None:
146-
nodes.append('<%s: %s> -->' % (node.key, node.value))
154+
nodes.append('\t<%s: %s> -->' % (node.key, node.value))
147155
node = node.previous
148156
return '<MRU>\n' + '\n'.join(nodes) + '\n<LRU>'
149157

tests/storage/adapters/test_cache_trait.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Cache testing module."""
22
#pylint: disable=protected-access,no-self-use,line-too-long
33
import time
4+
from random import choice
45

56
import pytest
67

@@ -55,6 +56,9 @@ def test_lru_behaviour(self, mocker):
5556
assert 'some' not in cache._data
5657
assert len(cache._data) == 5
5758
assert cache._lru.key == 'some_other'
59+
for node in cache._data.values():
60+
if node != cache._mru:
61+
assert node.next is not None
5862

5963
# `some_other` should be the next key to be evicted.
6064
# if we issue a get call for it, it should be marked as the MRU,
@@ -64,6 +68,21 @@ def test_lru_behaviour(self, mocker):
6468
assert cache._mru.key == 'some_other'
6569
assert cache._lru.key == 'another'
6670

71+
def test_intensive_usage_behavior(self, mocker):
72+
"""Test fetches with random repeated strings."""
73+
user_func = mocker.Mock()
74+
user_func.side_effect = lambda *p, **kw: len(p[0])
75+
key_func = mocker.Mock()
76+
key_func.side_effect = lambda *p, **kw: p[0]
77+
cache = cache_trait.LocalMemoryCache(key_func, user_func, 1, 5)
78+
79+
strings = ['a', 'bb', 'ccc', 'dddd', 'eeeee', 'ffffff', 'ggggggg', 'hhhhhhhh',
80+
'jjjjjjjjj', 'kkkkkkkkkk']
81+
for _ in range(0, 100000):
82+
chosen = choice(strings)
83+
assert cache.get(chosen) == len(chosen)
84+
assert cache._lru is not None
85+
6786
def test_expiration_behaviour(self, mocker):
6887
"""Test time expiration works as expected."""
6988
user_func = mocker.Mock()

0 commit comments

Comments
 (0)