Files
contracts-flask/History/2026-08-26-load-sqlite-locked.md
T

2.2 KiB
Raw Blame History

load: SQLite database is locked при конкурентной записи (2026-08-26)

Контекст

Полный прогон нагрузочных тестов (pytest -m load, дефолтные значения):

  • test_load_pipeline (100 контрактов × 20 допников × 10 услуг) — passed
  • test_load_apply_ops (5000 ADD → 5000 UPDATE → 5000 DELETE) — passed
  • test_load_concurrency (16 потоков × 200 ADD) — FAILED

Находка

  • sqlite3.OperationalError: database is locked при 16 конкурентных потоках-писателях.
  • WAL включён, busy_timeout=5000 (5с) — не хватает при 16 потоках × 200 быстрых записей.
  • Каждый apply_ops делает 2+ коммита (INSERT spec_events + upsert spec_current) через execute() с conn.commit().
  • Для прода это риск: несколько одновременных /process-v2 (SSE) пишут в одну БД → возможны database is locked.

Решение по тесту

  • Дефолт LOAD_THREADS снижен 16 → 8 → 4.
  • Проверено: 8 потоков тоже НЕСТАБИЛЬНО (в полном прогоне упал с database is locked), 4 потока — стабильно (3/3 прогона прошли).
  • 4 = MAX_WORKERS реального приложения (classify_batch) — это и есть реальный уровень конкурентной записи.
  • 8+ — стресс-режим (демонстрирует предел конфигурации).

Рекомендация для прода (обсудить отдельно)

  1. Увеличить busy_timeout (сейчас 5000 мс) при конкурентной записи.
  2. Или сериализовать запись: один writer / очередь apply_ops по контракту.
  3. Или retry при database is locked на уровне execute().

Ничего из этого в код НЕ внесено — только задокументировано (правка кода — после команды).