-
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add documentation about custom storage implementation
- Loading branch information
Showing
3 changed files
with
136 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
.. currentmodule:: limits | ||
|
||
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 relatively painless to add a new storage (without having to submit patches | ||
to the package itself). | ||
|
||
Creating a custom backend requires: | ||
|
||
#. Subclassing :class:`limits.storage.Storage` | ||
#. Providing implementations for the abstractmethods of :class:`limits.storage.Storage` | ||
#. If the storage can support the :ref:`moving-window` strategy - additionally implementing | ||
the `acquire_entry` instance method. | ||
#. Providing a naming *scheme* that can be used to lookup the custom storage in the storage registry. | ||
(Refer to :ref:`storage-scheme` for more details) | ||
|
||
Example | ||
======= | ||
The following example shows two backend stores: one which doesn't implement the | ||
:ref:`moving-window` strategy and one that does. Do note the :code:`STORAGE_SCHEME` class | ||
variables which result in the classes getting registered with the **limits** storage registry:: | ||
|
||
|
||
import urlparse | ||
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): | ||
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 | ||
|
||
|
||
class AwesomerStorage(Storage): | ||
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, no_add=False | ||
): | ||
return True | ||
|
||
|
||
Once the above implementations are declared you can look them up | ||
using the factory method described in :ref:`storage-scheme` 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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
.. currentmodule:: limits | ||
.. _storage-scheme: | ||
|
||
Storage url scheme | ||
------------------ | ||
|
||
**limits** uses a url style storage scheme notation (similar to the JDBC driver | ||
connection string notation) for configuring and initializing | ||
storage backends. This notation additionally provides a simple | ||
mechanism to both identify and configure the backend implementation based on | ||
a single string argument. | ||
|
||
The storage scheme follows the format :code:`{scheme}://{parameters}` | ||
|
||
:func:`limits.storage.storage_from_string` is provided to | ||
lookup and construct an instance of a storage based on the storage scheme. For example:: | ||
|
||
import limits.storage | ||
uri = "redis://localhost:9999" | ||
options = {} | ||
redis_storage = limits.storage.storage_from_string(uri, **options) | ||
|
||
|
||
Examples | ||
======== | ||
|
||
In-Memory | ||
The in-memory storage takes no parameters so the only relevant value is :code:`memory://` | ||
|
||
Memcached | ||
Requires the location of the memcached server(s). As such | ||
the parameters is a comma separated list of :code:`{host}:{port}` locations such as | ||
:code:`memcached://localhost:11211` or | ||
:code:`memcached://localhost:11211,localhost:11212,192.168.1.1:11211` etc... | ||
|
||
Redis | ||
Requires the location of the redis server and optionally the database number. | ||
:code:`redis://localhost:6379` or :code:`redis://localhost:6379/1` (for database `1`) | ||
|
||
Redis with Sentinel | ||
Requires the location(s) of the redis sentinal instances and the `service-name` | ||
that is monitored by the sentinels. | ||
:code:`redis+sentinel://localhost:26379/my-redis-service` | ||
or :code:`redis+sentinel://localhost:26379,localhost:26380/my-redis-service` |