2222 TYPE_CHECKING ,
2323)
2424
25- import asyncstdlib as a
2625from bt_decode import MetadataV15 , PortableRegistry , decode as decode_by_type_string
2726from scalecodec .base import ScaleBytes , ScaleType , RuntimeConfigurationObject
2827from scalecodec .types import (
5857 get_next_id ,
5958 rng as random ,
6059)
61- from async_substrate_interface .utils .cache import async_sql_lru_cache
60+ from async_substrate_interface .utils .cache import async_sql_lru_cache , CachedFetcher
6261from async_substrate_interface .utils .decoding import (
6362 _determine_if_old_runtime_call ,
6463 _bt_decode_to_dict_or_list ,
@@ -539,14 +538,17 @@ def __init__(
539538 "You are instantiating the AsyncSubstrateInterface Websocket outside of an event loop. "
540539 "Verify this is intended."
541540 )
542- now = asyncio .new_event_loop ().time ()
541+ # default value for in case there's no running asyncio loop
542+ # this really doesn't matter in most cases, as it's only used for comparison on the first call to
543+ # see how long it's been since the last call
544+ now = 0.0
543545 self .last_received = now
544546 self .last_sent = now
547+ self ._in_use_ids = set ()
545548
546549 async def __aenter__ (self ):
547- async with self ._lock :
548- self ._in_use += 1
549- await self .connect ()
550+ self ._in_use += 1
551+ await self .connect ()
550552 return self
551553
552554 @staticmethod
@@ -559,18 +561,19 @@ async def connect(self, force=False):
559561 self .last_sent = now
560562 if self ._exit_task :
561563 self ._exit_task .cancel ()
562- if not self ._initialized or force :
563- self ._initialized = True
564- try :
565- self ._receiving_task .cancel ()
566- await self ._receiving_task
567- await self .ws .close ()
568- except (AttributeError , asyncio .CancelledError ):
569- pass
570- self .ws = await asyncio .wait_for (
571- connect (self .ws_url , ** self ._options ), timeout = 10
572- )
573- self ._receiving_task = asyncio .create_task (self ._start_receiving ())
564+ async with self ._lock :
565+ if not self ._initialized or force :
566+ try :
567+ self ._receiving_task .cancel ()
568+ await self ._receiving_task
569+ await self .ws .close ()
570+ except (AttributeError , asyncio .CancelledError ):
571+ pass
572+ self .ws = await asyncio .wait_for (
573+ connect (self .ws_url , ** self ._options ), timeout = 10
574+ )
575+ self ._receiving_task = asyncio .create_task (self ._start_receiving ())
576+ self ._initialized = True
574577
575578 async def __aexit__ (self , exc_type , exc_val , exc_tb ):
576579 async with self ._lock : # TODO is this actually what I want to happen?
@@ -619,6 +622,7 @@ async def _recv(self) -> None:
619622 self ._open_subscriptions -= 1
620623 if "id" in response :
621624 self ._received [response ["id" ]] = response
625+ self ._in_use_ids .remove (response ["id" ])
622626 elif "params" in response :
623627 self ._received [response ["params" ]["subscription" ]] = response
624628 else :
@@ -649,6 +653,9 @@ async def send(self, payload: dict) -> int:
649653 id: the internal ID of the request (incremented int)
650654 """
651655 original_id = get_next_id ()
656+ while original_id in self ._in_use_ids :
657+ original_id = get_next_id ()
658+ self ._in_use_ids .add (original_id )
652659 # self._open_subscriptions += 1
653660 await self .max_subscriptions .acquire ()
654661 try :
@@ -674,7 +681,7 @@ async def retrieve(self, item_id: int) -> Optional[dict]:
674681 self .max_subscriptions .release ()
675682 return item
676683 except KeyError :
677- await asyncio .sleep (0.001 )
684+ await asyncio .sleep (0.1 )
678685 return None
679686
680687
@@ -725,6 +732,7 @@ def __init__(
725732 )
726733 else :
727734 self .ws = AsyncMock (spec = Websocket )
735+
728736 self ._lock = asyncio .Lock ()
729737 self .config = {
730738 "use_remote_preset" : use_remote_preset ,
@@ -748,6 +756,12 @@ def __init__(
748756 self .registry_type_map = {}
749757 self .type_id_to_name = {}
750758 self ._mock = _mock
759+ self ._block_hash_fetcher = CachedFetcher (512 , self ._get_block_hash )
760+ self ._parent_hash_fetcher = CachedFetcher (512 , self ._get_parent_block_hash )
761+ self ._runtime_info_fetcher = CachedFetcher (16 , self ._get_block_runtime_info )
762+ self ._runtime_version_for_fetcher = CachedFetcher (
763+ 512 , self ._get_block_runtime_version_for
764+ )
751765
752766 async def __aenter__ (self ):
753767 if not self ._mock :
@@ -1869,9 +1883,8 @@ async def get_metadata(self, block_hash=None) -> MetadataV15:
18691883
18701884 return runtime .metadata_v15
18711885
1872- @a .lru_cache (maxsize = 512 )
18731886 async def get_parent_block_hash (self , block_hash ):
1874- return await self ._get_parent_block_hash (block_hash )
1887+ return await self ._parent_hash_fetcher . execute (block_hash )
18751888
18761889 async def _get_parent_block_hash (self , block_hash ):
18771890 block_header = await self .rpc_request ("chain_getHeader" , [block_hash ])
@@ -1916,9 +1929,8 @@ async def get_storage_by_key(self, block_hash: str, storage_key: str) -> Any:
19161929 "Unknown error occurred during retrieval of events"
19171930 )
19181931
1919- @a .lru_cache (maxsize = 16 )
19201932 async def get_block_runtime_info (self , block_hash : str ) -> dict :
1921- return await self ._get_block_runtime_info (block_hash )
1933+ return await self ._runtime_info_fetcher . execute (block_hash )
19221934
19231935 get_block_runtime_version = get_block_runtime_info
19241936
@@ -1929,9 +1941,8 @@ async def _get_block_runtime_info(self, block_hash: str) -> dict:
19291941 response = await self .rpc_request ("state_getRuntimeVersion" , [block_hash ])
19301942 return response .get ("result" )
19311943
1932- @a .lru_cache (maxsize = 512 )
19331944 async def get_block_runtime_version_for (self , block_hash : str ):
1934- return await self ._get_block_runtime_version_for (block_hash )
1945+ return await self ._runtime_version_for_fetcher . execute (block_hash )
19351946
19361947 async def _get_block_runtime_version_for (self , block_hash : str ):
19371948 """
@@ -2149,14 +2160,14 @@ async def _make_rpc_request(
21492160 and current_time - self .ws .last_sent >= self .retry_timeout
21502161 ):
21512162 if attempt >= self .max_retries :
2152- logger .warning (
2163+ logger .error (
21532164 f"Timed out waiting for RPC requests { attempt } times. Exiting."
21542165 )
21552166 raise MaxRetriesExceeded ("Max retries reached." )
21562167 else :
21572168 self .ws .last_received = time .time ()
21582169 await self .ws .connect (force = True )
2159- logger .error (
2170+ logger .warning (
21602171 f"Timed out waiting for RPC requests. "
21612172 f"Retrying attempt { attempt + 1 } of { self .max_retries } "
21622173 )
@@ -2240,9 +2251,8 @@ async def rpc_request(
22402251 else :
22412252 raise SubstrateRequestException (result [payload_id ][0 ])
22422253
2243- @a .lru_cache (maxsize = 512 )
22442254 async def get_block_hash (self , block_id : int ) -> str :
2245- return await self ._get_block_hash (block_id )
2255+ return await self ._block_hash_fetcher . execute (block_id )
22462256
22472257 async def _get_block_hash (self , block_id : int ) -> str :
22482258 return (await self .rpc_request ("chain_getBlockHash" , [block_id ]))["result" ]
0 commit comments