11from __future__ import annotations
22
3+ import logging
34import time
45from collections import OrderedDict
56from typing import TYPE_CHECKING , Any , Literal
67
78from zarr .abc .store import ByteRequest , Store
89from zarr .storage ._wrapper import WrapperStore
9- import logging
10+
1011logging .basicConfig (level = logging .INFO )
1112logger = logging .getLogger (__name__ )
1213
@@ -31,10 +32,11 @@ def buffer_size(v: Any) -> int:
3132 # Fallback to numpy if available
3233 try :
3334 import numpy as np
35+
3436 return int (np .asarray (v ).nbytes )
3537 except ImportError :
3638 # If numpy not available, estimate size
37- return len (str (v ).encode (' utf-8' ))
39+ return len (str (v ).encode (" utf-8" ))
3840
3941
4042class CacheStore (WrapperStore [Store ]):
@@ -85,11 +87,17 @@ def __init__(
8587 max_age_seconds : int | str = "infinity" ,
8688 max_size : int | None = None ,
8789 key_insert_times : dict [str , float ] | None = None ,
88- cache_set_data : bool = True
90+ cache_set_data : bool = True ,
8991 ) -> None :
9092 super ().__init__ (store )
9193 self ._cache = cache_store
92- self .max_age_seconds = max_age_seconds
94+ # Validate and convert max_age_seconds
95+ if isinstance (max_age_seconds , str ):
96+ if max_age_seconds != "infinity" :
97+ raise ValueError ("max_age_seconds string value must be 'infinity'" )
98+ self .max_age_seconds = "infinity"
99+ else :
100+ self .max_age_seconds = max_age_seconds
93101 self .max_size = max_size
94102 if key_insert_times is None :
95103 self .key_insert_times = {}
@@ -110,16 +118,8 @@ def _is_key_fresh(self, key: str) -> bool:
110118
111119 def _get_cache_size (self , key : str ) -> int :
112120 """Get the size of a cached item."""
113- try :
114- # Try to get the size from the cache store if it supports getsize
115- if hasattr (self ._cache , 'getsize' ):
116- # This would be async, but we need sync here
117- # For now, estimate size by getting the data
118- pass
119- # For now, we'll estimate by getting the data when we cache it
120- return 0 # Will be properly set when caching
121- except Exception :
122- return 0
121+ # For now, we'll estimate by getting the data when we cache it
122+ return 0 # Will be properly set when caching
123123
124124 def _accommodate_value (self , value_size : int ) -> None :
125125 """Ensure there is enough space in the cache for a new value."""
@@ -142,17 +142,21 @@ def _evict_key(self, key: str) -> None:
142142 if key in self .key_insert_times :
143143 del self .key_insert_times [key ]
144144 # Note: Actual size reduction will happen when we get the item size
145- logger .info (' _evict_key: evicted key %s from cache' , key )
145+ logger .info (" _evict_key: evicted key %s from cache" , key )
146146 except Exception as e :
147- logger .warning (' _evict_key: failed to evict key %s: %s' , key , e )
147+ logger .warning (" _evict_key: failed to evict key %s: %s" , key , e )
148148
149149 def _cache_value (self , key : str , value : Any ) -> None :
150150 """Cache a value with size tracking."""
151151 value_size = buffer_size (value )
152152
153153 # Check if value exceeds max size
154154 if self .max_size is not None and value_size > self .max_size :
155- logger .warning ('_cache_value: value size %d exceeds max_size %d, not caching' , value_size , self .max_size )
155+ logger .warning (
156+ "_cache_value: value size %d exceeds max_size %d, not caching" ,
157+ value_size ,
158+ self .max_size ,
159+ )
156160 return
157161
158162 # Make room for the new value
@@ -163,7 +167,7 @@ def _cache_value(self, key: str, value: Any) -> None:
163167 self ._current_size += value_size
164168 self .key_insert_times [key ] = time .monotonic ()
165169
166- logger .info (' _cache_value: cached key %s with size %d bytes' , key , value_size )
170+ logger .info (" _cache_value: cached key %s with size %d bytes" , key , value_size )
167171
168172 def _update_access_order (self , key : str ) -> None :
169173 """Update the access order for LRU tracking."""
@@ -184,20 +188,22 @@ async def _get_try_cache(
184188 """Try to get data from cache first, falling back to source store."""
185189 maybe_cached_result = await self ._cache .get (key , prototype , byte_range )
186190 if maybe_cached_result is not None :
187- logger .info (' _get_try_cache: key %s found in cache' , key )
191+ logger .info (" _get_try_cache: key %s found in cache" , key )
188192 # Update access order for LRU
189193 self ._update_access_order (key )
190194 # Verify the key still exists in source store before returning cached data
191195 if await super ().exists (key ):
192196 return maybe_cached_result
193197 else :
194198 # Key no longer exists in source, clean up cache
195- logger .info ('_get_try_cache: key %s no longer exists in source, cleaning up cache' , key )
199+ logger .info (
200+ "_get_try_cache: key %s no longer exists in source, cleaning up cache" , key
201+ )
196202 await self ._cache .delete (key )
197203 self ._remove_from_tracking (key )
198204 return None
199205 else :
200- logger .info (' _get_try_cache: key %s not found in cache, fetching from store' , key )
206+ logger .info (" _get_try_cache: key %s not found in cache, fetching from store" , key )
201207 maybe_fresh_result = await super ().get (key , prototype , byte_range )
202208 if maybe_fresh_result is None :
203209 await self ._cache .delete (key )
@@ -217,7 +223,7 @@ async def _get_no_cache(
217223 await self ._cache .delete (key )
218224 self ._remove_from_tracking (key )
219225 else :
220- logger .info (' _get_no_cache: key %s found in store, setting in cache' , key )
226+ logger .info (" _get_no_cache: key %s found in store, setting in cache" , key )
221227 await self ._cache .set (key , maybe_fresh_result )
222228 self ._cache_value (key , maybe_fresh_result )
223229 return maybe_fresh_result
@@ -246,10 +252,10 @@ async def get(
246252 The retrieved data, or None if not found
247253 """
248254 if not self ._is_key_fresh (key ):
249- logger .info (' get: key %s is not fresh, fetching from store' , key )
255+ logger .info (" get: key %s is not fresh, fetching from store" , key )
250256 return await self ._get_no_cache (key , prototype , byte_range )
251257 else :
252- logger .info (' get: key %s is fresh, trying cache' , key )
258+ logger .info (" get: key %s is fresh, trying cache" , key )
253259 return await self ._get_try_cache (key , prototype , byte_range )
254260
255261 async def set (self , key : str , value : Buffer ) -> None :
@@ -263,14 +269,14 @@ async def set(self, key: str, value: Buffer) -> None:
263269 value : Buffer
264270 The data to store
265271 """
266- logger .info (' set: setting key %s in store' , key )
272+ logger .info (" set: setting key %s in store" , key )
267273 await super ().set (key , value )
268274 if self .cache_set_data :
269- logger .info (' set: setting key %s in cache' , key )
275+ logger .info (" set: setting key %s in cache" , key )
270276 await self ._cache .set (key , value )
271277 self ._cache_value (key , value )
272278 else :
273- logger .info (' set: deleting key %s from cache' , key )
279+ logger .info (" set: deleting key %s from cache" , key )
274280 await self ._cache .delete (key )
275281 self ._remove_from_tracking (key )
276282
@@ -283,32 +289,34 @@ async def delete(self, key: str) -> None:
283289 key : str
284290 The key to delete
285291 """
286- logger .info (' delete: deleting key %s from store' , key )
292+ logger .info (" delete: deleting key %s from store" , key )
287293 await super ().delete (key )
288- logger .info (' delete: deleting key %s from cache' , key )
294+ logger .info (" delete: deleting key %s from cache" , key )
289295 await self ._cache .delete (key )
290296 self ._remove_from_tracking (key )
291297
292298 def cache_info (self ) -> dict [str , Any ]:
293299 """Return information about the cache state."""
294300 return {
295301 "cache_store_type" : type (self ._cache ).__name__ ,
296- "max_age_seconds" : "infinity" if self .max_age_seconds == "infinity" else self .max_age_seconds ,
302+ "max_age_seconds" : "infinity"
303+ if self .max_age_seconds == "infinity"
304+ else self .max_age_seconds ,
297305 "max_size" : self .max_size ,
298306 "current_size" : self ._current_size ,
299307 "cache_set_data" : self .cache_set_data ,
300308 "tracked_keys" : len (self .key_insert_times ),
301- "cached_keys" : len (self ._cache_order )
309+ "cached_keys" : len (self ._cache_order ),
302310 }
303311
304312 async def clear_cache (self ) -> None :
305313 """Clear all cached data and tracking information."""
306314 # Clear the cache store if it supports clear
307- if hasattr (self ._cache , ' clear' ):
315+ if hasattr (self ._cache , " clear" ):
308316 await self ._cache .clear ()
309317
310318 # Reset tracking
311319 self .key_insert_times .clear ()
312320 self ._cache_order .clear ()
313321 self ._current_size = 0
314- logger .info (' clear_cache: cleared all cache data' )
322+ logger .info (" clear_cache: cleared all cache data" )
0 commit comments