-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for async_simple_cache_middleware
- Loading branch information
Showing
5 changed files
with
275 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Async support upport for caching certain methods via ``async_simple_cache_middleware`` as well as constructing custom async caching middleware via ``async_construct_simple_cache_middleware``. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import functools | ||
import threading | ||
from typing import ( | ||
TYPE_CHECKING, | ||
Any, | ||
Callable, | ||
Collection, | ||
Dict, | ||
Tuple, | ||
Type, | ||
cast, | ||
) | ||
|
||
import lru | ||
|
||
from web3._utils.caching import ( | ||
generate_cache_key, | ||
) | ||
from web3.types import ( | ||
AsyncMiddleware, | ||
Middleware, | ||
RPCEndpoint, | ||
RPCResponse, | ||
) | ||
|
||
if TYPE_CHECKING: | ||
from web3 import Web3 # noqa: F401 | ||
|
||
|
||
SIMPLE_CACHE_RPC_WHITELIST = cast( | ||
Tuple[RPCEndpoint], | ||
( | ||
"web3_clientVersion", | ||
"eth_getBlockTransactionCountByHash", | ||
"eth_getUncleCountByBlockHash", | ||
"eth_getBlockByHash", | ||
"eth_getTransactionByHash", | ||
"eth_getTransactionByBlockHashAndIndex", | ||
"eth_getRawTransactionByHash", | ||
"eth_getUncleByBlockHashAndIndex", | ||
"eth_chainId", | ||
), | ||
) | ||
|
||
|
||
def _should_cache_response( | ||
_method: RPCEndpoint, _params: Any, response: RPCResponse | ||
) -> bool: | ||
return ( | ||
"error" not in response | ||
and "result" in response | ||
and response["result"] is not None | ||
) | ||
|
||
|
||
async def async_construct_simple_cache_middleware( | ||
cache_class: Type[Dict[Any, Any]], | ||
rpc_whitelist: Collection[RPCEndpoint] = SIMPLE_CACHE_RPC_WHITELIST, | ||
should_cache_fn: Callable[ | ||
[RPCEndpoint, Any, RPCResponse], bool | ||
] = _should_cache_response, | ||
) -> Middleware: | ||
""" | ||
Constructs a middleware which caches responses based on the request | ||
``method`` and ``params`` | ||
:param cache_class: Any dictionary-like object | ||
:param rpc_whitelist: A set of RPC methods which may have their responses cached. | ||
:param should_cache_fn: A callable which accepts ``method`` ``params`` and | ||
``response`` and returns a boolean as to whether the response should be | ||
cached. | ||
""" | ||
|
||
async def async_simple_cache_middleware( | ||
make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "Web3" | ||
) -> AsyncMiddleware: | ||
cache = cache_class() | ||
lock = threading.Lock() | ||
|
||
async def middleware(method: RPCEndpoint, params: Any) -> RPCResponse: | ||
if method in rpc_whitelist: | ||
with lock: | ||
cache_key = generate_cache_key((method, params)) | ||
if cache_key not in cache: | ||
response = await make_request(method, params) | ||
if should_cache_fn(method, params, response): | ||
cache[cache_key] = response | ||
return response | ||
return cache[cache_key] | ||
else: | ||
return await make_request(method, params) | ||
|
||
return middleware | ||
|
||
return async_simple_cache_middleware | ||
|
||
|
||
async def _async_simple_cache_middleware( | ||
make_request: Callable[[RPCEndpoint, Any], Any], async_w3: "Web3" | ||
): | ||
middleware = await async_construct_simple_cache_middleware( | ||
cache_class=cast(Type[Dict[Any, Any]], functools.partial(lru.LRU, 256)), | ||
) | ||
return await middleware(make_request, async_w3) |