Merge pull request #65 from flashbots/backoff-retry
Add middleware to retry with backoff
This commit is contained in:
commit
c51d907655
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
tests
|
||||||
|
cache
|
@ -18,3 +18,4 @@ repos:
|
|||||||
- id: 'mypy'
|
- id: 'mypy'
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- 'pydantic'
|
- 'pydantic'
|
||||||
|
- 'types-requests'
|
||||||
|
@ -18,4 +18,4 @@ COPY . /app
|
|||||||
# easter eggs 😝
|
# easter eggs 😝
|
||||||
RUN echo "PS1='🕵️:\[\033[1;36m\]\h \[\033[1;34m\]\W\[\033[0;35m\]\[\033[1;36m\]$ \[\033[0m\]'" >> ~/.bashrc
|
RUN echo "PS1='🕵️:\[\033[1;36m\]\h \[\033[1;34m\]\W\[\033[0;35m\]\[\033[1;36m\]$ \[\033[0m\]'" >> ~/.bashrc
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
CMD [ "python", "run.py"]
|
||||||
|
2
Tiltfile
2
Tiltfile
@ -11,7 +11,7 @@ k8s_yaml(secret_from_dict("mev-inspect-db-credentials", inputs = {
|
|||||||
"password": "password",
|
"password": "password",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
docker_build('mev-inspect', '.',
|
docker_build('mev-inspect-py', '.',
|
||||||
live_update=[
|
live_update=[
|
||||||
sync('.', '/app'),
|
sync('.', '/app'),
|
||||||
run('cd /app && poetry install',
|
run('cd /app && poetry install',
|
||||||
|
@ -16,9 +16,8 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: mev-inspect
|
- name: mev-inspect
|
||||||
image: mev-inspect:latest
|
image: mev-inspect-py
|
||||||
command: [ "/bin/bash", "-c", "--" ]
|
command: [ "python", "run.py"]
|
||||||
args: [ "while true; do sleep 30; done;" ]
|
|
||||||
env:
|
env:
|
||||||
- name: POSTGRES_USER
|
- name: POSTGRES_USER
|
||||||
valueFrom:
|
valueFrom:
|
||||||
@ -30,6 +29,8 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: mev-inspect-db-credentials
|
name: mev-inspect-db-credentials
|
||||||
key: password
|
key: password
|
||||||
|
- name: POSTGRES_HOST
|
||||||
|
value: postgresql
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
exec:
|
exec:
|
||||||
command:
|
command:
|
||||||
|
@ -7,9 +7,9 @@ from sqlalchemy.orm import sessionmaker
|
|||||||
def get_sqlalchemy_database_uri():
|
def get_sqlalchemy_database_uri():
|
||||||
username = os.getenv("POSTGRES_USER")
|
username = os.getenv("POSTGRES_USER")
|
||||||
password = os.getenv("POSTGRES_PASSWORD")
|
password = os.getenv("POSTGRES_PASSWORD")
|
||||||
server = "postgresql"
|
host = os.getenv("POSTGRES_HOST")
|
||||||
db_name = "mev_inspect"
|
db_name = "mev_inspect"
|
||||||
return f"postgresql://{username}:{password}@{server}/{db_name}"
|
return f"postgresql://{username}:{password}@{host}/{db_name}"
|
||||||
|
|
||||||
|
|
||||||
def get_engine():
|
def get_engine():
|
||||||
|
59
mev_inspect/retry.py
Normal file
59
mev_inspect/retry.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import time
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Collection,
|
||||||
|
Type,
|
||||||
|
)
|
||||||
|
|
||||||
|
from requests.exceptions import (
|
||||||
|
ConnectionError,
|
||||||
|
HTTPError,
|
||||||
|
Timeout,
|
||||||
|
TooManyRedirects,
|
||||||
|
)
|
||||||
|
from web3 import Web3
|
||||||
|
from web3.middleware.exception_retry_request import check_if_retry_on_failure
|
||||||
|
from web3.types import (
|
||||||
|
RPCEndpoint,
|
||||||
|
RPCResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exception_retry_with_backoff_middleware(
|
||||||
|
make_request: Callable[[RPCEndpoint, Any], RPCResponse],
|
||||||
|
web3: Web3, # pylint: disable=unused-argument
|
||||||
|
errors: Collection[Type[BaseException]],
|
||||||
|
retries: int = 5,
|
||||||
|
backoff_time_seconds: float = 0.1,
|
||||||
|
) -> Callable[[RPCEndpoint, Any], RPCResponse]:
|
||||||
|
"""
|
||||||
|
Creates middleware that retries failed HTTP requests. Is a default
|
||||||
|
middleware for HTTPProvider.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
|
||||||
|
if check_if_retry_on_failure(method):
|
||||||
|
for i in range(retries):
|
||||||
|
try:
|
||||||
|
return make_request(method, params)
|
||||||
|
# https://github.com/python/mypy/issues/5349
|
||||||
|
except errors: # type: ignore
|
||||||
|
if i < retries - 1:
|
||||||
|
time.sleep(backoff_time_seconds)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return make_request(method, params)
|
||||||
|
|
||||||
|
return middleware
|
||||||
|
|
||||||
|
|
||||||
|
def http_retry_with_backoff_request_middleware(
|
||||||
|
make_request: Callable[[RPCEndpoint, Any], Any], web3: Web3
|
||||||
|
) -> Callable[[RPCEndpoint, Any], Any]:
|
||||||
|
return exception_retry_with_backoff_middleware(
|
||||||
|
make_request, web3, (ConnectionError, HTTPError, Timeout, TooManyRedirects)
|
||||||
|
)
|
@ -32,7 +32,6 @@ build-backend = "poetry.core.masonry.api"
|
|||||||
lint = 'scripts.poetry.dev_tools:lint'
|
lint = 'scripts.poetry.dev_tools:lint'
|
||||||
test = 'scripts.poetry.dev_tools:test'
|
test = 'scripts.poetry.dev_tools:test'
|
||||||
isort = 'scripts.poetry.dev_tools:isort'
|
isort = 'scripts.poetry.dev_tools:isort'
|
||||||
mypy = 'scripts.poetry.dev_tools:mypy'
|
|
||||||
black = 'scripts.poetry.dev_tools:black'
|
black = 'scripts.poetry.dev_tools:black'
|
||||||
pre_commit = 'scripts.poetry.dev_tools:pre_commit'
|
pre_commit = 'scripts.poetry.dev_tools:pre_commit'
|
||||||
start = 'scripts.poetry.docker:start'
|
start = 'scripts.poetry.docker:start'
|
||||||
|
5
run.py
Normal file
5
run.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
while True:
|
||||||
|
time.sleep(30)
|
@ -22,6 +22,7 @@ from mev_inspect.crud.swaps import delete_swaps_for_block, write_swaps
|
|||||||
from mev_inspect.db import get_session
|
from mev_inspect.db import get_session
|
||||||
from mev_inspect.miner_payments import get_miner_payments
|
from mev_inspect.miner_payments import get_miner_payments
|
||||||
from mev_inspect.swaps import get_swaps
|
from mev_inspect.swaps import get_swaps
|
||||||
|
from mev_inspect.retry import http_retry_with_backoff_request_middleware
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@ -34,7 +35,7 @@ def cli():
|
|||||||
@click.argument("rpc")
|
@click.argument("rpc")
|
||||||
@click.option("--cache/--no-cache", default=True)
|
@click.option("--cache/--no-cache", default=True)
|
||||||
def inspect_block(block_number: int, rpc: str, cache: bool):
|
def inspect_block(block_number: int, rpc: str, cache: bool):
|
||||||
base_provider = Web3.HTTPProvider(rpc)
|
base_provider = _get_base_provider(rpc)
|
||||||
w3 = Web3(base_provider)
|
w3 = Web3(base_provider)
|
||||||
|
|
||||||
if not cache:
|
if not cache:
|
||||||
@ -49,7 +50,7 @@ def inspect_block(block_number: int, rpc: str, cache: bool):
|
|||||||
@click.argument("rpc")
|
@click.argument("rpc")
|
||||||
@click.option("--cache/--no-cache", default=True)
|
@click.option("--cache/--no-cache", default=True)
|
||||||
def inspect_many_blocks(after_block: int, before_block: int, rpc: str, cache: bool):
|
def inspect_many_blocks(after_block: int, before_block: int, rpc: str, cache: bool):
|
||||||
base_provider = Web3.HTTPProvider(rpc)
|
base_provider = _get_base_provider(rpc)
|
||||||
w3 = Web3(base_provider)
|
w3 = Web3(base_provider)
|
||||||
|
|
||||||
if not cache:
|
if not cache:
|
||||||
@ -156,5 +157,16 @@ def get_stats(classified_traces) -> dict:
|
|||||||
return stats
|
return stats
|
||||||
|
|
||||||
|
|
||||||
|
def _get_base_provider(rpc: str) -> Web3.HTTPProvider:
|
||||||
|
base_provider = Web3.HTTPProvider(rpc)
|
||||||
|
base_provider.middlewares.remove("http_retry_request")
|
||||||
|
base_provider.middlewares.add(
|
||||||
|
http_retry_with_backoff_request_middleware,
|
||||||
|
"http_retry_with_backoff",
|
||||||
|
)
|
||||||
|
|
||||||
|
return base_provider
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cli()
|
cli()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user