22
22
TYPE_CHECKING ,
23
23
)
24
24
25
- import asyncstdlib as a
26
25
from bt_decode import MetadataV15 , PortableRegistry , decode as decode_by_type_string
27
26
from scalecodec .base import ScaleBytes , ScaleType , RuntimeConfigurationObject
28
27
from scalecodec .types import (
58
57
get_next_id ,
59
58
rng as random ,
60
59
)
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
62
61
from async_substrate_interface .utils .decoding import (
63
62
_determine_if_old_runtime_call ,
64
63
_bt_decode_to_dict_or_list ,
@@ -539,14 +538,17 @@ def __init__(
539
538
"You are instantiating the AsyncSubstrateInterface Websocket outside of an event loop. "
540
539
"Verify this is intended."
541
540
)
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
543
545
self .last_received = now
544
546
self .last_sent = now
547
+ self ._in_use_ids = set ()
545
548
546
549
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 ()
550
552
return self
551
553
552
554
@staticmethod
@@ -559,18 +561,19 @@ async def connect(self, force=False):
559
561
self .last_sent = now
560
562
if self ._exit_task :
561
563
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
574
577
575
578
async def __aexit__ (self , exc_type , exc_val , exc_tb ):
576
579
async with self ._lock : # TODO is this actually what I want to happen?
@@ -619,6 +622,7 @@ async def _recv(self) -> None:
619
622
self ._open_subscriptions -= 1
620
623
if "id" in response :
621
624
self ._received [response ["id" ]] = response
625
+ self ._in_use_ids .remove (response ["id" ])
622
626
elif "params" in response :
623
627
self ._received [response ["params" ]["subscription" ]] = response
624
628
else :
@@ -649,6 +653,9 @@ async def send(self, payload: dict) -> int:
649
653
id: the internal ID of the request (incremented int)
650
654
"""
651
655
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 )
652
659
# self._open_subscriptions += 1
653
660
await self .max_subscriptions .acquire ()
654
661
try :
@@ -674,7 +681,7 @@ async def retrieve(self, item_id: int) -> Optional[dict]:
674
681
self .max_subscriptions .release ()
675
682
return item
676
683
except KeyError :
677
- await asyncio .sleep (0.001 )
684
+ await asyncio .sleep (0.1 )
678
685
return None
679
686
680
687
@@ -725,6 +732,7 @@ def __init__(
725
732
)
726
733
else :
727
734
self .ws = AsyncMock (spec = Websocket )
735
+
728
736
self ._lock = asyncio .Lock ()
729
737
self .config = {
730
738
"use_remote_preset" : use_remote_preset ,
@@ -748,6 +756,12 @@ def __init__(
748
756
self .registry_type_map = {}
749
757
self .type_id_to_name = {}
750
758
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
+ )
751
765
752
766
async def __aenter__ (self ):
753
767
if not self ._mock :
@@ -1869,9 +1883,8 @@ async def get_metadata(self, block_hash=None) -> MetadataV15:
1869
1883
1870
1884
return runtime .metadata_v15
1871
1885
1872
- @a .lru_cache (maxsize = 512 )
1873
1886
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 )
1875
1888
1876
1889
async def _get_parent_block_hash (self , block_hash ):
1877
1890
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:
1916
1929
"Unknown error occurred during retrieval of events"
1917
1930
)
1918
1931
1919
- @a .lru_cache (maxsize = 16 )
1920
1932
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 )
1922
1934
1923
1935
get_block_runtime_version = get_block_runtime_info
1924
1936
@@ -1929,9 +1941,8 @@ async def _get_block_runtime_info(self, block_hash: str) -> dict:
1929
1941
response = await self .rpc_request ("state_getRuntimeVersion" , [block_hash ])
1930
1942
return response .get ("result" )
1931
1943
1932
- @a .lru_cache (maxsize = 512 )
1933
1944
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 )
1935
1946
1936
1947
async def _get_block_runtime_version_for (self , block_hash : str ):
1937
1948
"""
@@ -2149,14 +2160,14 @@ async def _make_rpc_request(
2149
2160
and current_time - self .ws .last_sent >= self .retry_timeout
2150
2161
):
2151
2162
if attempt >= self .max_retries :
2152
- logger .warning (
2163
+ logger .error (
2153
2164
f"Timed out waiting for RPC requests { attempt } times. Exiting."
2154
2165
)
2155
2166
raise MaxRetriesExceeded ("Max retries reached." )
2156
2167
else :
2157
2168
self .ws .last_received = time .time ()
2158
2169
await self .ws .connect (force = True )
2159
- logger .error (
2170
+ logger .warning (
2160
2171
f"Timed out waiting for RPC requests. "
2161
2172
f"Retrying attempt { attempt + 1 } of { self .max_retries } "
2162
2173
)
@@ -2240,9 +2251,8 @@ async def rpc_request(
2240
2251
else :
2241
2252
raise SubstrateRequestException (result [payload_id ][0 ])
2242
2253
2243
- @a .lru_cache (maxsize = 512 )
2244
2254
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 )
2246
2256
2247
2257
async def _get_block_hash (self , block_id : int ) -> str :
2248
2258
return (await self .rpc_request ("chain_getBlockHash" , [block_id ]))["result" ]
0 commit comments