django-pglock

Postgres advisory locks, table locks, and blocking-lock management for Django, with queryable lock models and a management command.

Library
PyPI
v1.8.0
70stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
29/100Needs Attention
Development Activity0
Maintenance20
Community24
Maturity52
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
87/100Excellent
Architecture78
Code Quality92
Innovation78
Learning Curve100

django-pglock gives Django applications direct, ergonomic access to Postgres’s locking primitives. pglock.advisory wraps pg_advisory_lock and its variants as a context manager or decorator so overlapping tasks (cron jobs, Celery tasks, management commands) can be serialized without a race condition, while pglock.model issues an explicit LOCK TABLE statement inside a transaction to exclusively lock an entire model during sensitive operations like backfills or migrations.

Beyond acquiring locks, the library exposes what’s already locked: PGLock and BlockedPGLock are unmanaged Django models built on custom SQL CTEs over pg_locks and pg_blocking_pids, so blocking activity can be queried, filtered, and joined with the ORM instead of hand-written SQL. pglock.prioritize takes this further with a background thread that periodically finds and terminates (or cancels) any queries blocking a critical section — useful for keeping migrations from getting stuck behind long-running transactions. A pglock management command and a PGLOCK_CONFIGS settings-driven config system expose the same querysets from the command line for ops and debugging.

What You Get

  • pglock.advisory — advisory locks usable as a context manager or function decorator, with shared/exclusive, transactional, and timeout variants
  • pglock.model — an explicit LOCK TABLE helper for taking an exclusive (or configurable-mode) lock on one or more Django models inside a transaction
  • pglock.timeout — a context manager/decorator for dynamically setting Postgres’s lock_timeout runtime parameter, nestable and restorable
  • pglock.prioritize — a background worker that periodically terminates or cancels queries blocking a wrapped critical section
  • PGLock and BlockedPGLock — unmanaged Django models/querysets that expose pg_locks and blocking-PID relationships for direct querying
  • A pglock management command plus PGLOCK_CONFIGS settings for inspecting, filtering, and killing locks from the CLI

Common Use Cases

  • Preventing overlapping execution of scheduled tasks or management commands using pglock.advisory as a decorator
  • Safely running large data backfills or schema changes without other transactions reading half-migrated rows, via pglock.model
  • Keeping Django migrations from stalling indefinitely behind a long-running query by wrapping them in pglock.prioritize
  • Building an internal ops dashboard or CLI workflow that lists and kills blocking queries using the PGLock/BlockedPGLock querysets

Under The Hood

Architecture The library is organized around a single core module, pglock/core.py, that wraps raw SQL lock-acquisition statements (pg_advisory_lock variants and LOCK TABLE) in context-manager/decorator classes (advisory, model, lock_timeout, prioritize) built directly on Django’s connections API and transaction machinery, with savepoint handling to avoid leaving transactions in an errored state on failed non-raising acquisitions. pglock/models.py layers on top of django-pgactivity’s PGTable/PGTableQuerySet machinery, overriding its SQL compiler (PGTableQueryCompiler.get_ctes) to inject two custom CTEs — _pglock_lock_cte over pg_locks/pg_class and _pglock_blocked_lock_cte using pg_blocking_pids — so lock and blocking-lock data can be queried as ordinary (unmanaged) Django models. pglock/management/commands/pglock.py is a thin BaseCommand that reuses the same querysets, driven by a small config resolver (pglock/config.py) that reads settings.PGLOCK_CONFIGS and merges CLI overrides.

Tech Stack Built for Python 3.10–3.14 and Django 4.2 through 6.0, with a hard dependency on django-pgactivity (>=1.2,<2) for the underlying activity/CTE plumbing and dynamic support for both psycopg2 and psycopg3 (detected at import time via utils.psycopg_maj_version). Packaging uses Poetry with poetry-core; the CI matrix (CircleCI, via a shared ambitioneng orb) runs the full test suite against Postgres 14 through 18 in service containers. Docs are built with MkDocs Material and mkdocstrings-python; linting uses Ruff (E, F, B, I, G, C4) plus Pyright in standard mode with django-stubs for Django-aware typing.

Code Quality The test suite (pglock/tests/, over 550 lines in test_core.py alone, plus dedicated files for config, the management command, and models) runs under pytest-django with --reuse-db, and pyproject.toml enforces fail_under = 100 branch coverage via pytest-cov. The public API ships a py.typed marker and is fully type-annotated with explicit TypeVar/ParamSpec/TypeAlias usage; failure modes raise specific, documented exceptions (ValueError, TypeError, RuntimeError, django.db.utils.OperationalError) rather than swallowing errors. Both Ruff and Pyright run in CI, and the CircleCI pipeline exercises the minimum and maximum supported Postgres versions.

What Makes It Unique Most Postgres-locking helpers for Django stop at exposing pg_advisory_lock as a context manager. django-pglock goes further on two fronts: it turns pg_locks and pg_blocking_pids into genuinely queryable Django querysets via custom SQL CTEs injected through a compiler override, and pglock.prioritize runs an active background thread that periodically detects and kills whatever is blocking a wrapped critical section — an automated blocking-lock mitigation pattern (particularly aimed at migrations) that’s uncommon in comparable libraries, which typically only document the problem rather than actively resolving it at runtime.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search