Skip to content

fix: avoid binding loop affinity on invalid cache key inputs #739

@BobTheBuidler

Description

@BobTheBuidler

Summary:
When a call fails before a cache key can be created (e.g., unhashable args), the cache still records __first_loop. A later valid call from another event loop then raises the cross-loop RuntimeError even though no cache entry was created. This makes “retry with corrected input on a different loop” fail unexpectedly.

Steps to Reproduce:

  1. Define a cached function.
  2. Call it with an unhashable argument on Loop A (raises TypeError).
  3. Call it with a valid argument on Loop B.
import asyncio
from async_lru import alru_cache  # or faster_async_lru on the fork

@alru_cache(maxsize=100)
async def cached_func(key):
    return f"data_{key}"

loop1 = asyncio.new_event_loop()
try:
    loop1.run_until_complete(cached_func([]))  # unhashable -> TypeError
finally:
    loop1.close()

loop2 = asyncio.new_event_loop()
try:
    loop2.run_until_complete(cached_func("ok"))  # should succeed
finally:
    loop2.close()

Expected:
The second call succeeds because the first call failed before any cache entry was created.

Actual:
The second call raises:
RuntimeError: alru_cache is not safe to use across event loops...

Root cause:
_check_loop() runs before _make_key(...), so failures in key construction still bind __first_loop.

Proposed fix:
Move _check_loop(loop) to after _make_key(...) in __call__.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions