7
7
from aws_lambda_powertools .warnings import PowertoolsUserWarning
8
8
9
9
10
+ class KeyBufferCache :
11
+ """
12
+ A cache implementation for a single key with size tracking and eviction support.
13
+
14
+ This class manages a buffer for a specific key, keeping track of the current size
15
+ and providing methods to add, remove, and manage cached items. It supports automatic
16
+ eviction tracking and size management.
17
+
18
+ Attributes
19
+ ----------
20
+ cache : deque
21
+ A double-ended queue storing the cached items.
22
+ current_size : int
23
+ The total size of all items currently in the cache.
24
+ has_evicted : bool
25
+ A flag indicating whether any items have been evicted from the cache.
26
+ """
27
+
28
+ def __init__ (self ):
29
+ """
30
+ Initialize a buffer cache for a specific key.
31
+ """
32
+ self .cache : deque = deque ()
33
+ self .current_size : int = 0
34
+ self .has_evicted : bool = False
35
+
36
+ def add (self , item : Any ) -> None :
37
+ """
38
+ Add an item to the cache.
39
+
40
+ Parameters
41
+ ----------
42
+ item : Any
43
+ The item to be stored in the cache.
44
+ """
45
+ item_size = len (str (item ))
46
+ self .cache .append (item )
47
+ self .current_size += item_size
48
+
49
+ def remove_oldest (self ) -> Any :
50
+ """
51
+ Remove and return the oldest item from the cache.
52
+
53
+ Returns
54
+ -------
55
+ Any
56
+ The removed item.
57
+ """
58
+ removed_item = self .cache .popleft ()
59
+ self .current_size -= len (str (removed_item ))
60
+ self .has_evicted = True
61
+ return removed_item
62
+
63
+ def get (self ) -> list :
64
+ """
65
+ Retrieve items for this key.
66
+
67
+ Returns
68
+ -------
69
+ list
70
+ List of items in the cache.
71
+ """
72
+ return list (self .cache )
73
+
74
+ def clear (self ) -> None :
75
+ """
76
+ Clear the cache for this key.
77
+ """
78
+ self .cache .clear ()
79
+ self .current_size = 0
80
+ self .has_evicted = False
81
+
82
+
10
83
class LoggerBufferCache :
84
+ """
85
+ A multi-key buffer cache with size-based eviction and management.
86
+
87
+ This class provides a flexible caching mechanism that manages multiple keys,
88
+ with each key having its own buffer cache. The total size of each key's cache
89
+ is limited, and older items are automatically evicted when the size limit is reached.
90
+
91
+ Key Features:
92
+ - Multiple key support
93
+ - Size-based eviction
94
+ - Tracking of evicted items
95
+ - Configurable maximum buffer size
96
+
97
+ Example
98
+ --------
99
+ >>> buffer_cache = LoggerBufferCache(max_size_bytes=1000)
100
+ >>> buffer_cache.add("logs", "First log message")
101
+ >>> buffer_cache.add("debug", "Debug information")
102
+ >>> buffer_cache.get("logs")
103
+ ['First log message']
104
+ >>> buffer_cache.get_current_size("logs")
105
+ 16
106
+ """
107
+
11
108
def __init__ (self , max_size_bytes : int ):
12
109
"""
13
110
Initialize the LoggerBufferCache.
14
111
15
112
Parameters
16
113
----------
17
114
max_size_bytes : int
18
- Maximum size of the cache in bytes.
115
+ Maximum size of the cache in bytes for each key .
19
116
"""
20
117
self .max_size_bytes : int = max_size_bytes
21
- self .cache : dict [str , deque ] = {}
22
- self .current_size : dict [str , int ] = {}
23
- self .has_evicted : bool = False
118
+ self .cache : dict [str , KeyBufferCache ] = {}
24
119
25
120
def add (self , key : str , item : Any ) -> None :
26
121
"""
@@ -33,31 +128,34 @@ def add(self, key: str, item: Any) -> None:
33
128
item : Any
34
129
The item to be stored in the cache.
35
130
36
- Notes
37
- -----
38
- If the item size exceeds the maximum cache size, it will not be added.
131
+ Returns
132
+ -------
133
+ bool
134
+ True if item was added, False otherwise.
39
135
"""
136
+ # Check if item is larger than entire buffer
40
137
item_size = len (str (item ))
41
-
42
138
if item_size > self .max_size_bytes :
43
139
warnings .warn (
44
- message = f"Item size { item_size } bytes exceeds total cache size { self .max_size_bytes } bytes" ,
45
- category = PowertoolsUserWarning ,
140
+ f"Item size { item_size } bytes exceeds total cache size { self .max_size_bytes } bytes" ,
141
+ PowertoolsUserWarning ,
46
142
stacklevel = 2 ,
47
143
)
48
- return
144
+ return False
49
145
146
+ # Create the key's cache if it doesn't exist
50
147
if key not in self .cache :
51
- self .cache [key ] = deque ()
52
- self .current_size [key ] = 0
148
+ self .cache [key ] = KeyBufferCache ()
53
149
54
- while self .current_size [key ] + item_size > self .max_size_bytes and self .cache [key ]:
55
- removed_item = self .cache [key ].popleft ()
56
- self .current_size [key ] -= len (str (removed_item ))
57
- self .has_evicted = True
150
+ # Calculate the size after adding the new item
151
+ new_total_size = self .cache [key ].current_size + item_size
58
152
59
- self .cache [key ].append (item )
60
- self .current_size [key ] += item_size
153
+ # If adding the item would exceed max size, remove oldest items
154
+ while new_total_size > self .max_size_bytes and self .cache [key ].cache :
155
+ self .cache [key ].remove_oldest ()
156
+ new_total_size = self .cache [key ].current_size + item_size
157
+
158
+ self .cache [key ].add (item )
61
159
62
160
def get (self , key : str ) -> list :
63
161
"""
@@ -73,21 +171,53 @@ def get(self, key: str) -> list:
73
171
list
74
172
List of items for the given key, or an empty list if the key doesn't exist.
75
173
"""
76
- return list ( self .cache . get (key , deque ()) )
174
+ return [] if key not in self .cache else self . cache [ key ]. get ()
77
175
78
176
def clear (self , key : str | None = None ) -> None :
79
177
"""
80
178
Clear the cache, either for a specific key or entirely.
81
179
82
180
Parameters
83
181
----------
84
- key : str, optional
182
+ key : Optional[ str] , optional
85
183
The key to clear. If None, clears the entire cache.
86
184
"""
87
185
if key :
88
186
if key in self .cache :
187
+ self .cache [key ].clear ()
89
188
del self .cache [key ]
90
- del self .current_size [key ]
91
189
else :
92
190
self .cache .clear ()
93
- self .current_size .clear ()
191
+
192
+ def has_evicted (self , key : str ) -> bool :
193
+ """
194
+ Check if a specific key's cache has evicted items.
195
+
196
+ Parameters
197
+ ----------
198
+ key : str
199
+ The key to check for evicted items.
200
+
201
+ Returns
202
+ -------
203
+ bool
204
+ True if items have been evicted, False otherwise.
205
+ """
206
+ return False if key not in self .cache else self .cache [key ].has_evicted
207
+
208
+ def get_current_size (self , key : str ) -> int | None :
209
+ """
210
+ Get the current size of the buffer for a specific key.
211
+
212
+ Parameters
213
+ ----------
214
+ key : str
215
+ The key to get the current size for.
216
+
217
+ Returns
218
+ -------
219
+ int
220
+ The current size of the buffer for the key.
221
+ Returns 0 if the key does not exist.
222
+ """
223
+ return None if key not in self .cache else self .cache [key ].current_size
0 commit comments