SQLAlchemy-DLock
Distributed lock based on Database and SQLAlchemy.
It currently supports below locks:
MySQL
- named lock: https://dev.mysql.com/doc/refman/8.0/en/locking-functions.htmlPostgreSQL
- advisory lock: https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS
❗ Note:
The project is not stable enough and DO NOT use it in production.
Usages
Work with SQLAlchemy’s
Connection
object:from sqlalchemy import create_engine from sqlalchemy_dlock import create_sadlock key = 'user/001' engine = create_engine('postgresql://scott:tiger@localhost/') conn = engine.connect() # Create the D-Lock on the connection lock = create_sadlock(conn, key) # it's not lock when constructed assert not lock.acquired # lock lock.acquire() assert lock.acquired # un-lock lock.release() assert not lock.acquired
Used in
with
statementfrom contextlib import closing from sqlalchemy import create_engine from sqlalchemy_dlock import create_sadlock key = 'user/001' engine = create_engine('postgresql://scott:tiger@localhost/') with engine.connect() as conn: # Create the D-Lock on the connection with create_sadlock(conn, key) as lock: # It's locked assert lock.acquired # Auto un-locked assert not lock.acquired # 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.acquired # lock it now: lock2.acquire() assert lock2.acquired # Auto un-locked assert not lock2.acquired
Work with SQLAlchemy’s
ORM
session:❗ Note:
According to https://docs.sqlalchemy.org/14/orm/extensions/asyncio.html:
The asyncio extension as of SQLAlchemy 1.4.3 can now be considered to be beta level software.
The asyncio extension requires at least Python version 3.6
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@localhost/') Session = sessionmaker(bind=engine) with Session() as session: with create_sadlock(session, key) as lock: assert lock.acquired assert not lock.acquired
Work asynchronously
from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy_dlock.asyncio import create_async_sadlock key = 'user/001' engine = create_async_engine('postgresql+asyncpg://scott:tiger@localhost/') async with engine.begin() 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
Tests
Set environment variables TEST_URLS
and TEST_ASYNC_URLS
for sync and async database connection url.
Multiple connections separated by space.
Set environment variables NO_ASYNCIO
to 1
disable asyncio tests.
The test cases load environment variables in tests/.env
.
eg (and also the defaults):
TEST_URLS=mysql://test:test@localhost/test postgresql://postgres:test@localhost/
TEST_ASYNC_URLS=mysql+aiomysql://test:test@localhost/test postgresql+asyncpg://postgres:test@localhost/