Metadata-Version: 2.1
Name: sqlalchemy-dlock
Version: 0.2b3.post4
Summary: A distributed lock implementation based on SQLAlchemy
Home-page: https://github.com/tanbro/sqlalchemy-dlock
Author: liu xue yan
Author-email: liu_xue_yan@foxmail.com
License: BSD 3-Clause License
Keywords: SQLAlchemy,lock,distributed,distributed lock,SQL,database,DBMS
Platform: UNKNOWN
Classifier: License :: OSI Approved :: BSD License
Classifier: Development Status :: 4 - Beta
Classifier: Topic :: Database :: Front-Ends
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.6
Description-Content-Type: text/markdown
Provides-Extra: asyncio
License-File: LICENSE

# SQLAlchemy-DLock

[![GitHub](https://img.shields.io/github/license/tanbro/sqlalchemy-dlock)](https://github.com/tanbro/sqlalchemy-dlock)
[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/tanbro/sqlalchemy-dlock)](https://github.com/tanbro/sqlalchemy-dlock/tags)
[![Python package](https://github.com/tanbro/sqlalchemy-dlock/actions/workflows/python-package.yml/badge.svg)](https://github.com/tanbro/sqlalchemy-dlock/actions/workflows/python-package.yml)
[![PyPI](https://img.shields.io/pypi/v/sqlalchemy-dlock)](https://pypi.org/project/sqlalchemy-dlock/)
[![PyPI - Status](https://img.shields.io/pypi/status/sqlalchemy-dlock)](https://pypi.org/project/sqlalchemy-dlock/)
[![PyPI - License](https://img.shields.io/pypi/l/sqlalchemy-dlock)](https://pypi.org/project/sqlalchemy-dlock/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sqlalchemy-dlock)](https://pypi.org/project/sqlalchemy-dlock/)
[![Documentation Status](https://readthedocs.org/projects/sqlalchemy-dlock/badge/?version=latest)](https://sqlalchemy-dlock.readthedocs.io/en/latest/?badge=latest)
[![codecov](https://codecov.io/gh/tanbro/sqlalchemy-dlock/branch/main/graph/badge.svg?token=GfcDT1ckFX)](https://codecov.io/gh/tanbro/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.html>
- `PostgreSQL` - 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:

  ```python
  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
  ```

- Use in `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@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

  ```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@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

  ```python
  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

Just `up` the docker-compose in `tests` directory:

```bash
(cd tests; docker-compose up --abort-on-container-exit --exit-code-from pytest; docker-compose down)
```

[SQLAlchemy]: https://www.sqlalchemy.org/ "The Python SQL Toolkit and Object Relational Mapper"

# CHANGELOG

## v0.2b2/b3

Date: 2021-03-23

- Add:
  - More unit tests
  - Optimized CI

## v0.2b1

Date: 2021-03-16

- Add:

  - New unit tests
  - CI by github workflows

## v0.2a3

Date: 2021-03-14

- Change:

  - Drop Python 3.5 support.
  - Remove SQLAlchemy version requires earlier than 1.4 in setup, it's not supported actually.
  - Adjust PostgreSQL lock's constructor arguments order

- Add:

  - More test cases, and add test/deploy workflow in github actions.
  - Add docker-compose test scripts

## v0.2a2

Date: 2021-03-09

- Change:

  - Rename a lot of function/class:

    - `sadlock` -> `create_sadlock`
    - `asyncio.sadlock` -> `asyncio.create_async_sadlock`
  
    and some other ...

## v0.2a1

Date: 2021-03-08

- New:

  - Asynchronous IO Support by:

    - [aiomysql](https://github.com/aio-libs/aiomysql) for MySQL

      Connection URL is like: `"mysql+aiomysql://user:password@host:3306/schema?charset=utf8mb4"`

    - [asyncpg](https://github.com/MagicStack/asyncpg) for PostgreSQL

      Connection URL is like: `"postgresql+asyncpg://user:password@host:5432/db"`

    Read <https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html> for details

## v0.1.2

Date: 2021-01-26

Still an early version, not for production.

- Changes:
  - Arguments and it's default value of `acquire` now similar to stdlib's `multiprossing.Lock`, instead of `Threading.Lock`
  - MySQL lock now accepts float-point value as `timeout`
- Adds
  - Several new test cases
- Other
  - Many other small adjustment

## v0.1.1

- A very early version, maybe not stable enough.
- Replace black2b with crc64-iso in PostgreSQL key convert function
- Only named arguments as extra parameters allowed in Lock's implementation class

# AUTHORS

* Liu Xue Yan (<liu_xue_yan@foxmail.com>)

  [![liu_xue_yan@foxmail.com](https://www.gravatar.com/avatar/049d2fae1fd2df6439e87d1383d0276b)](mailto:liu_xue_yan@foxmail.com)


