Skip to content
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ Note that 0.3.3 was never released; 0.3.4 follows 0.3.2.
`delete()` and the other methods let the error through.
([#177](https://github.com/allen0099/FastAPI-CacheX/issues/177))

- **`MemoryBackend` rejects a `cleanup_interval` that is not positive.** With
`0` or a negative value, `asyncio.sleep()` returned at once and the cleanup
loop spun, using a full CPU core and taking the cache lock on every pass. It
now raises `ValueError`.
([#180](https://github.com/allen0099/FastAPI-CacheX/issues/180))

## [0.3.7] - 2026-09-25

### Added
Expand Down
3 changes: 2 additions & 1 deletion docs/BACKENDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ All backends namespace their keys with a prefix (`fastapi_cachex:` by default,

If you don't specify a backend, FastAPI-CacheX will use the in-memory cache by default.
This is suitable for development and testing purposes. The backend automatically runs
a cleanup task to remove expired entries every 60 seconds (`MemoryBackend(cleanup_interval=60)`).
a cleanup task to remove expired entries every 60 seconds (`MemoryBackend(cleanup_interval=60)`;
the interval must be positive).

```python
from fastapi_cachex.backends import MemoryBackend
Expand Down
8 changes: 8 additions & 0 deletions fastapi_cachex/backends/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ def __init__(self, cleanup_interval: int = 60) -> None:

Args:
cleanup_interval: Interval in seconds between cleanup runs (default: 60)

Raises:
ValueError: If ``cleanup_interval`` is not positive
"""
if cleanup_interval <= 0:
# asyncio.sleep() returns at once for these, so the cleanup loop
# would spin, taking the cache lock on every pass.
msg = f"cleanup_interval must be positive, got {cleanup_interval!r}"
raise ValueError(msg)
self.cache: dict[str, CacheItem] = {}
self.lock = asyncio.Lock()
self.cleanup_interval = cleanup_interval
Expand Down
2 changes: 1 addition & 1 deletion i18n/zh-TW/docs/BACKENDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

## 記憶體(預設) {#in-memory-default}

若未指定後端,FastAPI-CacheX 預設會使用記憶體快取。這適合開發與測試用途。此後端會自動執行清理工作,每 60 秒移除一次已過期的項目(`MemoryBackend(cleanup_interval=60)`)。
若未指定後端,FastAPI-CacheX 預設會使用記憶體快取。這適合開發與測試用途。此後端會自動執行清理工作,每 60 秒移除一次已過期的項目(`MemoryBackend(cleanup_interval=60)`;間隔必須大於 0)。

```python
from fastapi_cachex.backends import MemoryBackend
Expand Down
7 changes: 7 additions & 0 deletions tests/backends/test_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ def memory_backend():
return MemoryBackend()


@pytest.mark.parametrize("interval", [0, -5])
def test_cleanup_interval_must_be_positive(interval: int) -> None:
"""A non-positive interval made the cleanup loop spin (#180)."""
with pytest.raises(ValueError, match="cleanup_interval must be positive"):
MemoryBackend(cleanup_interval=interval)


@pytest.mark.asyncio
async def test_memory_backend_set_get(memory_backend: MemoryBackend):
key = "test_key"
Expand Down
Loading