Skip to main content

Lock mechanism implemented with PostgreSQL advisory locks.

Project description

postgresql-lock

Lock mechanism implemented with PostgreSQL advisory locks.

Easily implement distributed database locking.

Install

pip install postgresql-lock

Supported database interfaces

  • asyncpg
    • asynchronous
  • psycopg2
    • synchronous
  • psycopg3
    • asynchronous
    • synchronous
  • sqlalchemy (supports version 1 & 2; can use any underlying database interface)
    • asynchronous
    • synchronous

Why would I use this?

  • PostgreSQL table locks aren't sufficient for your use-case
  • PostgreSQL row locks don't work on INSERT
  • You want to prevent race conditions between INSERT and UPDATE on the same primary key
  • None of the aforementioned details fit your use-case, but you have PostgreSQL installed and need to prevent race conditions in a distributed system

Default operation

By default postgresql-lock will use session lock scope in blocking mode with rollback_on_error enabled. The session lock scope means only a single database connection can acquire the lock at a time.

Usage

All work revolves around the Lock class.

The easiest way to use Lock is with with or async with statements. The lock will be released automatically. If rollback_on_error is enabled (default), rollbacks are automatically handled prior to release.

Using with and async with implies blocking mode.

from postgresql_lock import Lock

# setup connection
conn = ...

# create and use lock
with Lock(conn, "shared-identifier"):
    print("Acquired lock!")

    # do something here

Now compare the above example to the equivalent try/finally example below:

from postgresql_lock import Lock

# setup connection
conn = ...

# create lock
lock = Lock(conn, "shared-identifier")

try:
    # acquire lock
    lock.acquire()

    print("Acquired lock!")

    try:
        # do something here
        pass

    except Exception as exc:
        # handle_error() will rollback the transaction by default
        lock.handle_error(exc)

        raise exc
finally:
    # release lock (this is safe to run even if the lock has not been acquired)
    lock.release()

Asynchronous usage (without async with)

from postgresql_lock import Lock

# setup connection
conn = ...

# create lock
lock = Lock(conn, "shared-identifier")

try:
    # acquire lock
    await lock.acquire_async()

    print("Acquired lock!")

    try:
        # do something here
        pass

    except Exception as exc:
        # handle_error_async() will rollback the transaction by default
        await lock.handle_error_async(exc)

        raise exc
finally:
    # release lock (this is safe to run even if the lock has not been acquired)
    await lock.release_async()

Non-blocking mode (supports async as well)

from postgresql_lock import Lock

# setup connection
conn = ...

# create lock
lock = Lock(conn, "shared-identifier")

# acquire lock
if lock.acquire(block=False):
    # do something here
    pass

else:
    # could not acquire lock
    pass

# release lock (this is safe to run even if the lock has not been acquired)
lock.release()

Specify the database interface manually

from postgresql_lock import Lock

# setup connection
conn = ...

# create and use lock
lock = Lock(conn, "shared-identifier", interface="asyncpg")

# do things with the lock

Handle rollbacks manually

from postgresql_lock import Lock

# setup connection
conn = ...

# create and use lock
lock = Lock(conn, "shared-identifier", rollback_on_error=False)

# do things with the lock

Changelog

  • 0.2.1
    • Moved public Lock fields to properties
  • 0.1.9
    • Fix: release_async() bug for sqlalchemy connections
  • 0.1.8
    • Add logger() function
    • Use "postgresql_lock" logger name
  • 0.1.7
    • Add logging statements
  • 0.1.6
    • Use int.from_bytes() to convert lock key into integer
    • Fix: psycopg3 close() not being awaited bug
  • 0.1.5
    • Rename package from postgres-lock to postgresql-lock
  • 0.1.4
    • Add py.typed for mypy
  • 0.1.3
    • Key can be any object
  • 0.1.2
    • Add Lock.rollback_on_error (default true)
    • Add Lock.handle_error() & Lock.handle_error_async()
  • 0.1.1
    • Key can be str or int

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

postgresql_lock-0.2.1.tar.gz (7.4 kB view details)

Uploaded Source

Built Distribution

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

postgresql_lock-0.2.1-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file postgresql_lock-0.2.1.tar.gz.

File metadata

  • Download URL: postgresql_lock-0.2.1.tar.gz
  • Upload date:
  • Size: 7.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.12.3 Linux/6.11.0-26-generic

File hashes

Hashes for postgresql_lock-0.2.1.tar.gz
Algorithm Hash digest
SHA256 d601059932d717a68affc12f9c25c4b5667e6d176f44e1a6cceeb17d1daf8e73
MD5 f6b75f2d49dae59dee380a50654b8193
BLAKE2b-256 ae1c1ce9031a036f2a0b688573f9d6f45e707b2ea13150e3db6169a66b76556e

See more details on using hashes here.

File details

Details for the file postgresql_lock-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: postgresql_lock-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 10.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.12.3 Linux/6.11.0-26-generic

File hashes

Hashes for postgresql_lock-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fce20bfa69845652ee6d565b4d262891fc3d72408280d8b63a974d457917b0ec
MD5 afc0071f710b648468d3e824b926d0c9
BLAKE2b-256 d11316ecce84c23180aae3dd3b611e1a26423bc7039e3eb991830c1e680aa31f

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