Custom storage backends#

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

To accommodate customizations to either the default storage backends or different storage backends altogether, limits uses a registry pattern that makes it painless to add your own custom storage (without having to submit patches to the package itself).

Creating a custom backend requires:

  1. Subclassing limits.storage.Storage or limits.aio.storage.Storage

  2. Providing implementations for the abstractmethods of Storage

  3. If the storage can support the Moving Window strategy - additionally implementing the methods from MovingWindowSupport

  4. Providing naming schemes that can be used to lookup the custom storage in the storage registry. (Refer to Storage scheme for more details)

Example#

The following example shows two backend stores: one which doesn’t implement the Moving Window strategy and one that does. Do note the STORAGE_SCHEME class variables which result in the classes getting registered with the limits storage registry:

import urlparse
from limits.storage import MovingWindowSupport
from limits.storage import Storage
import time

class AwesomeStorage(Storage):
    STORAGE_SCHEME = ["awesomedb"]
    def __init__(self, uri, **options):
        self.awesomesness = options.get("awesomeness", None)
        self.host = urlparse.urlparse(uri).netloc
        self.port = urlparse.urlparse(uri).port

    def check(self) -> bool:
        return True

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

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

    def get(self, key):
        return 0


class AwesomerStorage(Storage, MovingWindowSupport):
    STORAGE_SCHEME = ["awesomerdb"]
    def __init__(self, uri, **options):
        self.awesomesness = options.get("awesomeness", None)
        self.host = urlparse.urlparse(uri).netloc
        self.port = urlparse.urlparse(uri).port

    def check(self):
        return True

    def get_expiry(self, key):
        return int(time.time())

    def incr(self, key, expiry, elastic_expiry=False):
        return

    def get(self, key):
        return 0

    def acquire_entry(self, key, limit, expiry):
        return True

    def get_moving_window(
        self, key, limit, expiry
    ):
        return [0, 10]

Once the above implementations are declared you can look them up using the Storage Factory function in the following manner:

from limits.storage import storage_from_string

awesome = storage_from_string("awesomedb://localhoax:42", awesomeness=0)
awesomer = storage_from_string("awesomerdb://localhoax:42", awesomeness=1)