MyFSIO v0.2.6 Release #18
@@ -1617,14 +1617,16 @@ class ObjectStorage:
|
|||||||
|
|
||||||
Uses LRU eviction to prevent unbounded cache growth.
|
Uses LRU eviction to prevent unbounded cache growth.
|
||||||
Thread-safe with per-bucket locks to reduce contention.
|
Thread-safe with per-bucket locks to reduce contention.
|
||||||
|
Also checks file-based marker for cross-process cache invalidation.
|
||||||
"""
|
"""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
marker_mtime = self._get_cache_marker_mtime(bucket_id)
|
||||||
|
|
||||||
with self._cache_lock:
|
with self._cache_lock:
|
||||||
cached = self._object_cache.get(bucket_id)
|
cached = self._object_cache.get(bucket_id)
|
||||||
if cached:
|
if cached:
|
||||||
objects, timestamp = cached
|
objects, timestamp = cached
|
||||||
if now - timestamp < self._cache_ttl:
|
if now - timestamp < self._cache_ttl and marker_mtime <= timestamp:
|
||||||
self._object_cache.move_to_end(bucket_id)
|
self._object_cache.move_to_end(bucket_id)
|
||||||
return objects
|
return objects
|
||||||
cache_version = self._cache_version.get(bucket_id, 0)
|
cache_version = self._cache_version.get(bucket_id, 0)
|
||||||
@@ -1635,7 +1637,7 @@ class ObjectStorage:
|
|||||||
cached = self._object_cache.get(bucket_id)
|
cached = self._object_cache.get(bucket_id)
|
||||||
if cached:
|
if cached:
|
||||||
objects, timestamp = cached
|
objects, timestamp = cached
|
||||||
if now - timestamp < self._cache_ttl:
|
if now - timestamp < self._cache_ttl and marker_mtime <= timestamp:
|
||||||
self._object_cache.move_to_end(bucket_id)
|
self._object_cache.move_to_end(bucket_id)
|
||||||
return objects
|
return objects
|
||||||
objects = self._build_object_cache(bucket_path)
|
objects = self._build_object_cache(bucket_path)
|
||||||
@@ -1656,21 +1658,42 @@ class ObjectStorage:
|
|||||||
"""Invalidate the object cache and etag index for a bucket.
|
"""Invalidate the object cache and etag index for a bucket.
|
||||||
|
|
||||||
Increments version counter to signal stale reads.
|
Increments version counter to signal stale reads.
|
||||||
|
Also touches marker file for cross-process invalidation.
|
||||||
"""
|
"""
|
||||||
with self._cache_lock:
|
with self._cache_lock:
|
||||||
self._object_cache.pop(bucket_id, None)
|
self._object_cache.pop(bucket_id, None)
|
||||||
self._cache_version[bucket_id] = self._cache_version.get(bucket_id, 0) + 1
|
self._cache_version[bucket_id] = self._cache_version.get(bucket_id, 0) + 1
|
||||||
|
|
||||||
|
self._touch_cache_marker(bucket_id)
|
||||||
|
|
||||||
etag_index_path = self._system_bucket_root(bucket_id) / "etag_index.json"
|
etag_index_path = self._system_bucket_root(bucket_id) / "etag_index.json"
|
||||||
try:
|
try:
|
||||||
etag_index_path.unlink(missing_ok=True)
|
etag_index_path.unlink(missing_ok=True)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _touch_cache_marker(self, bucket_id: str) -> None:
|
||||||
|
"""Touch the cache marker file to signal other processes that cache is stale."""
|
||||||
|
marker_path = self._system_bucket_root(bucket_id) / ".cache_marker"
|
||||||
|
try:
|
||||||
|
marker_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
marker_path.write_text(str(time.time()), encoding="utf-8")
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_cache_marker_mtime(self, bucket_id: str) -> float:
|
||||||
|
"""Get the mtime of the cache marker file, or 0 if it doesn't exist."""
|
||||||
|
marker_path = self._system_bucket_root(bucket_id) / ".cache_marker"
|
||||||
|
try:
|
||||||
|
return marker_path.stat().st_mtime
|
||||||
|
except OSError:
|
||||||
|
return 0
|
||||||
|
|
||||||
def _update_object_cache_entry(self, bucket_id: str, key: str, meta: Optional[ObjectMeta]) -> None:
|
def _update_object_cache_entry(self, bucket_id: str, key: str, meta: Optional[ObjectMeta]) -> None:
|
||||||
"""Update a single entry in the object cache instead of invalidating the whole cache.
|
"""Update a single entry in the object cache instead of invalidating the whole cache.
|
||||||
|
|
||||||
This is a performance optimization - lazy update instead of full invalidation.
|
This is a performance optimization - lazy update instead of full invalidation.
|
||||||
|
Also touches a marker file to signal cache invalidation to other processes.
|
||||||
"""
|
"""
|
||||||
with self._cache_lock:
|
with self._cache_lock:
|
||||||
cached = self._object_cache.get(bucket_id)
|
cached = self._object_cache.get(bucket_id)
|
||||||
@@ -1682,6 +1705,7 @@ class ObjectStorage:
|
|||||||
objects[key] = meta
|
objects[key] = meta
|
||||||
self._cache_version[bucket_id] = self._cache_version.get(bucket_id, 0) + 1
|
self._cache_version[bucket_id] = self._cache_version.get(bucket_id, 0) + 1
|
||||||
self._sorted_key_cache.pop(bucket_id, None)
|
self._sorted_key_cache.pop(bucket_id, None)
|
||||||
|
self._touch_cache_marker(bucket_id)
|
||||||
|
|
||||||
def warm_cache(self, bucket_names: Optional[List[str]] = None) -> None:
|
def warm_cache(self, bucket_names: Optional[List[str]] = None) -> None:
|
||||||
"""Pre-warm the object cache for specified buckets or all buckets.
|
"""Pre-warm the object cache for specified buckets or all buckets.
|
||||||
|
|||||||
Reference in New Issue
Block a user