|
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,
|
|
38 | 39 | from web3.middleware import (
|
39 | 40 | ExtraDataToPOAMiddleware,
|
40 | 41 | )
|
| 42 | +from web3.providers.persistent.request_processor import ( |
| 43 | + TaskReliantQueue, |
| 44 | +) |
41 | 45 | from web3.types import (
|
42 | 46 | BlockData,
|
43 | 47 | FormattedEthSubscriptionResponse,
|
@@ -914,3 +918,62 @@ async def test_run_forever_starts_with_0_subs_and_runs_until_task_cancelled(
|
914 | 918 |
|
915 | 919 | # cleanup
|
916 | 920 | await clean_up_task(run_forever_task)
|
| 921 | + |
| 922 | + @pytest.mark.asyncio |
| 923 | + async def test_high_throughput_subscription_task_based( |
| 924 | + self, async_w3: AsyncWeb3 |
| 925 | + ) -> None: |
| 926 | + async_w3.provider._request_processor._handler_subscription_queue = ( |
| 927 | + TaskReliantQueue(maxsize=5_000) |
| 928 | + ) |
| 929 | + sub_manager = async_w3.subscription_manager |
| 930 | + sub_manager.task_based = True |
| 931 | + |
| 932 | + async def high_throughput_handler( |
| 933 | + handler_context: LogsSubscriptionContext, |
| 934 | + ) -> None: |
| 935 | + handler_context.counter.val += 1 |
| 936 | + if ( |
| 937 | + handler_context.async_w3.subscription_manager.total_handler_calls |
| 938 | + == 5_000 |
| 939 | + ): |
| 940 | + await handler_context.subscription.unsubscribe() |
| 941 | + # 3 seconds / 5_000 = 0.0006 seconds. If we awaited, we would expect this to |
| 942 | + # take at least 3 seconds to process all 5_000 messages. |
| 943 | + await asyncio.sleep(0.0006) |
| 944 | + |
| 945 | + class Counter: |
| 946 | + val: int = 0 |
| 947 | + |
| 948 | + counter = Counter() |
| 949 | + high_throughput_subscription = LogsSubscription( |
| 950 | + "0xdead00000000000000000000000000000000beef", # type: ignore |
| 951 | + handler=high_throughput_handler, |
| 952 | + handler_context={"counter": counter}, |
| 953 | + ) |
| 954 | + sub_id = await sub_manager.subscribe(high_throughput_subscription) |
| 955 | + async_w3.provider._request_processor.cache_request_information( |
| 956 | + request_id=sub_id, |
| 957 | + method=RPCEndpoint("eth_subscribe"), |
| 958 | + params=["testestest"], |
| 959 | + response_formatters=((), (), ()), # type: ignore |
| 960 | + ) |
| 961 | + |
| 962 | + # put 5_000 messages in the queue |
| 963 | + for _ in range(5_000): |
| 964 | + async_w3.provider._request_processor._handler_subscription_queue.put_nowait( |
| 965 | + { |
| 966 | + "jsonrpc": "2.0", |
| 967 | + "method": "eth_subscription", |
| 968 | + "params": {"subscription": sub_id, "result": {"foo": "bar"}}, # type: ignore # noqa: E501 |
| 969 | + } |
| 970 | + ) |
| 971 | + |
| 972 | + start = time.time() |
| 973 | + await sub_manager.handle_subscriptions() |
| 974 | + stop = time.time() |
| 975 | + |
| 976 | + assert counter.val == 5_000 |
| 977 | + |
| 978 | + assert sub_manager.total_handler_calls == 5_000 |
| 979 | + assert stop - start < 3, "subscription handling took too long!" |
0 commit comments