Package: postgresql-lock
postgresql-lockLock mechanism implemented with PostgreSQL advisory locks.Easily implement distributed database locking.Installpipinstallpostgresql-lockSupported database interfacesasyncpgasynchronouspsycopg2synchronouspsycopg3asynchronoussynchronoussqlalchemy(supports version 1 & 2; can use any underlying database interface)asynchronoussynchronousWhy would I use this?PostgreSQL table locks aren't sufficient for your use-casePostgreSQL row locks don't work onINSERTYou want to prevent race conditions betweenINSERTandUPDATEon the same primary keyNone of the aforementioned details fit your use-case, but you have PostgreSQL installed and need to prevent race conditions in a distributed systemDefault operationBy defaultpostgresql-lockwill usesessionlock scope inblockingmode withrollback_on_errorenabled. Thesessionlock scope means only a single database connection can
acquire the lock at a time.UsageAll work revolves around theLockclass.The easiest way to useLockis withwithorasync withstatements. The lock will be
released automatically. Ifrollback_on_erroris enabled (default), rollbacks are automatically
handled prior to release.Usingwithandasync withimplies blocking mode.frompostgresql_lockimportLock# setup connectionconn=...# create and use lockwithLock(conn,"shared-identifier"):print("Acquired lock!")# do something hereNow compare the above example to the equivalent try/finally example below:frompostgresql_lockimportLock# setup connectionconn=...# create locklock=Lock(conn,"shared-identifier")try:# acquire locklock.acquire()print("Acquired lock!")try:# do something herepassexceptExceptionasexc:# handle_error() will rollback the transaction by defaultlock.handle_error(exc)raiseexcfinally:# release lock (this is safe to run even if the lock has not been acquired)lock.release()Asynchronous usage (withoutasync with)frompostgresql_lockimportLock# setup connectionconn=...# create locklock=Lock(conn,"shared-identifier")try:# acquire lockawaitlock.acquire_async()print("Acquired lock!")try:# do something herepassexceptExceptionasexc:# handle_error_async() will rollback the transaction by defaultawaitlock.handle_error_async(exc)raiseexcfinally:# release lock (this is safe to run even if the lock has not been acquired)awaitlock.release_async()Non-blocking mode (supports async as well)frompostgresql_lockimportLock# setup connectionconn=...# create locklock=Lock(conn,"shared-identifier")# acquire lockiflock.acquire(block=False):# do something herepasselse:# could not acquire lockpass# release lock (this is safe to run even if the lock has not been acquired)lock.release()Specify the database interface manuallyfrompostgresql_lockimportLock# setup connectionconn=...# create and use locklock=Lock(conn,"shared-identifier",interface="asyncpg")# do things with the lockHandle rollbacks manuallyfrompostgresql_lockimportLock# setup connectionconn=...# create and use locklock=Lock(conn,"shared-identifier",rollback_on_error=False)# do things with the lockChangelog0.1.5Rename package from postgres-lock to postgresql-lock0.1.4Add py.typed for mypy0.1.3Key can be any object0.1.2Add Lock.rollback_on_error (default true)Add Lock.handle_error() & Lock.handle_error_async()0.1.1Key can be str or int