Я только что закончил миграцию своего приложения 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
Получается, что эти окончательные коммиты не сохраняются полностью? Они сохраняются только в логике упреждающей записи Дака или что-то в этом роде? Я перепробовал все возможные комбинации коммитов и сбросов, чтобы принудительно внести эти окончательные обновления в базу данных и без проблем.
Чтобы внести ясность, я не прерываю приложение во время записи или чего-то еще, я выхожу и перезапускаю его после того, как эти записи завершатся. Как будто я убежден, что могу запустить приложение в течение дня, и ни одна из этих конкретных записей не сохранится после выхода. Кроме того, я элегантно обрабатываю прерывание клавиатуры в своем приложении, включая окончательную фиксацию базы данных на всякий случай.
Что мне не хватает? Есть ли что-то особенное в утке, что мне нужно сделать?
@snakecharmerb Я предполагаю, что они используют github.com/Mause/duckdb_engine (автор — разработчик Duckdb)
@jqurious Я догадался о том же, просто хотел подтверждения.
да, это тот самый






Господи, наконец-то разобрался. Оказывается, .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)
И это сработало для меня.
Из любопытства: какой диалект/драйвер вы используете для интеграции DuckDB/SQLAlchemy?