Skip to main content

Lock context manager implemented via redis SETNX/BLPOP.

Project description

Documentation Status Travis-CI Build Status Coverage Status Code Quality Status Scrtinizer Status
PyPI Package latest release PyPI Package monthly downloads PyPI Wheel Supported versions Supported imlementations

Lock context manager implemented via redis SETNX/BLPOP.

  • Free software: BSD license

Interface targeted to be exactly like threading.Lock.

Usage

Because we don’t want to require users to share the lock instance across processes you will have to give them names. Eg:

conn = StrictRedis()
with redis_lock.Lock(conn, "name-of-the-lock"):
    print("Got the lock. Doing some work ...")
    time.sleep(5)

Eg:

lock = redis_lock.Lock(conn, "name-of-the-lock")
if lock.acquire(blocking=False):
    print("Got the lock.")
else:
    print("Someone else has the lock.")

You can also associate an identifier along with the lock so that it can be retrieved later by the same process, or by a different one. This is useful in cases where the application needs to identify the lock owner (find out who currently owns the lock). Eg:

import socket
host_id = "owned-by-%s" % socket.gethostname()
lock = redis_lock.Lock(conn, "name-of-the-lock", id=host_id)
if lock.acquire(blocking=False):
    print("Got the lock.")
else:
    if lock.get_owner_id() == host_id:
        print("I already acquired this in another process.")
    else:
        print("The lock is held on another machine.")

Avoid dogpile effect in django

The dogpile is also known as the thundering herd effect or cache stampede. Here’s a pattern to avoid the problem without serving stale data. The work will be performed a single time and every client will wait for the fresh data.

To use this you will need django-redis, however, python-redis-lock provides you a cache backend that has a cache method for your convenience. Just install python-redis-lock like this:

pip install "python-redis-lock[django]"

Now put something like this in your settings:

CACHES = {
    'default': {
        'BACKEND': 'redis_lock.django_cache.RedisCache',
        'LOCATION': '127.0.0.1:6379',
        'OPTIONS': {
            'DB': 1
        }
    }
}

This backend just adds a convenient .lock(name, expire=None) function to django-redis’s cache backend.

You would write your functions like this:

from django.core.cache import cache

def function():
    val = cache.get(key)
    if val:
        return val
    else:
        with cache.lock(key):
            val = cache.get(key)
            if val:
                return val
            else:
                # DO EXPENSIVE WORK
                val = ...

                cache.set(key, value)
                return val

Troubleshooting

In some cases, the lock remains in redis forever (like a server blackout / redis or application crash / an unhandled exception). In such cases, the lock is not removed by restarting the application. One solution is to use the reset_all() function when the application starts:

# On application start/restart
import redis_lock
redis_lock.reset_all()

Alternativelly, you can reset individual locks via the reset method.

Use these carefully, if you understand what you do.

Features

  • based on the standard SETNX recipe

  • optional expiry

  • no spinloops at acquire

Implementation

redis_lock will use 2 keys for each lock named <name>:

  • lock:<name> - a string value for the actual lock

  • lock-signal:<name> - a list value for signaling the waiters when the lock is released

This is how it works:

python-redis-lock flow diagram

Documentation

https://python-redis-lock.readthedocs.org/

Development

To run the all tests run:

tox

Requirements

OS:

Any

Runtime:

Python 2.6, 2.7, 3.2, 3.3 or PyPy

Services:

Redis 2.6.12 or later.

Similar projects

Changelog

2.1.0 (2015-03-12)

  • New specific exception classes: AlreadyAcquired and NotAcquired.

  • Slightly improved efficiency when non-waiting acquires are used.

2.0.0 (2014-12-29)

  • Rename Lock.token to Lock.id. Now only allowed to be set via constructor. (contributed by Jardel Weyrich)

1.0.0 (2014-12-23)

  • Fix Django integration. (reported by Jardel Weyrich)

  • Reorganize tests to use py.test.

  • Add test for Django integration.

  • Add reset_all functionality. (contributed by Yokotoka)

  • Add Lock.reset functionality.

  • Expose the Lock.token attribute.

0.1.2 (2013-11-05)

  • ?

0.1.1 (2013-10-26)

  • ?

0.1.0 (2013-10-26)

  • ?

0.0.1 (2013-10-25)

  • First release on PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python-redis-lock-2.1.0.tar.gz (83.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_redis_lock-2.1.0-py2.py3-none-any.whl (9.6 kB view details)

Uploaded Python 2Python 3

File details

Details for the file python-redis-lock-2.1.0.tar.gz.

File metadata

File hashes

Hashes for python-redis-lock-2.1.0.tar.gz
Algorithm Hash digest
SHA256 166958e8862ac69e01a91125cf9be33505b4bd5481f3eee20d24c8b7bccac316
MD5 0ebec9ff3008bf6053f29f65b11f5346
BLAKE2b-256 5553756a18716af614b4f031298e3ff18b65b4edda775b3ffa83a5976797b6d9

See more details on using hashes here.

File details

Details for the file python_redis_lock-2.1.0-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for python_redis_lock-2.1.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 379f9b85fca5710e9c6516844e49e86fc108256e272e676312188cf1dc987eac
MD5 fe4f61593a0b1934fb9f1e33a79bd5ed
BLAKE2b-256 0a2531148325d1e5e2697be4168dbbb23d927bbcf894a54a06036f71edc95059

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page