Skip to content

Commit 00351f1

Browse files
Copilotmykaul
andcommitted
Improve TLSSessionCache performance with OrderedDict and named tuple
Co-authored-by: mykaul <[email protected]>
1 parent 62c2bcf commit 00351f1

File tree

2 files changed

+27
-24
lines changed

2 files changed

+27
-24
lines changed

cassandra/connection.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from __future__ import absolute_import # to enable import io from stdlib
16-
from collections import defaultdict, deque
16+
from collections import defaultdict, deque, OrderedDict, namedtuple
1717
import errno
1818
from functools import wraps, partial, total_ordering
1919
from heapq import heappush, heappop
@@ -128,14 +128,18 @@ def decompress(byts):
128128
frame_header_v3 = struct.Struct('>BhBi')
129129

130130

131+
# Named tuple for TLS session cache entries
132+
_SessionCacheEntry = namedtuple('_SessionCacheEntry', ['session', 'timestamp'])
133+
134+
131135
class TLSSessionCache:
132136
"""
133137
Thread-safe cache for TLS sessions to enable session resumption.
134138
135139
This cache stores TLS sessions per endpoint (host:port) to allow
136140
quick TLS renegotiation when reconnecting to the same server.
137141
Sessions are automatically expired after a TTL and the cache has
138-
a maximum size with LRU eviction.
142+
a maximum size with LRU eviction using OrderedDict.
139143
"""
140144

141145
def __init__(self, max_size=100, ttl=3600):
@@ -146,7 +150,7 @@ def __init__(self, max_size=100, ttl=3600):
146150
max_size: Maximum number of sessions to cache (default: 100)
147151
ttl: Time-to-live for cached sessions in seconds (default: 3600)
148152
"""
149-
self._sessions = {} # {endpoint_key: (session, timestamp, access_time)}
153+
self._sessions = OrderedDict() # OrderedDict for O(1) LRU eviction
150154
self._lock = RLock()
151155
self._max_size = max_size
152156
self._ttl = ttl
@@ -171,16 +175,16 @@ def get_session(self, host, port):
171175
if key not in self._sessions:
172176
return None
173177

174-
session, timestamp, _ = self._sessions[key]
178+
entry = self._sessions[key]
175179

176180
# Check if session has expired
177-
if time.time() - timestamp > self._ttl:
181+
if time.time() - entry.timestamp > self._ttl:
178182
del self._sessions[key]
179183
return None
180184

181-
# Update access time for LRU
182-
self._sessions[key] = (session, timestamp, time.time())
183-
return session
185+
# Move to end to mark as recently used (LRU)
186+
self._sessions.move_to_end(key)
187+
return entry.session
184188

185189
def set_session(self, host, port, session):
186190
"""
@@ -198,23 +202,26 @@ def set_session(self, host, port, session):
198202
current_time = time.time()
199203

200204
with self._lock:
201-
# If cache is at max size, remove least recently used entry
202-
if len(self._sessions) >= self._max_size and key not in self._sessions:
203-
# Find entry with oldest access time
204-
oldest_key = min(self._sessions.keys(),
205-
key=lambda k: self._sessions[k][2])
206-
del self._sessions[oldest_key]
205+
# If key already exists, just update it
206+
if key in self._sessions:
207+
self._sessions[key] = _SessionCacheEntry(session, current_time)
208+
self._sessions.move_to_end(key)
209+
return
210+
211+
# If cache is at max size, remove least recently used entry (first item)
212+
if len(self._sessions) >= self._max_size:
213+
self._sessions.popitem(last=False)
207214

208-
# Store session with creation time and access time
209-
self._sessions[key] = (session, current_time, current_time)
215+
# Store session with creation time
216+
self._sessions[key] = _SessionCacheEntry(session, current_time)
210217

211218
def clear_expired(self):
212219
"""Remove all expired sessions from the cache."""
213220
current_time = time.time()
214221
with self._lock:
215222
expired_keys = [
216-
key for key, (_, timestamp, _) in self._sessions.items()
217-
if current_time - timestamp > self._ttl
223+
key for key, entry in self._sessions.items()
224+
if current_time - entry.timestamp > self._ttl
218225
]
219226
for key in expired_keys:
220227
del self._sessions[key]

tests/unit/test_tls_session_cache.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,15 @@ def test_cache_max_size_eviction(self):
8787

8888
# Fill cache to capacity
8989
cache.set_session('host1', 9042, session1)
90-
time.sleep(0.01) # Ensure different access times
9190
cache.set_session('host2', 9042, session2)
92-
time.sleep(0.01)
9391
cache.set_session('host3', 9042, session3)
9492

9593
self.assertEqual(cache.size(), 3)
9694

97-
# Access session2 to update its access time
98-
time.sleep(0.01)
95+
# Access session2 to mark it as recently used
9996
cache.get_session('host2', 9042)
10097

101-
# Add a fourth session - should evict session1 (oldest access)
102-
time.sleep(0.01)
98+
# Add a fourth session - should evict session1 (least recently used)
10399
cache.set_session('host4', 9042, session4)
104100

105101
self.assertEqual(cache.size(), 3)

0 commit comments

Comments
 (0)