Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove use of memoize for average block time #37

Merged
merged 3 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions atxm/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,12 @@ def __init__(
self._task.clock = self.__CLOCK
self._task.interval = self._IDLE_INTERVAL

# busy interval
self._busy_interval = None

super().__init__()

self.add_observer(_Machine.LogObserver())
self.add_listener(_Machine.LogObserver())

@property
def _busy(self) -> bool:
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions atxm/utils.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
73 changes: 69 additions & 4 deletions tests/test_machine.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Loading