Skip to content

Commit

Permalink
feat: add thread safety to local cache (#224)
Browse files Browse the repository at this point in the history
- thread safety in LocalLRUCache since by default the cachetools classes
are not threadsafe:
https://cachetools.readthedocs.io/en/latest/#cache-implementations
- option to clear all instances in singleton metaclass

---------

Signed-off-by: Avik Basu <[email protected]>
  • Loading branch information
ab93 authored Jul 7, 2023
1 parent b21e246 commit 466681b
Show file tree
Hide file tree
Showing 6 changed files with 445 additions and 525 deletions.
16 changes: 11 additions & 5 deletions numalogic/registry/localcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# limitations under the License.

from copy import deepcopy
from threading import Lock
from typing import Optional

from cachetools import TTLCache
Expand All @@ -19,7 +20,7 @@


class LocalLRUCache(ArtifactCache, metaclass=Singleton):
r"""A local in-memory LRU cache with per item Time-to-live support.
r"""A local in-memory LRU cache registry with per artifact Time-to-live support.
Args:
----
Expand All @@ -34,6 +35,7 @@ def __init__(self, cachesize: int = 512, ttl: int = 300):
super().__init__(cachesize, ttl)
if not self.__cache:
self.__cache = TTLCache(maxsize=cachesize, ttl=ttl)
self.__lock = Lock()

def __contains__(self, artifact_key: str) -> bool:
"""Check if an artifact is in the cache."""
Expand All @@ -51,7 +53,8 @@ def load(self, artifact_key: str) -> Optional[ArtifactData]:
-------
The artifact data instance if found, None otherwise.
"""
return self.__cache.get(artifact_key)
with self.__lock:
return self.__cache.get(artifact_key)

def save(self, key: str, artifact: ArtifactData) -> None:
"""
Expand All @@ -64,7 +67,8 @@ def save(self, key: str, artifact: ArtifactData) -> None:
"""
artifact = deepcopy(artifact)
artifact.extras["source"] = self._STORETYPE
self.__cache[key] = artifact
with self.__lock:
self.__cache[key] = artifact

def delete(self, key: str) -> Optional[ArtifactData]:
"""
Expand All @@ -78,11 +82,13 @@ def delete(self, key: str) -> Optional[ArtifactData]:
-------
The deleted artifact data instance if found, None otherwise.
"""
return self.__cache.pop(key, default=None)
with self.__lock:
return self.__cache.pop(key, default=None)

def clear(self) -> None:
"""Clears the whole cache."""
self.__cache.clear()
with self.__lock:
self.__cache.clear()

def keys(self) -> list[str]:
"""Returns the current keys of the cache."""
Expand Down
4 changes: 4 additions & 0 deletions numalogic/tools/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

@classmethod
def clear_instances(cls):
cls._instances = {}
Loading

0 comments on commit 466681b

Please sign in to comment.