Skip to content

Enhancement: Atomic operations for Litestar Stores #4668

@sparfenyuk

Description

@sparfenyuk

Summary

Currently, the Litestar Store protocol (and its implementations like RedisStore, MemoryStore, etc.) provides basic key-value operations like get, set, and delete. However, it lacks support for atomic operations such as incrementing/decrementing a numeric value (incr/decr) or setting a value only if it does not already exist (setnx).

When implementing features like rate limiting, brute-force protection, or circuit breakers, developers are forced to use a non-atomic get followed by a set. In highly concurrent async environments, this read-modify-write cycle leads to race conditions where simultaneous requests read the same initial state and overwrite each other, effectively bypassing the intended limits.

We need atomic primitives added to the Store protocol to support thread-safe and concurrency-safe state tracking.

Basic Example

To count failed attempts (e.g., OTP verification failures), developers currently have to do this:

async def record_failure(store: Store, key: str) -> int:
     # ❌ RACE CONDITION: Multiple concurrent requests can read the same value
     # before any of them update it.
     raw = await store.get(key)
     count = int(raw.decode()) + 1 if raw else 1

     await store.set(key, str(count).encode(), expires_in=900)
     return count

Drawbacks and Impact

Drawbacks:

  1. Implementation Complexity across Backends: While implementing incr or setnx is trivial in Redis (INCR, SETNX), it might be more complex to implement atomically in other storage backends (like a database-backed store or a filesystem store) without introducing internal locking mechanisms.
  2. Protocol Expansion: It expands the footprint of the Store protocol. Every existing and future custom Store implementation will be required to implement these new atomic methods to satisfy the interface.

Impact:

  • Security & Correctness: Eliminates critical race conditions in rate limiters, brute-force trackers, and circuit breakers, which are common patterns in modern web APIs.
  • Performance: Atomic operations in systems like Redis are significantly faster than a full roundtrip of get -> compute in Python -> set.
  • Developer Experience: Prevents developers from having to bypass Litestar's Store abstractions to use underlying driver features (e.g., importing redis-py directly) or write complex Lua scripts just to safely increment a counter.

Unresolved questions

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    EnhancementThis is a new feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions