# sqlalchemy-dlock
[](https://github.com/tanbro/sqlalchemy-dlock/actions/workflows/python-package.yml)
[](https://pypi.org/project/sqlalchemy-dlock/)
[](https://sqlalchemy-dlock.readthedocs.io/en/latest/)
[](https://codecov.io/gh/tanbro/sqlalchemy-dlock)
`sqlalchemy-dlock` is a distributed-lock library based on Database and [SQLAlchemy][].
It currently supports below locks:
| Database | Lock |
|------------|-----------------------------------------------------------------------------------------------|
| MySQL | [named lock](https://dev.mysql.com/doc/refman/en/locking-functions.html) |
| PostgreSQL | [advisory lock](https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS) |
## Install
```bash
pip install sqlalchemy-dlock
```
## Usage
- Work with [SQLAlchemy][] [`Connection`](https://docs.sqlalchemy.org/20/core/connections.html):
```python
from sqlalchemy import create_engine
from sqlalchemy_dlock import create_sadlock
key = 'user/001'
engine = create_engine('postgresql://scott:tiger@127.0.0.1/')
conn = engine.connect()
# Create the D-Lock on the connection
lock = create_sadlock(conn, key)
# it's not lock when constructed
assert not lock.locked
# lock
lock.acquire()
assert lock.locked
# un-lock
lock.release()
assert not lock.locked
```
- `with` statement
```python
from contextlib import closing
from sqlalchemy import create_engine
from sqlalchemy_dlock import create_sadlock
key = 'user/001'
engine = create_engine('postgresql://scott:tiger@127.0.0.1/')
with engine.connect() as conn:
# Create the D-Lock on the connection
with create_sadlock(conn, key) as lock:
# It's locked
assert lock.locked
# Auto un-locked
assert not lock.locked
# If do not want to be locked in `with`, a `closing` wrapper may help
with closing(create_sadlock(conn, key)) as lock2:
# It's NOT locked here !!!
assert not lock2.locked
# lock it now:
lock2.acquire()
assert lock2.locked
# Auto un-locked
assert not lock2.locked
```
- Work with [SQLAlchemy][] [`ORM` `Session`](https://docs.sqlalchemy.org/en/20/orm/session.html):
```python
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy_dlock import create_sadlock
key = 'user/001'
engine = create_engine('postgresql://scott:tiger@127.0.0.1/')
Session = sessionmaker(bind=engine)
with Session() as session:
with create_sadlock(session, key) as lock:
assert lock.locked
assert not lock.locked
```
- Asynchronous I/O Support
> đź’ˇ **TIP**
>
> - [SQLAlchemy][] `1.x`'s asynchronous I/O: <https://docs.sqlalchemy.org/14/orm/extensions/asyncio.html>
> - [SQLAlchemy][] `2.x`'s asynchronous I/O: <https://docs.sqlalchemy.org/20/orm/extensions/asyncio.html>
```python
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy_dlock import create_async_sadlock
key = 'user/001'
engine = create_async_engine('postgresql+asyncpg://scott:tiger@127.0.0.1/')
async with engine.connect() as conn:
async with create_async_sadlock(conn, key) as lock:
assert lock.locked
await lock.release()
assert not lock.locked
await lock.acquire()
assert not lock.locked
```
> ℹ️ **NOTE** \
> [aiomysql][], [asyncpg][] and [psycopg][] are tested asynchronous drivers.
## Test
Following drivers are tested:
- MySQL:
- [mysqlclient][] (synchronous)
- [pymysql][] (synchronous)
- [aiomysql][] (asynchronous)
- Postgres:
- [psycopg2][] (synchronous)
- [asyncpg][] (asynchronous)
- [psycopg][] (synchronous and asynchronous)
You can run unit-tests
- on local environment:
1. Install the project in editable mode with `asyncio` optional dependencies, and libraries/drivers needed in test. A virtual environment ([venv][]) is strongly advised:
```bash
pip install -e . --group dev
```
2. start up mysql and postgresql service
There is a docker [compose][] file `db.docker-compose.yml` in project's top directory,
which can be used to run mysql and postgresql develop environment conveniently:
```bash
docker compose -f db.docker-compose.yml up
```
3. set environment variables `TEST_URLS` and `TEST_ASYNC_URLS` for sync and async database connection url.
Multiple connections separated by space.
eg: (following values are also the defaults, and can be omitted)
```ini
TEST_URLS=mysql://test:test@127.0.0.1/test postgresql://postgres:test@127.0.0.1/
TEST_ASYNC_URLS=mysql+aiomysql://test:test@127.0.0.1/test postgresql+asyncpg://postgres:test@127.0.0.1/
```
> ℹ️ **NOTE** \
> The test cases would load environment variables from dot-env file `tests/.env`.
4. run unit-test
```bash
python -m unittest
```
- or on docker [compose][]:
`tests/docker-compose.yml` defines a Python and [SQLAlchemy][] version matrix -- it combines multiple Python versions and [SQLAlchemy][] `v1`/`v2` for test cases. We can run it by:
```bash
cd tests
docker compose up --abort-on-container-exit
```
[SQLAlchemy]: https://www.sqlalchemy.org/ "The Python SQL Toolkit and Object Relational Mapper"
[venv]: https://docs.python.org/library/venv.html "The venv module supports creating lightweight “virtual environments”, each with their own independent set of Python packages installed in their site directories. "
[mysqlclient]: https://pypi.org/project/mysqlclient/ "Python interface to MySQL"
[psycopg2]: https://pypi.org/project/psycopg2/ "PostgreSQL database adapter for Python"
[psycopg]: https://pypi.org/project/psycopg/ "Psycopg 3 is a modern implementation of a PostgreSQL adapter for Python."
[aiomysql]: https://pypi.org/project/aiomysql/ "aiomysql is a “driver” for accessing a MySQL database from the asyncio (PEP-3156/tulip) framework."
[asyncpg]: https://pypi.org/project/asyncpg/ "asyncpg is a database interface library designed specifically for PostgreSQL and Python/asyncio. "
[pymysql]: https://pypi.org/project/pymysql/ "Pure Python MySQL Driver"
[compose]: https://docs.docker.com/compose/ "Compose is a tool for defining and running multi-container Docker applications."
Raw data
{
"_id": null,
"home_page": null,
"name": "sqlalchemy-dlock",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "SQLAlchemy, lock, distributed, distributed lock, SQL, database, DBMS, RDBMS",
"author": null,
"author_email": "liu xue yan <liu_xue_yan@foxmail.com>",
"download_url": "https://files.pythonhosted.org/packages/96/b2/ef20875e2385262b4b5fc9c749d927f8d25a8de33b3a1270e1ef7122d2f5/sqlalchemy_dlock-0.7.0.tar.gz",
"platform": null,
"description": "# sqlalchemy-dlock\n\n[](https://github.com/tanbro/sqlalchemy-dlock/actions/workflows/python-package.yml)\n[](https://pypi.org/project/sqlalchemy-dlock/)\n[](https://sqlalchemy-dlock.readthedocs.io/en/latest/)\n[](https://codecov.io/gh/tanbro/sqlalchemy-dlock)\n\n`sqlalchemy-dlock` is a distributed-lock library based on Database and [SQLAlchemy][].\n\nIt currently supports below locks:\n\n| Database | Lock |\n|------------|-----------------------------------------------------------------------------------------------|\n| MySQL | [named lock](https://dev.mysql.com/doc/refman/en/locking-functions.html) |\n| PostgreSQL | [advisory lock](https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS) |\n\n## Install\n\n```bash\npip install sqlalchemy-dlock\n```\n\n## Usage\n\n- Work with [SQLAlchemy][] [`Connection`](https://docs.sqlalchemy.org/20/core/connections.html):\n\n ```python\n from sqlalchemy import create_engine\n from sqlalchemy_dlock import create_sadlock\n\n key = 'user/001'\n\n engine = create_engine('postgresql://scott:tiger@127.0.0.1/')\n conn = engine.connect()\n\n # Create the D-Lock on the connection\n lock = create_sadlock(conn, key)\n\n # it's not lock when constructed\n assert not lock.locked\n\n # lock\n lock.acquire()\n assert lock.locked\n\n # un-lock\n lock.release()\n assert not lock.locked\n ```\n\n- `with` statement\n\n ```python\n from contextlib import closing\n\n from sqlalchemy import create_engine\n from sqlalchemy_dlock import create_sadlock\n\n key = 'user/001'\n\n engine = create_engine('postgresql://scott:tiger@127.0.0.1/')\n with engine.connect() as conn:\n\n # Create the D-Lock on the connection\n with create_sadlock(conn, key) as lock:\n # It's locked\n assert lock.locked\n\n # Auto un-locked\n assert not lock.locked\n\n # If do not want to be locked in `with`, a `closing` wrapper may help\n with closing(create_sadlock(conn, key)) as lock2:\n # It's NOT locked here !!!\n assert not lock2.locked\n # lock it now:\n lock2.acquire()\n assert lock2.locked\n\n # Auto un-locked\n assert not lock2.locked\n ```\n\n- Work with [SQLAlchemy][] [`ORM` `Session`](https://docs.sqlalchemy.org/en/20/orm/session.html):\n\n ```python\n from sqlalchemy import create_engine\n from sqlalchemy.orm import sessionmaker\n from sqlalchemy_dlock import create_sadlock\n\n key = 'user/001'\n\n engine = create_engine('postgresql://scott:tiger@127.0.0.1/')\n Session = sessionmaker(bind=engine)\n\n with Session() as session:\n with create_sadlock(session, key) as lock:\n assert lock.locked\n assert not lock.locked\n ```\n\n- Asynchronous I/O Support\n\n > \ud83d\udca1 **TIP**\n >\n > - [SQLAlchemy][] `1.x`'s asynchronous I/O: <https://docs.sqlalchemy.org/14/orm/extensions/asyncio.html>\n > - [SQLAlchemy][] `2.x`'s asynchronous I/O: <https://docs.sqlalchemy.org/20/orm/extensions/asyncio.html>\n\n ```python\n from sqlalchemy.ext.asyncio import create_async_engine\n from sqlalchemy_dlock import create_async_sadlock\n\n key = 'user/001'\n\n engine = create_async_engine('postgresql+asyncpg://scott:tiger@127.0.0.1/')\n\n async with engine.connect() as conn:\n async with create_async_sadlock(conn, key) as lock:\n assert lock.locked\n await lock.release()\n assert not lock.locked\n await lock.acquire()\n assert not lock.locked\n ```\n\n > \u2139\ufe0f **NOTE** \\\n > [aiomysql][], [asyncpg][] and [psycopg][] are tested asynchronous drivers.\n\n## Test\n\nFollowing drivers are tested:\n\n- MySQL:\n - [mysqlclient][] (synchronous)\n - [pymysql][] (synchronous)\n - [aiomysql][] (asynchronous)\n- Postgres:\n - [psycopg2][] (synchronous)\n - [asyncpg][] (asynchronous)\n - [psycopg][] (synchronous and asynchronous)\n\nYou can run unit-tests\n\n- on local environment:\n\n 1. Install the project in editable mode with `asyncio` optional dependencies, and libraries/drivers needed in test. A virtual environment ([venv][]) is strongly advised:\n\n ```bash\n pip install -e . --group dev\n ```\n\n 2. start up mysql and postgresql service\n\n There is a docker [compose][] file `db.docker-compose.yml` in project's top directory,\n which can be used to run mysql and postgresql develop environment conveniently:\n\n ```bash\n docker compose -f db.docker-compose.yml up\n ```\n\n 3. set environment variables `TEST_URLS` and `TEST_ASYNC_URLS` for sync and async database connection url.\n Multiple connections separated by space.\n\n eg: (following values are also the defaults, and can be omitted)\n\n ```ini\n TEST_URLS=mysql://test:test@127.0.0.1/test postgresql://postgres:test@127.0.0.1/\n TEST_ASYNC_URLS=mysql+aiomysql://test:test@127.0.0.1/test postgresql+asyncpg://postgres:test@127.0.0.1/\n ```\n\n > \u2139\ufe0f **NOTE** \\\n > The test cases would load environment variables from dot-env file `tests/.env`.\n\n 4. run unit-test\n\n ```bash\n python -m unittest\n ```\n\n- or on docker [compose][]:\n\n `tests/docker-compose.yml` defines a Python and [SQLAlchemy][] version matrix -- it combines multiple Python versions and [SQLAlchemy][] `v1`/`v2` for test cases. We can run it by:\n\n ```bash\n cd tests\n docker compose up --abort-on-container-exit\n ```\n\n[SQLAlchemy]: https://www.sqlalchemy.org/ \"The Python SQL Toolkit and Object Relational Mapper\"\n[venv]: https://docs.python.org/library/venv.html \"The venv module supports creating lightweight \u201cvirtual environments\u201d, each with their own independent set of Python packages installed in their site directories. \"\n[mysqlclient]: https://pypi.org/project/mysqlclient/ \"Python interface to MySQL\"\n[psycopg2]: https://pypi.org/project/psycopg2/ \"PostgreSQL database adapter for Python\"\n[psycopg]: https://pypi.org/project/psycopg/ \"Psycopg 3 is a modern implementation of a PostgreSQL adapter for Python.\"\n[aiomysql]: https://pypi.org/project/aiomysql/ \"aiomysql is a \u201cdriver\u201d for accessing a MySQL database from the asyncio (PEP-3156/tulip) framework.\"\n[asyncpg]: https://pypi.org/project/asyncpg/ \"asyncpg is a database interface library designed specifically for PostgreSQL and Python/asyncio. \"\n[pymysql]: https://pypi.org/project/pymysql/ \"Pure Python MySQL Driver\"\n[compose]: https://docs.docker.com/compose/ \"Compose is a tool for defining and running multi-container Docker applications.\"\n",
"bugtrack_url": null,
"license": null,
"summary": "A distributed lock implementation based on SQLAlchemy",
"version": "0.7.0",
"project_urls": {
"documentation": "https://sqlalchemy-dlock.readthedocs.io/",
"homepage": "https://github.com/tanbro/sqlalchemy-dlock",
"repository": "https://github.com/tanbro/sqlalchemy-dlock.git"
},
"split_keywords": [
"sqlalchemy",
" lock",
" distributed",
" distributed lock",
" sql",
" database",
" dbms",
" rdbms"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "30ae0940c9f45206af5554242604a278642f7a464613d1b6aa9fedc95f421bdf",
"md5": "b87a466a2a75a3882cfa2c48f88a993f",
"sha256": "cd979ab24c3002f0001bea2dae5cce3d7ce868bf5f2a8aa2c4d5e1f5f87301ec"
},
"downloads": -1,
"filename": "sqlalchemy_dlock-0.7.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b87a466a2a75a3882cfa2c48f88a993f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 17771,
"upload_time": "2025-10-11T15:45:02",
"upload_time_iso_8601": "2025-10-11T15:45:02.438983Z",
"url": "https://files.pythonhosted.org/packages/30/ae/0940c9f45206af5554242604a278642f7a464613d1b6aa9fedc95f421bdf/sqlalchemy_dlock-0.7.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "96b2ef20875e2385262b4b5fc9c749d927f8d25a8de33b3a1270e1ef7122d2f5",
"md5": "a0b0d6d4edc60d03c35160cebf089ac4",
"sha256": "9c5467946ff6bf96f472a71690b49ee24240de1a637ef636d050b80fe4cdfc0a"
},
"downloads": -1,
"filename": "sqlalchemy_dlock-0.7.0.tar.gz",
"has_sig": false,
"md5_digest": "a0b0d6d4edc60d03c35160cebf089ac4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 18182,
"upload_time": "2025-10-11T15:45:03",
"upload_time_iso_8601": "2025-10-11T15:45:03.378286Z",
"url": "https://files.pythonhosted.org/packages/96/b2/ef20875e2385262b4b5fc9c749d927f8d25a8de33b3a1270e1ef7122d2f5/sqlalchemy_dlock-0.7.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-11 15:45:03",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "tanbro",
"github_project": "sqlalchemy-dlock",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"lcname": "sqlalchemy-dlock"
}