diff --git a/ape_alchemy/provider.py b/ape_alchemy/provider.py index 5d5ed2c..a856d39 100644 --- a/ape_alchemy/provider.py +++ b/ape_alchemy/provider.py @@ -1,4 +1,5 @@ -import os +import os, time +import numpy.random as random from typing import Any, Dict, Iterator from ape.api import UpstreamProvider, Web3Provider @@ -26,6 +27,12 @@ class Alchemy(Web3Provider, UpstreamProvider): network_uris: Dict[tuple, str] = {} + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.block_page_size = 5000 + self.concurrency = 1 + print(f"setting block_page_size to {self.block_page_size} and concurrency to {self.concurrency}") + @property def uri(self): """ @@ -129,22 +136,37 @@ def get_virtual_machine_error(self, exception: Exception) -> VirtualMachineError return VirtualMachineError(message=message) def _make_request(self, endpoint: str, parameters: list) -> Any: - try: - return super()._make_request(endpoint, parameters) - except HTTPError as err: - response_data = err.response.json() - if "error" not in response_data: - raise AlchemyProviderError(str(err)) from err - - error_data = response_data["error"] - message = ( - error_data.get("message", str(error_data)) - if isinstance(error_data, dict) - else error_data - ) - cls = ( - AlchemyFeatureNotAvailable - if "is not available" in message - else AlchemyProviderError - ) - raise cls(message) from err + MIN_RETRY_DELAY = 1000 + RETRY_BACKOFF_FACTOR = 2 + MAX_RETRY_DELAY = 30000 + MAX_RETRIES = 3 + # RETRY_INTERVAL = 1000 + RETRY_JITTER = 250 + for i in range(0,MAX_RETRIES): + print(f"_make_request attempt {i}") + try: + return super()._make_request(endpoint, parameters) + except HTTPError as err: + response_data = err.response.json() + if "error" not in response_data: + raise AlchemyProviderError(str(err)) from err + + error_data = response_data["error"] + message = ( + error_data.get("message", str(error_data)) + if isinstance(error_data, dict) + else error_data + ) + if message.__contains__("exceeded its compute units"): + RETRY_INTERVAL = min(MAX_RETRY_DELAY, MIN_RETRY_DELAY * RETRY_BACKOFF_FACTOR ** i) + print(f"Alchemy compute units exceeded, retrying #{i} in {RETRY_INTERVAL} ms") + delay = RETRY_INTERVAL + random.randint(0, RETRY_JITTER) + time.sleep(delay / 1000) + continue + cls = ( + AlchemyFeatureNotAvailable + if "is not available" in message + else AlchemyProviderError + ) + raise cls(message) from err + raise AlchemyProviderError(f"Rate limited for ${MAX_RETRIES} consecutive attempts.")