Skip to content

Module fl_server_core.utils.locked_atomic_transaction

View Source
# SPDX-FileCopyrightText: 2024 Benedikt Franke <benedikt.franke@dlr.de>
# SPDX-FileCopyrightText: 2024 Florian Heinrich <florian.heinrich@dlr.de>
#
# SPDX-License-Identifier: Apache-2.0

from contextlib import contextmanager
from django.db import transaction
from django.db.models import Model
from django.db.transaction import get_connection


@contextmanager
def locked_atomic_transaction(*models: Model):
    """
    Create a locked atomic transaction for a model.

    Opens an atomic transaction and locks a specific table until the context is closed.
    Original code taken from: <https://stackoverflow.com/a/54403001>

    Attention: "LOCK TABLE" is not available in SQLite, so this function will not work with SQLite.
               A possible fix would be to skip the "LOCK TABLE" part for SQLite since transactions in SQLite
               lock the whole database anyway. At least if "BEGIN IMMEDIATE TRANSACTION" or
               "BEGIN EXCLUSIVE TRANSACTION" is used. The default "BEGIN TRANSACTION" or "BEGIN DEFERRED TRANSACTION"
               does not lock the database until the first database access.

    Args:
        model (Model): database model
    """
    with transaction.atomic():
        tables = ", ".join(map(lambda model: model._meta.db_table, models))
        cursor = get_connection().cursor()
        cursor.execute(f"LOCK TABLE {tables}")
        try:
            yield
        finally:
            cursor.close()

Functions

locked_atomic_transaction

def locked_atomic_transaction(
    *models: django.db.models.base.Model
)

Create a locked atomic transaction for a model.

Opens an atomic transaction and locks a specific table until the context is closed. Original code taken from: https://stackoverflow.com/a/54403001

Attention: "LOCK TABLE" is not available in SQLite, so this function will not work with SQLite. A possible fix would be to skip the "LOCK TABLE" part for SQLite since transactions in SQLite lock the whole database anyway. At least if "BEGIN IMMEDIATE TRANSACTION" or "BEGIN EXCLUSIVE TRANSACTION" is used. The default "BEGIN TRANSACTION" or "BEGIN DEFERRED TRANSACTION" does not lock the database until the first database access.

Parameters:

Name Type Description Default
model Model database model None
View Source
@contextmanager
def locked_atomic_transaction(*models: Model):
    """
    Create a locked atomic transaction for a model.

    Opens an atomic transaction and locks a specific table until the context is closed.
    Original code taken from: <https://stackoverflow.com/a/54403001>

    Attention: "LOCK TABLE" is not available in SQLite, so this function will not work with SQLite.
               A possible fix would be to skip the "LOCK TABLE" part for SQLite since transactions in SQLite
               lock the whole database anyway. At least if "BEGIN IMMEDIATE TRANSACTION" or
               "BEGIN EXCLUSIVE TRANSACTION" is used. The default "BEGIN TRANSACTION" or "BEGIN DEFERRED TRANSACTION"
               does not lock the database until the first database access.

    Args:
        model (Model): database model
    """
    with transaction.atomic():
        tables = ", ".join(map(lambda model: model._meta.db_table, models))
        cursor = get_connection().cursor()
        cursor.execute(f"LOCK TABLE {tables}")
        try:
            yield
        finally:
            cursor.close()