meringue.core.db ¤
PgAdvisoryLock ¤
PgAdvisoryLock(
lock_id: int | str,
namespace_id: int | str | None = None,
using: str = "default",
)
PostgreSQL advisory lock for Django projects.
This utility provides a context manager and decorator for safely acquiring PostgreSQL advisory locks based on either a single integer key or a (namespace, key) pair.
supports both signatures
pg_advisory_lock(key)— if onlylock_idis providedpg_advisory_lock(namespace, key)— ifnamespace_idis also provided
Warning
If the database is not PostgreSQL, the lock will not be applied. A warning will be issued, but the function or block will still execute normally.
Warning
If you pass strings for lock_id or namespace_id, they will be hashed to 32-bit signed ints using CRC32. Locks persist until manually released or until the DB session ends.
Examples:
Parameters:
-
lock_id(int | str) –Unique identifier for the resource to be locked. Strings are hashed to a 32-bit signed integer.
-
namespace_id(int | str | None, default:None) –Optional namespace (e.g. table name or resource type) to avoid key collisions. Also hashed if passed as a string.
-
using(str, default:'default') –Django database alias.
Source code in meringue/core/db.py
def __init__(
self,
lock_id: int | str,
namespace_id: int | str | None = None,
using: str = "default",
):
"""
Args:
lock_id: Unique identifier for the resource to be locked.
Strings are hashed to a 32-bit signed integer.
namespace_id: Optional namespace (e.g. table name or resource type)
to avoid key collisions. Also hashed if passed as a string.
using: Django database alias.
"""
self.lock_id = lock_id
self.namespace_id = namespace_id
self.db_name = using
self.is_postgresql = self._check_postgresql()
self.__stacklevel = 2
int_lock_id
cached
property
¤
int_lock_id: int
Returns the integer form of lock_id, applying CRC32 hash if it's a string.
Returns:
-
int–32-bit signed integer key for pg_advisory_lock.
int_namespace_id
cached
property
¤
int_namespace_id: int | None
Returns the integer form of namespace_id, applying CRC32 hash if it's a string.
Returns:
-
int | None–Signed 32-bit integer or None if namespace is not set.
lock ¤
Acquires the advisory lock.
Source code in meringue/core/db.py
def lock(self) -> None:
"""
Acquires the advisory lock.
"""
if not self.is_postgresql:
msg = "pg_advisory_lock is only supported with PostgreSQL databases."
warnings.warn(msg, UserWarning, stacklevel=self.__stacklevel)
return
if self.namespace_id is None:
sql = "SELECT pg_advisory_lock(%s)"
args = [self.int_lock_id]
else:
sql = "SELECT pg_advisory_lock(%s, %s)"
args = [self.int_namespace_id, self.int_lock_id]
with connections[self.db_name].cursor() as cursor:
cursor.execute(sql, args)
unlock ¤
Releases the advisory lock.
Source code in meringue/core/db.py
def unlock(self) -> None:
"""
Releases the advisory lock.
"""
if not self.is_postgresql:
return
if self.namespace_id is None:
sql = "SELECT pg_advisory_unlock(%s)"
args = [self.int_lock_id]
else:
sql = "SELECT pg_advisory_unlock(%s, %s)"
args = [self.int_namespace_id, self.int_lock_id]
with connections[self.db_name].cursor() as cursor:
cursor.execute(sql, args)
pg_advisory_xact_lock ¤
pg_advisory_xact_lock(
lock_id: int | str,
namespace_id: int | str | None = None,
using: str = "default",
) -> None
PostgreSQL transaction-level advisory lock for Django projects.
This function uses the same key handling as PgAdvisoryLock,
but calls PostgreSQL's pg_advisory_xact_lock functions. Transaction-level advisory locks are
released automatically at the end of the current transaction and cannot be released manually.
supports both signatures
pg_advisory_xact_lock(key)— if onlylock_idis providedpg_advisory_xact_lock(namespace, key)— ifnamespace_idis also provided
Warning
In Django autocommit mode this lock is released at the end of the SQL statement. Wrap usage
in transaction.atomic() when the lock must be held for a block of application code.
Examples:
from django.db import transaction
with transaction.atomic():
pg_advisory_xact_lock(12345, namespace_id="transfer")
...
Source code in meringue/core/db.py
def pg_advisory_xact_lock(
lock_id: int | str,
namespace_id: int | str | None = None,
using: str = "default",
) -> None:
"""
PostgreSQL transaction-level advisory lock for Django projects.
This function uses the same key handling as [PgAdvisoryLock][meringue.core.db.PgAdvisoryLock],
but calls PostgreSQL's `pg_advisory_xact_lock` functions. Transaction-level advisory locks are
released automatically at the end of the current transaction and cannot be released manually.
!!! info "supports both signatures"
- `pg_advisory_xact_lock(key)` — if only `lock_id` is provided
- `pg_advisory_xact_lock(namespace, key)` — if `namespace_id` is also provided
!!! warning
In Django autocommit mode this lock is released at the end of the SQL statement. Wrap usage
in `transaction.atomic()` when the lock must be held for a block of application code.
Examples:
```py
from django.db import transaction
with transaction.atomic():
pg_advisory_xact_lock(12345, namespace_id="transfer")
...
```
"""
if not _check_postgresql(using):
msg = "pg_advisory_xact_lock is only supported with PostgreSQL databases."
warnings.warn(msg, UserWarning, stacklevel=2)
return
connection = connections[using]
if connection.get_autocommit() and not connection.in_atomic_block:
msg = (
"pg_advisory_xact_lock is being used in autocommit mode. "
"Wrap usage in transaction.atomic() to hold the lock for application code."
)
warnings.warn(msg, UserWarning, stacklevel=2)
if namespace_id is None:
sql = "SELECT pg_advisory_xact_lock(%s)"
else:
sql = "SELECT pg_advisory_xact_lock(%s, %s)"
args = _advisory_lock_args(lock_id, namespace_id)
with connection.cursor() as cursor:
cursor.execute(sql, args)