Потеря записей DuckDB при выходе из Flask

Я только что закончил миграцию своего приложения Flask с Postgres на DuckDB, и почти все работает отлично. Однако есть одна очень странная проблема, из-за которой я часами бился головой о стену и, похоже, не мог в этом разобраться.

В одном из моих маршрутов Flask происходят различные вещи, связанные с материалом, который загружает пользователь. В ходе обработки файлы проходят через приложение, которое обновляет несколько разных таблиц в базе данных. В приложении все выглядит на 100% нормально и правильно, но когда я выхожу из Flask, последние три обновления таблицы просто исчезают. Данный маршрут Flask действительно длинный и сложный, но до определенного момента все работает идеально, как и раньше…

   ** whole bunch of stuff happens above here **
    
    try:    
        db.session.commit()
    except Exception as e:
        db.session.rollback()

    
    db.session.add(new_audit_history_entry)
    db.session.add(new_audit_log_entry)


    try:
        db.session.commit()
    except Exception as e:
        db.session.rollback()

    update_task = TaskQueue.query.get(task_id)
    update_task.completed_at = datetime.now()
    update_task.status = 'Complete'

    try:
        db.session.commit()
    except Exception as e:
        db.session.rollback()
    finally:
        audit_history = AuditHistory.query.all()
        audit_logs = AuditLog.query.all()
        tasks = TaskQueue.query.all()
        db.session.flush()
        db.session.close()

        return jsonify({'message': 'Files saved and validated!', 'This many files: ': len(files)})

У меня нет никаких ошибок, и в приложении все выглядит нормально — я вижу все, что ожидал увидеть. Все хорошо. Но если я выйду из Flask (ctl+c в CLI), все, начиная с db.session.add(new_audit_history_entry), просто исчезнет. Записи в таблицах Audit_history и Audit_log исчезают, а таблица TaskQueue вернулась к тому состоянию, которое было выше по маршруту при последнем обновлении (до окончательной фиксации этой таблицы, показанной выше).

Единственный намек, который я видел, это то, что когда я запускаю резервную копию Flask, я получаю следующее:

Exception in WAL playback: Violates foreign key constraint because key "auditid: 95693563-b229-40d7-9b12-5c4447bdb601" does not exist in the referenced table

Получается, что эти окончательные коммиты не сохраняются полностью? Они сохраняются только в логике упреждающей записи Дака или что-то в этом роде? Я перепробовал все возможные комбинации коммитов и сбросов, чтобы принудительно внести эти окончательные обновления в базу данных и без проблем.

Чтобы внести ясность, я не прерываю приложение во время записи или чего-то еще, я выхожу и перезапускаю его после того, как эти записи завершатся. Как будто я убежден, что могу запустить приложение в течение дня, и ни одна из этих конкретных записей не сохранится после выхода. Кроме того, я элегантно обрабатываю прерывание клавиатуры в своем приложении, включая окончательную фиксацию базы данных на всякий случай.

Что мне не хватает? Есть ли что-то особенное в утке, что мне нужно сделать?

Из любопытства: какой диалект/драйвер вы используете для интеграции DuckDB/SQLAlchemy?

snakecharmerb 24.04.2024 10:49

@snakecharmerb Я предполагаю, что они используют github.com/Mause/duckdb_engine (автор — разработчик Duckdb)

jqurious 24.04.2024 14:20

@jqurious Я догадался о том же, просто хотел подтверждения.

snakecharmerb 24.04.2024 15:04

да, это тот самый

dongle 24.04.2024 16:11
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
127
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Господи, наконец-то разобрался. Оказывается, .commit() и .close() не запускают автоматически синхронизацию файла WAL со статическим файлом БД.

Вам нужно вызвать CHECKPOINT (документация здесь), чтобы принудительно выполнить синхронизацию. API Python не имеет для этого ничего встроенного, поэтому вам нужно выполнить его как SQL. В моем случае, используя SQLalchemy, я добавил db.engine.execute('CHECKPOINT;') к своей функции «изящного выхода»:

def handler(signal_received, frame):
    with app.app_context():
        print('SIGINT or CTRL-C detected. Gracefully exiting')
        # Force a checkpoint in DuckDB to synchronize WAL with the main database
        try:
            db.engine.execute('CHECKPOINT;')
            print("Database CHECKPOINT completed.")
        except Exception as e:
            print(f"Failed to execute CHECKPOINT: {e}")
        db.session.commit()
        db.session.close()
        exit(0)

И это сработало для меня.

Другие вопросы по теме