score.distlock

This module provides means of creating and managing reentrant mutexes in multi-server environments. Locks are created in a common database accessible by all involed parties.

Quickstart

Acquire the lock “mylock” for the duration of the dosomething() call:

>>> with conf.get('mylock') as lock:
...     dosomething()
...
>>> # the lock is automatically released at this point

No other process can acquire the same lock “mylock” while dosomething() is running. If the dosomething() operation takes a long time, your lock might expire in the mean time. You should occasionally call Lock.extend() in such cases:

>>> with conf.get('mylock') as lock:
...     do_part_of_something()
...     lock.extend()
...     do_more_things()
...

Configuration

score.distlock.init(confdict)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

sqlalchemy.*

All configuration values under this key will be passed to sqlalchemy.engine_from_config(), which in turn calls sqlalchemy.create_engine() with these configuration values as keyword arguments. Usually the following is sufficient:

sqlalchemy.url = postgresql://dbuser@localhost/projname
maxtime 1m
Maximum time frame a lock can be held without being updated. Any lock older than this time frame is considered expired.

Details

Tokens

If a lock is to be held even after the process has finished, it can ben converted into a token:

>>> token = conf.acquire('mylock')
>>> token
b'0f5ba13b1a9d2c9951b29352af619534c0f1487c5a9b5b7e0a6d64042fe8fa15aad37be9d5fae35217381e2fffbdb25c1787a572a89b0ce98eaa509ed8f3346b8fabc82deb625542b2e29c9d26f301906fd1d3bd026bf816faa60180374a077146f08d0995e3dbd84726754c9e9f9080404a6283a8d78c41f2d1ac5cd0aa6c62'

As long as you do not hit a timeout, this token represents the hold on the Lock “mylock”. You can use this token to control your lock from another process:

>>> conf.release('mylock', token)

Timeouts

Since it is possible to keep a lock after leaving a python process, all locks will expire automatically after a certain time frame to prevent process starvation. If you want to keep a lock for a longer duration than the configured maxtime, you should extend your lock before it expires:

>>> conf.extend('mylock', token)

API

Configuration

score.distlock.init(confdict)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

sqlalchemy.*

All configuration values under this key will be passed to sqlalchemy.engine_from_config(), which in turn calls sqlalchemy.create_engine() with these configuration values as keyword arguments. Usually the following is sufficient:

sqlalchemy.url = postgresql://dbuser@localhost/projname
maxtime 1m
Maximum time frame a lock can be held without being updated. Any lock older than this time frame is considered expired.
class score.distlock.ConfiguredDistlockModule(engine, maxtime)[source]

This module’s configuration class.

engine

The SQLAlchemy Engine that will provide the connection to the common database.

maxtime

The configured maximum age of a Lock.

get(name)[source]

Provides the Lock with given name.

Keeps a weak reference to the Lock internally and will return the same object on consecutive calls (as long as the object is not garbage-collected in the mean time.)

acquire(name)[source]

Convenient access to Lock.acquire().

try_acquiring(name)[source]

Convenient access to Lock.try_acquiring().

extend(name, token=None)[source]

Convenient access to Lock.extend().

release(name, token=None)[source]

Convenient access to Lock.release().

vacuum(session=None)[source]

Cleans up all expired locks from the database, speeding up all future lock operations.

There is no need to call this function manually, it will be invoked on each attempt t acquire a lock.

Lock

class score.distlock.Lock(conf, name)[source]

Allows manipulating a single named lock.

This object can also be used as a context generator for with statements:

with lock:
    do_something()
acquire()[source]

Acquires the lock or raises CouldNotAcquireLock.

Returns an authentication token on success, that needs to be passed to various other functions if the lock will be managed by a different process. See the narrative documentation for an explanation of the resulting token.

try_acquiring()[source]

Tries to acquire the lock and returns an authentication token on success, or None if the lock could not be acquired.

Returns an authentication token on success, that needs to be passed to various other functions if the lock will be managed by a different process. See the narrative documentation for an explanation of the resulting token.

extend(token=None)[source]

Extends the lock.

See the narrative documentation for an explanation of the token parameter.

If this function is successfull, the lock will be valid for another “maxtime” seconds.

release(token=None, ignore_expired=False)[source]

Releases this lock, others may acquire it immediately.

See the narrative documentation for an explanation of the token parameter.

Unless the ignore_expired parameter is passed a truthy value, the function will raise LockExpired if the user is no longer holding the lock. token parameter.

Exceptions

class score.distlock.CouldNotAcquireLock[source]

Thrown when a lock could not be acquired.

class score.distlock.LockExpired[source]

Thrown when a lock expired unexpectedly.