Custom storage backends

The Freiner package ships with a few built-in storage implementations which allow you to get started with some common data stores (redis & memcached) used for rate limiting.

Creating your own storage backend is relatively straightforward. Each type of strategy (fixed-window and moving-window) has its own storage requirements, and a storage backend can easily support both at the same time.

Fixed Window Storage

You need to fulfil the contract set out by freiner.storage.FixedWindowStorage. This is a typing.Protocol class, so you don’t need to extend it.

The following example shows a fixed-window storage backend.:

class MyFixedWindowStorage:
    def incr(self, key: str, expiry: int, elastic_expiry: bool = False) -> int:
        return 1

    def get(self, key: str) -> int:
        return 0

    def get_expiry(self, key: str) -> int:
        return -1

    def clear(self, key: str):
        pass

Moving Window Storage

You need to fulfil the contract set out by freiner.storage.MovingWindowStorage. This is a typing.Protocol class, so you don’t need to extend it.

The following example shows a moving-window storage backend.:

class MyMovingWindowStorage:
    def acquire_entry(self, key: str, limit: int, expiry: int, no_add: bool = False) -> bool:
        return True

    def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[float, int]:
        return time.time(), 0

    def clear(self, key: str):
        pass