## postgresql-lock
Lock mechanism implemented with PostgreSQL advisory locks.
Easily implement distributed database locking.
### Install
```sh
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._
```python
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:
```python
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`)
```python
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)
```python
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
```python
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
```python
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.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
Raw data
{
"_id": null,
"home_page": "https://github.com/seankerr/py-postgresql-lock",
"name": "postgresql-lock",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7,<4.0",
"maintainer_email": "",
"keywords": "postgresql,postgres,distributed,lock",
"author": "Sean Kerr",
"author_email": "sean@code-box.org",
"download_url": "https://files.pythonhosted.org/packages/cd/a8/ce6716c1a5daf714fb7f1b106cf5a00b798df24fabc5c3fbd8660ec6df9f/postgresql_lock-0.1.5.tar.gz",
"platform": null,
"description": "## postgresql-lock\n\nLock mechanism implemented with PostgreSQL advisory locks.\n\nEasily implement distributed database locking.\n\n### Install\n\n```sh\npip install postgresql-lock\n```\n\n### Supported database interfaces\n\n- **asyncpg**\n - asynchronous\n- **psycopg2**\n - synchronous\n- **psycopg3**\n - asynchronous\n - synchronous\n- **sqlalchemy** (supports version 1 & 2; can use any underlying database interface)\n - asynchronous\n - synchronous\n\n### Why would I use this?\n\n- PostgreSQL table locks aren't sufficient for your use-case\n- PostgreSQL row locks don't work on `INSERT`\n- You want to prevent race conditions between `INSERT` and `UPDATE` on the same primary key\n- None of the aforementioned details fit your use-case, but you have PostgreSQL installed and need to prevent race conditions in a distributed system\n\n### Default operation\n\nBy default `postgresql-lock` will use `session` lock scope in `blocking` mode with\n`rollback_on_error` enabled. The `session` lock scope means only a single database connection can\nacquire the lock at a time.\n\n### Usage\n\nAll work revolves around the `Lock` class.\n\nThe easiest way to use `Lock` is with `with` or `async with` statements. The lock will be\nreleased automatically. If `rollback_on_error` is enabled (default), rollbacks are automatically\nhandled prior to release.\n\n_Using `with` and `async with` implies blocking mode._\n\n```python\nfrom postgresql_lock import Lock\n\n# setup connection\nconn = ...\n\n# create and use lock\nwith Lock(conn, \"shared-identifier\"):\n print(\"Acquired lock!\")\n\n # do something here\n```\n\nNow compare the above example to the equivalent try/finally example below:\n\n```python\nfrom postgresql_lock import Lock\n\n# setup connection\nconn = ...\n\n# create lock\nlock = Lock(conn, \"shared-identifier\")\n\ntry:\n # acquire lock\n lock.acquire()\n\n print(\"Acquired lock!\")\n\n try:\n # do something here\n pass\n\n except Exception as exc:\n # handle_error() will rollback the transaction by default\n lock.handle_error(exc)\n\n raise exc\nfinally:\n # release lock (this is safe to run even if the lock has not been acquired)\n lock.release()\n```\n\n### Asynchronous usage (without `async with`)\n\n```python\nfrom postgresql_lock import Lock\n\n# setup connection\nconn = ...\n\n# create lock\nlock = Lock(conn, \"shared-identifier\")\n\ntry:\n # acquire lock\n await lock.acquire_async()\n\n print(\"Acquired lock!\")\n\n try:\n # do something here\n pass\n\n except Exception as exc:\n # handle_error_async() will rollback the transaction by default\n await lock.handle_error_async(exc)\n\n raise exc\nfinally:\n # release lock (this is safe to run even if the lock has not been acquired)\n await lock.release_async()\n```\n\n### Non-blocking mode (supports async as well)\n\n```python\nfrom postgresql_lock import Lock\n\n# setup connection\nconn = ...\n\n# create lock\nlock = Lock(conn, \"shared-identifier\")\n\n# acquire lock\nif lock.acquire(block=False):\n # do something here\n pass\n\nelse:\n # could not acquire lock\n pass\n\n# release lock (this is safe to run even if the lock has not been acquired)\nlock.release()\n```\n\n### Specify the database interface manually\n\n```python\nfrom postgresql_lock import Lock\n\n# setup connection\nconn = ...\n\n# create and use lock\nlock = Lock(conn, \"shared-identifier\", interface=\"asyncpg\")\n\n# do things with the lock\n```\n\n### Handle rollbacks manually\n\n```python\nfrom postgresql_lock import Lock\n\n# setup connection\nconn = ...\n\n# create and use lock\nlock = Lock(conn, \"shared-identifier\", rollback_on_error=False)\n\n# do things with the lock\n```\n\n### Changelog\n\n- **0.1.5**\n - Rename package from postgres-lock to postgresql-lock\n- **0.1.4**\n - Add py.typed for mypy\n- **0.1.3**\n - Key can be any object\n- **0.1.2**\n - Add Lock.rollback_on_error (default true)\n - Add Lock.handle_error() & Lock.handle_error_async()\n- **0.1.1**\n - Key can be str or int\n",
"bugtrack_url": null,
"license": "BSD-3-Clause",
"summary": "Lock mechanism implemented with PostgreSQL advisory locks.",
"version": "0.1.5",
"project_urls": {
"Homepage": "https://github.com/seankerr/py-postgresql-lock",
"Repository": "https://github.com/seankerr/py-postgresql-lock"
},
"split_keywords": [
"postgresql",
"postgres",
"distributed",
"lock"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "e7e4da3b781848c430a8ecdd8e500e97c6b20be202b3e7be3305b4e89789d5c3",
"md5": "05842fe795cbea43f612da85034f2a9b",
"sha256": "8fd4c03f6f0aeeb0c95938e4a0f4e3cc52eb68bc184991f1973d3ca128dc22a6"
},
"downloads": -1,
"filename": "postgresql_lock-0.1.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "05842fe795cbea43f612da85034f2a9b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7,<4.0",
"size": 9746,
"upload_time": "2023-09-05T16:25:32",
"upload_time_iso_8601": "2023-09-05T16:25:32.482039Z",
"url": "https://files.pythonhosted.org/packages/e7/e4/da3b781848c430a8ecdd8e500e97c6b20be202b3e7be3305b4e89789d5c3/postgresql_lock-0.1.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "cda8ce6716c1a5daf714fb7f1b106cf5a00b798df24fabc5c3fbd8660ec6df9f",
"md5": "8a8e62f1b804ea6739effa5258c7180a",
"sha256": "ebebca23f9ab1813a7c3f636f9df4bde190c1cdee5cd5ef39b1bdc45f0ff8221"
},
"downloads": -1,
"filename": "postgresql_lock-0.1.5.tar.gz",
"has_sig": false,
"md5_digest": "8a8e62f1b804ea6739effa5258c7180a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7,<4.0",
"size": 7083,
"upload_time": "2023-09-05T16:25:34",
"upload_time_iso_8601": "2023-09-05T16:25:34.514209Z",
"url": "https://files.pythonhosted.org/packages/cd/a8/ce6716c1a5daf714fb7f1b106cf5a00b798df24fabc5c3fbd8660ec6df9f/postgresql_lock-0.1.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-09-05 16:25:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "seankerr",
"github_project": "py-postgresql-lock",
"travis_ci": false,
"coveralls": true,
"github_actions": false,
"lcname": "postgresql-lock"
}