ethereum / web3.py

A python interface for interacting with the Ethereum blockchain and ecosystem.
http://web3py.readthedocs.io
MIT License
4.97k stars 1.69k forks source link

Weird Web3 object behaviour when using timeout in request_kwargs #3406

Closed choesy closed 4 months ago

choesy commented 4 months ago

What happened?

When i am using AsyncWeb3 with AsyncHttpProvider, i set request_kwargs={"timeout":2} like in the docs. It does not always raise a timeout error as it should. Sometimes it raises error after 9-10 seconds, and sometimes it doesn't even raise the error and i get back results despite almost 9 seconds passing by. That is more that 4 times the amount of set timeout.

Code that produced the error

#Timing the request execution

from web3 import AsyncWeb3
import asyncio
from time import time
w3=AsyncWeb3(AsyncWeb3.AsyncHTTPProvider('https://rpc.ankr.com/arbitrum',request_kwargs={'timeout':2}))

async def get_logs():
    start=time()
    logs = await w3.eth.get_logs({
        'fromBlock': hex(20000000),
        'toBlock': hex(20000000+2000),
        'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
    })
    print("n_logs:",len(logs))
    print("time taken:",time()-start)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(get_logs())
    loop.close()

Full error output

coco@coco-HP:~/Desktop/test_slow_server$ python test.py 
n_logs: 4592
time taken: 8.84072232246399

Fill this section in if you know how this could or should be fixed

No response

web3 Version

6.19.0

Python Version

3.11.9

Operating System

linux

Output from pip freeze

aiohttp==3.9.5
aiosignal==1.3.1
attrs==23.2.0
bitarray==2.9.2
blinker==1.8.2
certifi==2024.2.2
charset-normalizer==3.3.2
ckzg==1.0.2
click==8.1.7
cytoolz==0.12.3
eth-abi==5.1.0
eth-account==0.11.2
eth-hash==0.7.0
eth-keyfile==0.8.1
eth-keys==0.5.1
eth-rlp==1.0.1
eth-typing==4.2.3
eth-utils==4.1.1
flask==3.0.3
frozenlist==1.4.1
hexbytes==0.3.1
idna==3.7
itsdangerous==2.2.0
jinja2==3.1.4
jsonschema==4.22.0
jsonschema-specifications==2023.12.1
lru-dict==1.2.0
markupsafe==2.1.5
multidict==6.0.5
parsimonious==0.10.0
protobuf==5.26.1
pycryptodome==3.20.0
pyunormalize==15.1.0
referencing==0.35.1
regex==2024.5.15
requests==2.32.1
rlp==4.0.1
rpds-py==0.18.1
toolz==0.12.1
typing-extensions==4.11.0
urllib3==2.2.1
web3==6.19.0
websockets==12.0
werkzeug==3.0.3
yarl==1.9.4
jimtje commented 4 months ago

aiohttp's client timeout settings are actually set via passing values into a separate object, with a slightly more complex data structure and so passing just an int to it isn't likely to do anything. Also, the default timeout is 5 and I think the value are rounded because the timeout is set on the event loop level. The other caveat is that because the timeout is set on the event loop level for the client, it's going to be off by a little depending on your environment, and lower the setting the more off it may be unless it's None.

Try running this modified version of your code, although the timeout (default is 300, since it is a timeout for the entire event loop) is likely too short anyway.

rom web3 import AsyncWeb3
import asyncio
from aiohttp import ClientTimeout
from time import time

timeout = ClientTimeout(total=5)
w3=AsyncWeb3(AsyncWeb3.AsyncHTTPProvider('https://rpc.ankr.com/arbitrum',request_kwargs={'timeout':timeout}))

async def get_logs():
    start=time()
    logs = await w3.eth.get_logs({
        'fromBlock': hex(20000000),
        'toBlock': hex(20000000+2000),
        'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
    })
    print("n_logs:",len(logs))
    print("time taken:",time()-start)
choesy commented 4 months ago

Thank you for explaining it :)