From 09ca18a3aa9f795bf2a520825093e04a85dd6baf Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 26 Jul 2024 16:34:58 -0400 Subject: [PATCH 1/3] Update use of deprecated function. --- atxm/machine.py | 2 +- tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/atxm/machine.py b/atxm/machine.py index 6ed1992..38213f7 100644 --- a/atxm/machine.py +++ b/atxm/machine.py @@ -132,7 +132,7 @@ def __init__( super().__init__() - self.add_observer(_Machine.LogObserver()) + self.add_listener(_Machine.LogObserver()) @property def _busy(self) -> bool: diff --git a/tests/conftest.py b/tests/conftest.py index 0ec0cfd..341f2af 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -126,7 +126,7 @@ def on_transition(self, source, target): @pytest.fixture def state_observer(machine): _observer = StateObserver() - machine.add_observer(_observer) + machine.add_listener(_observer) return _observer From 46e4af3b49255b50ec38ce3861f6be202a6b3520 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 26 Jul 2024 16:54:35 -0400 Subject: [PATCH 2/3] Stop using memoize for average block time; atxm instance will now keep a cached value of the determined busy interval that uses average block time. --- atxm/machine.py | 18 ++++++++++++------ atxm/utils.py | 2 -- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/atxm/machine.py b/atxm/machine.py index 38213f7..2a36701 100644 --- a/atxm/machine.py +++ b/atxm/machine.py @@ -130,6 +130,9 @@ def __init__( self._task.clock = self.__CLOCK self._task.interval = self._IDLE_INTERVAL + # busy interval + self._busy_interval = None + super().__init__() self.add_listener(_Machine.LogObserver()) @@ -173,12 +176,15 @@ def _enter_idle_mode(self): @_transition_to_busy.before def _enter_busy_mode(self): """About to enter busy work mode (speed up interval)""" - average_block_time = _get_average_blocktime( - w3=self.w3, sample_size=self._BLOCK_SAMPLE_SIZE - ) - self._task.interval = max( - round(average_block_time * self._BLOCK_INTERVAL), self._MIN_INTERVAL - ) + if self._busy_interval is None: + average_block_time = _get_average_blocktime( + w3=self.w3, sample_size=self._BLOCK_SAMPLE_SIZE + ) + self._busy_interval = max( + round(average_block_time * self._BLOCK_INTERVAL), self._MIN_INTERVAL + ) + + self._task.interval = self._busy_interval self.log.info(f"[working] cycle interval is now {self._task.interval} seconds") @_BUSY.enter diff --git a/atxm/utils.py b/atxm/utils.py index df87a70..e71a997 100644 --- a/atxm/utils.py +++ b/atxm/utils.py @@ -1,7 +1,6 @@ import contextlib from typing import Callable, Optional -from cytoolz import memoize from eth_utils import ValidationError from twisted.internet import reactor from web3 import Web3 @@ -18,7 +17,6 @@ from atxm.tx import AsyncTx, PendingTx, TxHash -@memoize def _get_average_blocktime(w3: Web3, sample_size: int) -> float: """Returns the average block time in seconds.""" latest_block = w3.eth.get_block("latest") From b37a5fe09149877c927c52a6d558a07d2047ba7f Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 29 Jul 2024 12:28:59 -0400 Subject: [PATCH 3/3] Add test for busy interval caching. --- tests/test_machine.py | 73 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/tests/test_machine.py b/tests/test_machine.py index 2e9e7fd..59b2f8f 100644 --- a/tests/test_machine.py +++ b/tests/test_machine.py @@ -1,11 +1,9 @@ -import time -from unittest.mock import ANY - import math +import time from typing import List +from unittest.mock import ANY import pytest - import pytest_twisted from eth_account import Account from eth_utils import ValidationError @@ -18,6 +16,7 @@ Web3Exception, ) +import atxm from atxm import AutomaticTxMachine from atxm.strategies import AsyncTxStrategy, TimeoutStrategy from atxm.tx import FaultedTx, FinalizedTx, FutureTx, PendingTx @@ -1538,6 +1537,72 @@ def test_pause_when_busy(clock, machine, eip1559_transaction, account, mocker): machine.stop() +@pytest.mark.usefixtures("disable_auto_mining") +def test_busy_interval_caching( + ethereum_tester, + machine, + eip1559_transaction, + account, + mock_wake_sleep, + mocker, +): + average_blocktime_spy = mocker.spy(atxm.machine, "_get_average_blocktime") + + assert machine.current_state == machine._IDLE + assert not machine.paused + assert not machine.busy + assert machine._busy_interval is None + assert average_blocktime_spy.call_count == 0 + + atx = machine.queue_transaction( + params=eip1559_transaction, + signer=account, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), + on_finalized=mocker.Mock(), + on_insufficient_funds=mocker.Mock(), + ) + + # broadcast tx + machine._cycle() + assert machine.current_state == machine._BUSY + assert machine.busy + assert machine._busy_interval is not None + busy_interval_value = machine._busy_interval + assert machine._task.interval == busy_interval_value + assert average_blocktime_spy.call_count == 1 + + ethereum_tester.mine_block() + + # busy -> pause + machine.pause() + assert machine.current_state == machine._PAUSED + assert machine.paused + + # resume after pausing + machine.resume() + machine._cycle() # wake doesn't do anything because mocked + assert machine.current_state == machine._BUSY + assert machine._busy_interval is not None + assert machine._busy_interval == busy_interval_value + assert machine._task.interval == busy_interval_value + assert average_blocktime_spy.call_count == 1 + assert not machine.paused + + # finalize tx + while machine.busy: + ethereum_tester.mine_block() + machine._cycle() + + ethereum_tester.mine_block() + assert atx.final is True + + # transition to idle + machine._cycle() + assert machine.current_state == machine._IDLE + assert machine._task.interval != busy_interval_value + + @pytest.mark.usefixtures("disable_auto_mining") def test_simple_state_transitions( ethereum_tester,