Package: django-pglock
django-pglockdjango-pglockperforms advisory locks, table locks, and helps manage blocking locks.
Here's some of the functionality at a glance:pglock.advisoryfor application-level locking, for example, ensuring that tasks don't overlap.pglock.modelfor locking an entire model.pglock.timeoutfor dynamically setting the timeout to acquire a lock.pglock.prioritizeto kill blocking locks for critical code, such as migrations.ThePGLockandBlockedPGLockmodels for querying active and blocked locks.Thepglockmanagement command that wraps the models and provides other utilities.QuickstartAdvisory LocksUsepglock.advisoryto acquire aPostgres advisory lock:importpglockwithpglock.advisory("my_lock_id"):# This code blocks until the "my_lock_id" lock is availableAbove our code will block until the lock is available, meaning no instances of the function will run simultaneously. Use thetimeoutargument to configure how long to wait for the lock. A timeout of zero will return immediately:withpglock.advisory("my_lock_id",timeout=0)asacquired:ifacquired:# The lock is acquiredUseside_effect=pglock.Raiseto raise adjango.db.utils.OperationalErrorif the lock can't be acquired. When using the decorator, you can also useside_effect=pglock.Skipto skip the function if the lock can't be acquired:@pglock.advisory(timeout=0,side_effect=pglock.Skip)defnon_overlapping_func():# This function will not run if there's another one already running.# The decorator lock ID defaults to <module_name>.<function_name>Model Lockspglock.modelcan take a lock on an entire model during a transaction. For example:fromdjango.dbimporttransactionimportpglockwithtransaction.atomic():pglock.model("auth.User")# Any operations on auth.User will be exclusive here. Even read access# for other transactions is blockedpglock.modelusesPostgres's LOCK statement, and it accepts the lock mode as a argument. See thePostgres docs for more information.Notepglock.modelis similar topglock.advisory. Use thetimeoutargument to avoid waiting for locks, and supply the appropriateside_effectto adjust runtime behavior.Prioritizing Blocked Codepglock.prioritizewill terminate any locks blocking the wrapped code:importpglock@pglock.prioritize()defmy_func():# Any other statements that have conflicting locks will be killed on a# periodic interval.MyModel.objects.update(val="value")pglock.prioritizeis useful for prioritizing code, such as migrations, to avoid situations where locks are held for too long.Setting the Lock TimeoutUsepglock.timeoutto dynamically setPostgres's lock_timeout runtime setting:importpglock@pglock.timeout(1)defdo_stuff():# This function will throw an exception if any code takes longer than# one second to acquire a lockQuerying LocksUsepglock.models.PGLockto query active locks. It wrapsPostgres's pg_locks view. Usepglock.models.BlockedPGLockto query locks and join the activity that's blocking them.Usepython manage.py pglockto view and kill locks from the command line. It has several options for dynamic filters and re-usable configuration.Compatibilitydjango-pglockis compatible with Python 3.8 - 3.12, Django 3.2 - 5.0, Psycopg 2 - 3, and Postgres 12 - 16.DocumentationView the django-pglock docs hereto learn more about:Using advisory locks.Locking models.Setting dynamic lock timeouts.Killing blocking locks.The proxy models and custom queryset methods.Using and configuring the management command.InstallationInstalldjango-pglockwith:pip3 install django-pglockAfter this, add bothpgactivityandpglockto theINSTALLED_APPSsetting of your Django project.Contributing GuideFor information on setting up django-pglock for development and contributing changes, viewCONTRIBUTING.md.Primary AuthorsWes KendallPaul Gilmartin