Skip to content

Commit

Permalink
Merge pull request #37 from derekpierre/memoize-fix
Browse files Browse the repository at this point in the history
Remove use of memoize for average block time
  • Loading branch information
KPrasch authored Jul 30, 2024
2 parents 663e47f + b37a5fe commit 8e951c4
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 14 deletions.
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

0 comments on commit 8e951c4

Please sign in to comment.