Skip to content

Commit

Permalink
feat: reimplement ttl_cache with no threading lock (#39)
Browse files Browse the repository at this point in the history
* feat: reimplement ttl_cache with no threading lock

we dont care about race conditions when checksumming addresses, the lock adds more friction than value

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
BobTheBuidler and github-actions[bot] authored Dec 17, 2024
1 parent 9b1acc0 commit 1ae24ef
Showing 2 changed files with 54 additions and 2 deletions.
50 changes: 50 additions & 0 deletions evmspec/data/_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from importlib.metadata import version
from time import monotonic

from cachetools import cached, keys
from cachetools.func import TTLCache, _UnboundTTLCache


_CACHETOOLS_VERSION = tuple(int(i) for i in version("cachetools").split("."))


def ttl_cache(maxsize=128, ttl=600, timer=monotonic, typed=False):
"""Decorator to wrap a function with a memoizing callable that saves
up to `maxsize` results based on a Least Recently Used (LRU)
algorithm with a per-item time-to-live (TTL) value.
"""
if maxsize is None:
return _cache(_UnboundTTLCache(ttl, timer), None, typed)
elif callable(maxsize):
return _cache(TTLCache(128, ttl, timer), 128, typed)(maxsize)
else:
return _cache(TTLCache(maxsize, ttl, timer), maxsize, typed)


def _cache(cache, maxsize, typed, info: bool = False):
# reimplement ttl_cache with no RLock for race conditions

key = keys.typedkey if typed else keys.hashkey
get_params = lambda: {"maxsize": maxsize, "typed": typed}

# `info` param was added in 5.3
if _CACHETOOLS_VERSION >= (5, 3):

def decorator(func):
wrapper = cached(cache=cache, key=key, lock=None, info=info)(func)
wrapper.cache_parameters = get_params
return wrapper

elif info:
raise ValueError(
"You cannot use the `info` param with cachetools versions < 5.3"
)

else:

def decorator(func):
wrapper = cached(cache=cache, key=key, lock=None)(func)
wrapper.cache_parameters = get_params
return wrapper

return decorator
6 changes: 4 additions & 2 deletions evmspec/data/_main.py
Original file line number Diff line number Diff line change
@@ -4,16 +4,18 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Callable, Tuple, Type, TypeVar, Union

from cachetools.func import ttl_cache
from cchecksum import to_checksum_address
from hexbytes import HexBytes
from msgspec import Raw, Struct, json
from typing_extensions import Self

from evmspec.data._cache import ttl_cache

if TYPE_CHECKING:
from evmspec.structs.log import Log
from evmspec.structs.receipt import TransactionReceipt


_T = TypeVar("_T")
"""A generic type variable."""

@@ -88,7 +90,7 @@ def _decode_hook(cls, typ: Type["Address"], obj: str):
return cls.checksum(obj)

@classmethod
@ttl_cache(ttl=600)
@ttl_cache(maxsize=None, ttl=600)
def checksum(cls, address: str) -> Self:
"""Returns the checksummed version of the address.

0 comments on commit 1ae24ef

Please sign in to comment.