77from aws_lambda_powertools .warnings import PowertoolsUserWarning
88
99
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+
1083class 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+
11108 def __init__ (self , max_size_bytes : int ):
12109 """
13110 Initialize the LoggerBufferCache.
14111
15112 Parameters
16113 ----------
17114 max_size_bytes : int
18- Maximum size of the cache in bytes.
115+ Maximum size of the cache in bytes for each key .
19116 """
20117 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 ] = {}
24119
25120 def add (self , key : str , item : Any ) -> None :
26121 """
@@ -33,31 +128,34 @@ def add(self, key: str, item: Any) -> None:
33128 item : Any
34129 The item to be stored in the cache.
35130
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.
39135 """
136+ # Check if item is larger than entire buffer
40137 item_size = len (str (item ))
41-
42138 if item_size > self .max_size_bytes :
43139 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 ,
46142 stacklevel = 2 ,
47143 )
48- return
144+ return False
49145
146+ # Create the key's cache if it doesn't exist
50147 if key not in self .cache :
51- self .cache [key ] = deque ()
52- self .current_size [key ] = 0
148+ self .cache [key ] = KeyBufferCache ()
53149
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
58152
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 )
61159
62160 def get (self , key : str ) -> list :
63161 """
@@ -73,21 +171,53 @@ def get(self, key: str) -> list:
73171 list
74172 List of items for the given key, or an empty list if the key doesn't exist.
75173 """
76- return list ( self .cache . get (key , deque ()) )
174+ return [] if key not in self .cache else self . cache [ key ]. get ()
77175
78176 def clear (self , key : str | None = None ) -> None :
79177 """
80178 Clear the cache, either for a specific key or entirely.
81179
82180 Parameters
83181 ----------
84- key : str, optional
182+ key : Optional[ str] , optional
85183 The key to clear. If None, clears the entire cache.
86184 """
87185 if key :
88186 if key in self .cache :
187+ self .cache [key ].clear ()
89188 del self .cache [key ]
90- del self .current_size [key ]
91189 else :
92190 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