|
3 | 3 | from dataclasses import (
|
4 | 4 | dataclass,
|
5 | 5 | )
|
| 6 | +import time |
6 | 7 | from typing import (
|
7 | 8 | TYPE_CHECKING,
|
8 | 9 | Any,
|
|
15 | 16 | )
|
16 | 17 |
|
17 | 18 | from eth_typing import (
|
| 19 | + Address, |
18 | 20 | ChecksumAddress,
|
19 | 21 | HexStr,
|
20 | 22 | )
|
|
38 | 40 | from web3.middleware import (
|
39 | 41 | ExtraDataToPOAMiddleware,
|
40 | 42 | )
|
| 43 | +from web3.providers.persistent.request_processor import ( |
| 44 | + TaskReliantQueue, |
| 45 | +) |
41 | 46 | from web3.types import (
|
42 | 47 | BlockData,
|
43 | 48 | FormattedEthSubscriptionResponse,
|
@@ -914,3 +919,60 @@ async def test_run_forever_starts_with_0_subs_and_runs_until_task_cancelled(
|
914 | 919 |
|
915 | 920 | # cleanup
|
916 | 921 | await clean_up_task(run_forever_task)
|
| 922 | + |
| 923 | + @pytest.mark.asyncio |
| 924 | + async def test_high_throughput_subscription_task_based( |
| 925 | + self, async_w3: AsyncWeb3 |
| 926 | + ) -> None: |
| 927 | + async_w3.provider._request_processor._handler_subscription_queue = ( |
| 928 | + TaskReliantQueue(maxsize=5_000) |
| 929 | + ) |
| 930 | + sub_manager = async_w3.subscription_manager |
| 931 | + sub_manager.task_based = True # turn on task-based processing |
| 932 | + |
| 933 | + class Counter: |
| 934 | + val: int = 0 |
| 935 | + |
| 936 | + counter = Counter() |
| 937 | + |
| 938 | + async def high_throughput_handler( |
| 939 | + handler_context: LogsSubscriptionContext, |
| 940 | + ) -> None: |
| 941 | + handler_context.counter.val += 1 |
| 942 | + if handler_context.counter.val == 5_000: |
| 943 | + await handler_context.subscription.unsubscribe() |
| 944 | + # if we awaited all 5_000 messages, we would sleep at least 5 seconds |
| 945 | + await asyncio.sleep(5 // 5_000) |
| 946 | + |
| 947 | + high_throughput_subscription = LogsSubscription( |
| 948 | + # build a meaningless subscription since we are fabricating the messages |
| 949 | + Address(b"\x00" * 20), |
| 950 | + handler=high_throughput_handler, |
| 951 | + handler_context={"counter": counter}, |
| 952 | + ) |
| 953 | + sub_id = await sub_manager.subscribe(high_throughput_subscription) |
| 954 | + async_w3.provider._request_processor.cache_request_information( |
| 955 | + request_id=sub_id, |
| 956 | + method=RPCEndpoint("eth_subscribe"), |
| 957 | + params=[], |
| 958 | + response_formatters=((), (), ()), # type: ignore |
| 959 | + ) |
| 960 | + |
| 961 | + # put 5_000 messages in the queue |
| 962 | + for _ in range(5_000): |
| 963 | + async_w3.provider._request_processor._handler_subscription_queue.put_nowait( |
| 964 | + { |
| 965 | + "jsonrpc": "2.0", |
| 966 | + "method": "eth_subscription", |
| 967 | + "params": {"subscription": sub_id, "result": {"foo": "bar"}}, # type: ignore # noqa: E501 |
| 968 | + } |
| 969 | + ) |
| 970 | + |
| 971 | + start = time.time() |
| 972 | + await sub_manager.handle_subscriptions() |
| 973 | + stop = time.time() |
| 974 | + |
| 975 | + assert counter.val == 5_000 |
| 976 | + |
| 977 | + assert sub_manager.total_handler_calls == 5_000 |
| 978 | + assert stop - start < 3, "subscription handling took too long!" |
0 commit comments