Skip to content

Drop single-use ttl_cache wrapper in utils/misc.py #95

@dev-miro26

Description

@dev-miro26

Problem

allways/utils/misc.py carries a generic @ttl_cache decorator plus a ttl_hash_gen generator — roughly 30 lines of cache machinery whose only purpose is to back one function:

@ttl_cache(maxsize=1, ttl=12)
def ttl_get_block(self) -> int:
    return self.subtensor.get_current_block()

Two things make this abstraction awkward:

  1. Single caller. ttl_get_block is referenced exactly once in the codebase — neurons/base/neuron.py:40, inside BaseNeuron.block. Grep for ttl_get_block | ttl_cache | ttl_hash_gen returns hits in utils/misc.py itself and that one import line.
  2. Free-standing self. ttl_get_block(self) takes self as an ordinary positional arg because @ttl_cache can't cleanly wrap bound methods — the decorator caches on the argument tuple, and passing a method through the decorator would need an update_wrapper dance that isn't there. So the function exists in utils/misc.py as a non-method that happens to accept a BaseNeuron instance, then is called from a class property. That's the abstraction leaking.

Impact

  • Readability. A reader tracing BaseNeuron.block has to cross a module boundary into utils/misc.py, follow the decorator through ttl_cachettl_hash_genlru_cache, and work out that the whole stack collapses to "cache the result for 12 seconds." That's five levels of indirection for a one-line cache.
  • Dead-flavored code. ttl_cache and ttl_hash_gen are exported as if they're general-purpose utilities, but nothing else uses them. Anyone reusing them would be building on a pattern the codebase has otherwise avoided. Fits the tone of refactor: remove dead read_result_option_swap and read_result_u128 #49's "remove dead abstractions that pretend to be reusable."
  • Module sprawl. utils/misc.py is the classic grab-bag — once the one useful thing is removed, there's no reason to keep the file.

Proposed fix

Inline an instance-level cache into BaseNeuron:

_BLOCK_CACHE_TTL_SECONDS = 12


class BaseNeuron(ABC):
    ...
    @property
    def block(self):
        """Current chain head, cached for one block (~12 s) to amortize the RPC round-trip."""
        now = time.monotonic()
        if now - self._cached_block_at >= _BLOCK_CACHE_TTL_SECONDS:
            self._cached_block = self.subtensor.get_current_block()
            self._cached_block_at = now
        return self._cached_block

    def __init__(self, config=None):
        ...
        self._cached_block: int = 0
        self._cached_block_at: float = 0.0

Then delete allways/utils/misc.py entirely — no callers remain.

time.monotonic() replaces the time.time() used inside ttl_hash_gen. This is strictly more robust (immune to wall-clock jumps, NTP slew, and leap seconds) and has identical semantics for a 12-second TTL — nothing in the codebase cares about wall vs. monotonic time at this granularity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions