Skip to content

Commit 7322149

Browse files
authored
Merge pull request #2909 from opentensor/feat/thewhaleking/retry-archive-node-support
Retry archive node support
2 parents 3590785 + b6dfa80 commit 7322149

File tree

7 files changed

+263
-8
lines changed

7 files changed

+263
-8
lines changed

bittensor/core/async_subtensor.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ def __init__(
120120
fallback_endpoints: Optional[list[str]] = None,
121121
retry_forever: bool = False,
122122
_mock: bool = False,
123+
archive_endpoints: Optional[list[str]] = None,
124+
websocket_shutdown_timer: float = 5.0,
123125
):
124126
"""
125127
Initializes an instance of the AsyncSubtensor class.
@@ -132,6 +134,9 @@ def __init__(
132134
Defaults to `None`.
133135
retry_forever: Whether to retry forever on connection errors. Defaults to `False`.
134136
_mock: Whether this is a mock instance. Mainly just for use in testing.
137+
archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases
138+
where you are requesting a block that is too old for your current (presumably lite) node. Defaults to
139+
`None`
135140
136141
Raises:
137142
Any exceptions raised during the setup, configuration, or connection process.
@@ -154,6 +159,8 @@ def __init__(
154159
fallback_endpoints=fallback_endpoints,
155160
retry_forever=retry_forever,
156161
_mock=_mock,
162+
archive_endpoints=archive_endpoints,
163+
ws_shutdown_timer=websocket_shutdown_timer,
157164
)
158165
if self.log_verbose:
159166
logging.info(
@@ -292,6 +299,8 @@ def _get_substrate(
292299
fallback_endpoints: Optional[list[str]] = None,
293300
retry_forever: bool = False,
294301
_mock: bool = False,
302+
archive_endpoints: Optional[list[str]] = None,
303+
ws_shutdown_timer: float = 5.0,
295304
) -> Union[AsyncSubstrateInterface, RetryAsyncSubstrate]:
296305
"""Creates the Substrate instance based on provided arguments.
297306
@@ -300,11 +309,16 @@ def _get_substrate(
300309
Defaults to `None`.
301310
retry_forever: Whether to retry forever on connection errors. Defaults to `False`.
302311
_mock: Whether this is a mock instance. Mainly just for use in testing.
312+
archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases
313+
where you are requesting a block that is too old for your current (presumably lite) node. Defaults to
314+
`None`
315+
ws_shutdown_timer: Amount of time, in seconds, to wait after the last response from the chain to close the
316+
connection.
303317
304318
Returns:
305319
the instance of the SubstrateInterface or RetrySyncSubstrate class.
306320
"""
307-
if fallback_endpoints or retry_forever:
321+
if fallback_endpoints or retry_forever or archive_endpoints:
308322
return RetryAsyncSubstrate(
309323
url=self.chain_endpoint,
310324
fallback_chains=fallback_endpoints,
@@ -314,6 +328,8 @@ def _get_substrate(
314328
use_remote_preset=True,
315329
chain_name="Bittensor",
316330
_mock=_mock,
331+
archive_nodes=archive_endpoints,
332+
ws_shutdown_timer=ws_shutdown_timer,
317333
)
318334
return AsyncSubstrateInterface(
319335
url=self.chain_endpoint,
@@ -322,6 +338,7 @@ def _get_substrate(
322338
use_remote_preset=True,
323339
chain_name="Bittensor",
324340
_mock=_mock,
341+
ws_shutdown_timer=ws_shutdown_timer,
325342
)
326343

327344
# Subtensor queries ===========================================================================================

bittensor/core/subtensor.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ class Subtensor(SubtensorMixin):
117117
def __init__(
118118
self,
119119
network: Optional[str] = None,
120-
config: Optional["Config"] = None,
120+
config: Optional[Config] = None,
121121
log_verbose: bool = False,
122122
fallback_endpoints: Optional[list[str]] = None,
123123
retry_forever: bool = False,
124124
_mock: bool = False,
125+
archive_endpoints: Optional[list[str]] = None,
125126
):
126127
"""
127128
Initializes an instance of the Subtensor class.
@@ -134,6 +135,9 @@ def __init__(
134135
Defaults to `None`.
135136
retry_forever: Whether to retry forever on connection errors. Defaults to `False`.
136137
_mock: Whether this is a mock instance. Mainly just for use in testing.
138+
archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases
139+
where you are requesting a block that is too old for your current (presumably lite) node. Defaults to
140+
`None`
137141
138142
Raises:
139143
Any exceptions raised during the setup, configuration, or connection process.
@@ -154,6 +158,7 @@ def __init__(
154158
fallback_endpoints=fallback_endpoints,
155159
retry_forever=retry_forever,
156160
_mock=_mock,
161+
archive_endpoints=archive_endpoints,
157162
)
158163
if self.log_verbose:
159164
logging.info(
@@ -175,19 +180,23 @@ def _get_substrate(
175180
fallback_endpoints: Optional[list[str]] = None,
176181
retry_forever: bool = False,
177182
_mock: bool = False,
183+
archive_endpoints: Optional[list[str]] = None,
178184
) -> Union[SubstrateInterface, RetrySyncSubstrate]:
179185
"""Creates the Substrate instance based on provided arguments.
180186
181187
Arguments:
182-
fallback_endpoints: List of fallback chains endpoints to use if main network isn't available.
183-
Defaults to `None`.
188+
fallback_endpoints: List of fallback chains endpoints to use if main network isn't available. Defaults to
189+
`None`.
184190
retry_forever: Whether to retry forever on connection errors. Defaults to `False`.
185191
_mock: Whether this is a mock instance. Mainly just for use in testing.
192+
archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases
193+
where you are requesting a block that is too old for your current (presumably lite) node. Defaults to
194+
`None`
186195
187196
Returns:
188197
the instance of the SubstrateInterface or RetrySyncSubstrate class.
189198
"""
190-
if fallback_endpoints or retry_forever:
199+
if fallback_endpoints or retry_forever or archive_endpoints:
191200
return RetrySyncSubstrate(
192201
url=self.chain_endpoint,
193202
ss58_format=SS58_FORMAT,
@@ -197,6 +206,7 @@ def _get_substrate(
197206
fallback_chains=fallback_endpoints,
198207
retry_forever=retry_forever,
199208
_mock=_mock,
209+
archive_nodes=archive_endpoints,
200210
)
201211
return SubstrateInterface(
202212
url=self.chain_endpoint,

bittensor/core/subtensor_api/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class SubtensorApi:
2929
retry_forever: Whether to retry forever on connection errors. Defaults to `False`.
3030
log_verbose: Enables or disables verbose logging.
3131
mock: Whether this is a mock instance. Mainly just for use in testing.
32+
archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases
33+
where you are requesting a block that is too old for your current (presumably lite) node. Defaults to `None`
34+
websocket_shutdown_timer: Amount of time, in seconds, to wait after the last response from the chain to close
35+
the connection. Only applicable to AsyncSubtensor.
3236
3337
Example:
3438
# sync version
@@ -75,10 +79,14 @@ def __init__(
7579
retry_forever: bool = False,
7680
log_verbose: bool = False,
7781
mock: bool = False,
82+
archive_endpoints: Optional[list[str]] = None,
83+
websocket_shutdown_timer: float = 5.0,
7884
):
7985
self.network = network
8086
self._fallback_endpoints = fallback_endpoints
87+
self._archive_endpoints = archive_endpoints
8188
self._retry_forever = retry_forever
89+
self._ws_shutdown_timer = websocket_shutdown_timer
8290
self._mock = mock
8391
self.log_verbose = log_verbose
8492
self.is_async = async_subtensor
@@ -119,6 +127,8 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]:
119127
fallback_endpoints=self._fallback_endpoints,
120128
retry_forever=self._retry_forever,
121129
_mock=self._mock,
130+
archive_endpoints=self._archive_endpoints,
131+
websocket_shutdown_timer=self._ws_shutdown_timer,
122132
)
123133
self.initialize = _subtensor.initialize
124134
return _subtensor
@@ -130,6 +140,7 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]:
130140
fallback_endpoints=self._fallback_endpoints,
131141
retry_forever=self._retry_forever,
132142
_mock=self._mock,
143+
archive_endpoints=self._archive_endpoints,
133144
)
134145

135146
def _determine_chain_endpoint(self) -> str:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ dependencies = [
3636
"uvicorn",
3737
"bittensor-drand>=0.5.0",
3838
"bittensor-wallet>=3.0.8",
39-
"async-substrate-interface>=1.2.0"
39+
"async-substrate-interface>=1.3.1"
4040
]
4141

4242
[project.optional-dependencies]

tests/helpers/helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import itertools
23
import json
34
import time
45
from collections import deque
@@ -204,6 +205,9 @@ def recv(self, *args, **kwargs):
204205
response = WEBSOCKET_RESPONSES[self.seed][item["method"]][
205206
json.dumps(item["params"])
206207
]
208+
if isinstance(response, itertools.cycle):
209+
# Allows us to cycle through different responses for the same method/params combo
210+
response = next(response)
207211
response["id"] = _id
208212
return json.dumps(response)
209213
except (KeyError, TypeError):

0 commit comments

Comments
 (0)