База данных Django/Celery SQLite заблокирована при одновременном доступе

У меня есть локальный проект на Django 5.1/Celery 5.4, который использует SQLite. Я единственный пользователь.

Определенные сохранения модели запускают задачу Celery, которая запрашивает (SELECT) обновленную запись (используя Django ORM), затем запускает вызов API для обновления удаленной записи на основе локальных данных, а затем запускает другую UPDATE локально. Задача заключает все это в with transaction.atomic():.

(Celery worker настроен на последовательный запуск задач.)

Во время выполнения этой задачи любые попытки записи в базу данных приводят к ошибке "база данных заблокирована".

Я настроил Django/SQLite с последними настройками "готовности к работе":

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': DB_DIR / 'db.sqlite3',
        'OPTIONS': {
            'init_command': """
                PRAGMA foreign_keys=ON;
                PRAGMA journal_mode=WAL;
                PRAGMA synchronous=NORMAL;
                PRAGMA busy_timeout = 5000;
                PRAGMA temp_store = MEMORY;
                PRAGMA mmap_size=134217728;
                PRAGMA journal_size_limit=67108864;
                PRAGMA cache_size=2000;
            """,
            'transaction_mode': 'IMMEDIATE',
            'timeout': 20,
        },
    },
}

У меня сложилось впечатление, что при таких настройках возможен одновременный доступ. "SQLite in Production" - это последнее новшество, и эти настройки, особенно новые для Django 5.1 'transaction_mode': 'IMMEDIATE' в настройках, позволят записывать данные в очередь. Что я упускаю?

Хотя SQLite может обрабатывать некоторый параллелизм с WAL, он по-прежнему ограничен в многопроцессорных средах (таких как Django + Celery). Я бы посоветовал либо использовать отдельную базу данных для Celery, либо перейти на полноценную базу данных, такую как PostgreSQL.

Если в ваших задачах Celery не требуется выполнять локально сложные операции с SQL, вы можете попробовать использовать отдельную базу данных для Celery, прежде чем переходить на более удобную конфигурацию базы данных:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': DB_DIR / 'db.sqlite3',
        'OPTIONS': {...},  # Your app DB
    },
    'celery': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': DB_DIR / 'celery.sqlite3',
        'OPTIONS': {...},  # A dedicated instance for Celery
    },
}

А затем вызовите:

from django.db import connections
def your_task():
    with connections['celery'].cursor() as cursor:
        cursor.execute("YOUR QUERY")

Подробнее об ошибке “База данных заблокирована” в разделе Django docs

Решение в данном конкретном случае состояло в том, чтобы сократить время моей транзакции, т.е. не удерживать транзакцию при выполнении внешнего вызова API. Это означает, что я должен быть более осторожен и не позволять представлениям и задачам наступать друг другу на пятки.

Я все еще озадачен тем, что так называемые настройки "готовности к работе" не допускают одновременного доступа, даже без очереди + тайм-аута!

Вернуться на верх