Skip to content

Commit e68fec2

Browse files
committed
docs: add AGENTS.md documentation and update CLAUDE.md reference
1 parent 13475eb commit e68fec2

3 files changed

Lines changed: 133 additions & 132 deletions

File tree

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ repos:
1919
- id: check-docstring-first
2020

2121
- repo: https://github.com/astral-sh/ruff-pre-commit
22-
rev: v0.15.0
22+
rev: v0.15.8
2323
hooks:
2424
# Run the linter.
2525
- id: ruff
@@ -36,7 +36,7 @@ repos:
3636
args: [--config-file, .mypy.ini, --ignore-missing-imports]
3737

3838
- repo: https://github.com/python-jsonschema/check-jsonschema
39-
rev: "0.36.1"
39+
rev: "0.37.1"
4040
hooks:
4141
- id: check-github-workflows
4242
- id: check-readthedocs

AGENTS.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# sqlalchemy-dlock
2+
3+
A distributed lock implementation based on SQLAlchemy.
4+
5+
## Commands
6+
7+
### Development
8+
```bash
9+
# Install with all dependency groups
10+
uv sync --all
11+
12+
# Install with specific groups
13+
uv sync --group test
14+
uv sync --group typecheck
15+
uv sync --group docs
16+
17+
# Run tests (requires databases running)
18+
python -m unittest
19+
20+
# Type checking
21+
uv run --no-dev mypy
22+
23+
# Linting and formatting
24+
ruff check .
25+
ruff format .
26+
27+
# Build package
28+
uv build
29+
```
30+
31+
### Testing with Docker
32+
```bash
33+
# Start test databases (MySQL, PostgreSQL, MSSQL, Oracle)
34+
docker compose -f db.docker-compose.yml up
35+
36+
# Run full test matrix across Python and SQLAlchemy versions
37+
cd tests
38+
docker compose up --abort-on-container-exit
39+
```
40+
41+
> **Note:** Oracle testing requires **Oracle Database Enterprise/Standard Edition**. Oracle Database Free (23c/23ai) does NOT support `DBMS_LOCK.REQUEST` which is required for distributed lock functionality. This is a fundamental limitation of the Free/Express edition, not related to the container image flavor.
42+
43+
For local Oracle testing, ensure you have a full Oracle Database installation or use the official Oracle image:
44+
```bash
45+
docker compose -f db.docker-compose.yml up
46+
```
47+
48+
### Pre-commit Hooks
49+
```bash
50+
# Install pre-commit hooks
51+
pre-commit install
52+
53+
# Run manually
54+
pre-commit run --all-files
55+
```
56+
57+
## Architecture
58+
59+
**Entry Points:** `create_sadlock()`, `create_async_sadlock()` in [factory.py](src/sqlalchemy_dlock/factory.py)
60+
61+
**Factory Pattern:**
62+
1. Inspect SQLAlchemy engine name from connection/session
63+
2. Look up lock class in [registry.py](src/sqlalchemy_dlock/registry.py) (`REGISTRY` for sync, `ASYNCIO_REGISTRY` for async)
64+
3. Instantiate database-specific lock implementation
65+
66+
**Key Directories:**
67+
- [lock/](src/sqlalchemy_dlock/lock/) - Database-specific lock implementations
68+
- [base.py](src/sqlalchemy_dlock/lock/base.py) - `BaseSadLock` (sync), `BaseAsyncSadLock` (async)
69+
- [mysql.py](src/sqlalchemy_dlock/lock/mysql.py) - MySQL/MariaDB named locks
70+
- [postgresql.py](src/sqlalchemy_dlock/lock/postgresql.py) - PostgreSQL advisory locks
71+
- [mssql.py](src/sqlalchemy_dlock/lock/mssql.py) - MSSQL application locks
72+
- [oracle.py](src/sqlalchemy_dlock/lock/oracle.py) - Oracle user locks
73+
- [statement/](src/sqlalchemy_dlock/statement/) - SQL statement templates for each database
74+
- [registry.py](src/sqlalchemy_dlock/registry.py) - Engine name to lock class mapping
75+
76+
**Test Structure:**
77+
- [tests/](tests/) - Synchronous tests
78+
- [tests/asyncio/](tests/asyncio/) - Asynchronous tests
79+
- [tests/engines.py](tests/engines.py) - Test database connection factory
80+
81+
## Gotchas
82+
83+
### Critical
84+
- **Thread-local locks:** `BaseSadLock` extends `threading.local` - lock objects CANNOT be safely passed between threads. Each thread must create its own lock instance.
85+
- **MySQL re-entrant behavior:** MySQL allows acquiring the same named lock multiple times on the same connection. This is NOT true mutual exclusion.
86+
- **Lock lifetime:** Locks are tied to database connections. Closing a connection releases all associated locks.
87+
88+
### Database-Specific
89+
- **Key hashing:** PostgreSQL and Oracle convert string keys to 64-bit integers via BLAKE2b hash.
90+
- **PostgreSQL timeout:** Implemented through polling, may have ~1 second variance.
91+
- **Oracle lock ID range:** 0-1073741823 (uses `DBMS_LOCK.REQUEST`)
92+
- **Oracle Free limitation:** Oracle Database Free (23c/23ai) does NOT support `DBMS_LOCK.REQUEST`. This is a fundamental limitation of the Free/Express edition. CI skips Oracle tests; test locally with `docker compose -f db.docker-compose.yml up` which uses the official Oracle image.
93+
- **MSSQL driver:** Requires ODBC driver installation (`msodbcsql18` on Ubuntu)
94+
95+
### Resource Requirements
96+
- **MSSQL container:** Requires at least 2GB RAM
97+
- **Oracle container:** Requires at least 2GB RAM
98+
99+
### API Notes
100+
- `contextual_timeout` parameter ONLY affects `with` statements, not direct `acquire()` calls
101+
- Async lock classes are separate (`*AsyncSadLock`) but defined in same modules as sync variants
102+
103+
## Environment
104+
105+
**Python:** 3.9+ (CI tests 3.10-3.14)
106+
107+
**Testing Databases:** Optional Docker services in [db.docker-compose.yml](db.docker-compose.yml)
108+
- MySQL: `mysql://test:test@127.0.0.1:3306/test`
109+
- PostgreSQL: `postgresql://postgres:test@127.0.0.1:5432/`
110+
- MSSQL: `mssql+pyodbc://sa:YourStrongPassword123@127.0.0.1:1433/master`
111+
- Oracle: `oracle+oracledb://sys:YourStrong@Passw0rd@127.0.0.1:1521/?service_name=FREEPDB1`
112+
113+
**Environment Variables:** Test URLs can be set via `TEST_URLS` and `TEST_ASYNC_URLS`, or loaded from `tests/.env`
114+
115+
## Code Style
116+
117+
- **Formatter:** Ruff (line length: 128)
118+
- **Linter:** Ruff with import sorting (`I`)
119+
- **Type checker:** mypy
120+
- **Source layout:** `src/` directory with setuptools
121+
- **Config:** [.ruff.toml](.ruff.toml), [.mypy.ini](.mypy.ini)
122+
123+
## Workflow
124+
125+
When adding support for a new database:
126+
1. Create lock implementation in [lock/](src/sqlalchemy_dlock/lock/) inheriting from `BaseSadLock` / `BaseAsyncSadLock`
127+
2. Create SQL templates in [statement/](src/sqlalchemy_dlock/statement/)
128+
3. Add entry to `REGISTRY` and `ASYNCIO_REGISTRY` in [registry.py](src/sqlalchemy_dlock/registry.py)
129+
4. Add tests in [tests/](tests/) and [tests/asyncio/](tests/asyncio/)
130+
5. Update [db.docker-compose.yml](db.docker-compose.yml) if testing locally

CLAUDE.md

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1 @@
1-
# sqlalchemy-dlock
2-
3-
A distributed lock implementation based on SQLAlchemy.
4-
5-
## Commands
6-
7-
### Development
8-
```bash
9-
# Install with all dependency groups
10-
uv sync --all
11-
12-
# Install with specific groups
13-
uv sync --group test
14-
uv sync --group typecheck
15-
uv sync --group docs
16-
17-
# Run tests (requires databases running)
18-
python -m unittest
19-
20-
# Type checking
21-
uv run --no-dev mypy
22-
23-
# Linting and formatting
24-
ruff check .
25-
ruff format .
26-
27-
# Build package
28-
uv build
29-
```
30-
31-
### Testing with Docker
32-
```bash
33-
# Start test databases (MySQL, PostgreSQL, MSSQL, Oracle)
34-
docker compose -f db.docker-compose.yml up
35-
36-
# Run full test matrix across Python and SQLAlchemy versions
37-
cd tests
38-
docker compose up --abort-on-container-exit
39-
```
40-
41-
> **Note:** Oracle testing requires **Oracle Database Enterprise/Standard Edition**. Oracle Database Free (23c/23ai) does NOT support `DBMS_LOCK.REQUEST` which is required for distributed lock functionality. This is a fundamental limitation of the Free/Express edition, not related to the container image flavor.
42-
43-
For local Oracle testing, ensure you have a full Oracle Database installation or use the official Oracle image:
44-
```bash
45-
docker compose -f db.docker-compose.yml up
46-
```
47-
48-
### Pre-commit Hooks
49-
```bash
50-
# Install pre-commit hooks
51-
pre-commit install
52-
53-
# Run manually
54-
pre-commit run --all-files
55-
```
56-
57-
## Architecture
58-
59-
**Entry Points:** `create_sadlock()`, `create_async_sadlock()` in [factory.py](src/sqlalchemy_dlock/factory.py)
60-
61-
**Factory Pattern:**
62-
1. Inspect SQLAlchemy engine name from connection/session
63-
2. Look up lock class in [registry.py](src/sqlalchemy_dlock/registry.py) (`REGISTRY` for sync, `ASYNCIO_REGISTRY` for async)
64-
3. Instantiate database-specific lock implementation
65-
66-
**Key Directories:**
67-
- [lock/](src/sqlalchemy_dlock/lock/) - Database-specific lock implementations
68-
- [base.py](src/sqlalchemy_dlock/lock/base.py) - `BaseSadLock` (sync), `BaseAsyncSadLock` (async)
69-
- [mysql.py](src/sqlalchemy_dlock/lock/mysql.py) - MySQL/MariaDB named locks
70-
- [postgresql.py](src/sqlalchemy_dlock/lock/postgresql.py) - PostgreSQL advisory locks
71-
- [mssql.py](src/sqlalchemy_dlock/lock/mssql.py) - MSSQL application locks
72-
- [oracle.py](src/sqlalchemy_dlock/lock/oracle.py) - Oracle user locks
73-
- [statement/](src/sqlalchemy_dlock/statement/) - SQL statement templates for each database
74-
- [registry.py](src/sqlalchemy_dlock/registry.py) - Engine name to lock class mapping
75-
76-
**Test Structure:**
77-
- [tests/](tests/) - Synchronous tests
78-
- [tests/asyncio/](tests/asyncio/) - Asynchronous tests
79-
- [tests/engines.py](tests/engines.py) - Test database connection factory
80-
81-
## Gotchas
82-
83-
### Critical
84-
- **Thread-local locks:** `BaseSadLock` extends `threading.local` - lock objects CANNOT be safely passed between threads. Each thread must create its own lock instance.
85-
- **MySQL re-entrant behavior:** MySQL allows acquiring the same named lock multiple times on the same connection. This is NOT true mutual exclusion.
86-
- **Lock lifetime:** Locks are tied to database connections. Closing a connection releases all associated locks.
87-
88-
### Database-Specific
89-
- **Key hashing:** PostgreSQL and Oracle convert string keys to 64-bit integers via BLAKE2b hash.
90-
- **PostgreSQL timeout:** Implemented through polling, may have ~1 second variance.
91-
- **Oracle lock ID range:** 0-1073741823 (uses `DBMS_LOCK.REQUEST`)
92-
- **Oracle Free limitation:** Oracle Database Free (23c/23ai) does NOT support `DBMS_LOCK.REQUEST`. This is a fundamental limitation of the Free/Express edition. CI skips Oracle tests; test locally with `docker compose -f db.docker-compose.yml up` which uses the official Oracle image.
93-
- **MSSQL driver:** Requires ODBC driver installation (`msodbcsql18` on Ubuntu)
94-
95-
### Resource Requirements
96-
- **MSSQL container:** Requires at least 2GB RAM
97-
- **Oracle container:** Requires at least 2GB RAM
98-
99-
### API Notes
100-
- `contextual_timeout` parameter ONLY affects `with` statements, not direct `acquire()` calls
101-
- Async lock classes are separate (`*AsyncSadLock`) but defined in same modules as sync variants
102-
103-
## Environment
104-
105-
**Python:** 3.9+ (CI tests 3.10-3.14)
106-
107-
**Testing Databases:** Optional Docker services in [db.docker-compose.yml](db.docker-compose.yml)
108-
- MySQL: `mysql://test:test@127.0.0.1:3306/test`
109-
- PostgreSQL: `postgresql://postgres:test@127.0.0.1:5432/`
110-
- MSSQL: `mssql+pyodbc://sa:YourStrongPassword123@127.0.0.1:1433/master`
111-
- Oracle: `oracle+oracledb://sys:YourStrong@Passw0rd@127.0.0.1:1521/?service_name=FREEPDB1`
112-
113-
**Environment Variables:** Test URLs can be set via `TEST_URLS` and `TEST_ASYNC_URLS`, or loaded from `tests/.env`
114-
115-
## Code Style
116-
117-
- **Formatter:** Ruff (line length: 128)
118-
- **Linter:** Ruff with import sorting (`I`)
119-
- **Type checker:** mypy
120-
- **Source layout:** `src/` directory with setuptools
121-
- **Config:** [.ruff.toml](.ruff.toml), [.mypy.ini](.mypy.ini)
122-
123-
## Workflow
124-
125-
When adding support for a new database:
126-
1. Create lock implementation in [lock/](src/sqlalchemy_dlock/lock/) inheriting from `BaseSadLock` / `BaseAsyncSadLock`
127-
2. Create SQL templates in [statement/](src/sqlalchemy_dlock/statement/)
128-
3. Add entry to `REGISTRY` and `ASYNCIO_REGISTRY` in [registry.py](src/sqlalchemy_dlock/registry.py)
129-
4. Add tests in [tests/](tests/) and [tests/asyncio/](tests/asyncio/)
130-
5. Update [db.docker-compose.yml](db.docker-compose.yml) if testing locally
1+
See: (AGENTS.md)[AGENTS.md]

0 commit comments

Comments
 (0)