Repository navigation
fix(procurement): ре-синк источника апсертом, а не «удалить и вставить» - #851
Merged
Merged
Conversation
Ночь 29.09.2026: синк SunWell/EKF свалился на первой же строке `sunwell#0`, оставшейся с предыдущего дня — `UNIQUE constraint failed: product_offers.source, product_offers.source_key`. Транзакция откатилась целиком, и 20 728 позиций крупнейшего поставщика сутки стояли непересчитанными; остальные пять прошли нормально. Разбор показал, что удаление само по себе исправно: `DELETE ... LIKE 'sunwell#%'` убирает все 20 728 строк и `sunwell#0` в той же транзакции после него не виден. Второго писателя в логах нет, отдельным процессом гонка не воспроизводится — ни одиночным прогоном, ни всеми шестью поставщиками подряд. Поэтому убран сам класс отказа, а не конкретный сценарий. - `replace_source_offers()` пишет апсертом (`ON CONFLICT (source, source_key) DO UPDATE`): оставшаяся строка и параллельная вставка того же ключа больше не конфликтуют, строка переписывается целиком — иначе у неё сохранились бы старые цена и остаток. - Устаревшие строки удаляются по метке времени прогона, а не списком ключей: `NOT IN (20 тыс. значений)` упёрся бы в лимит параметров SQLite. - INSERT идёт частями по 500 строк — 16 колонок × 500 = 8000 параметров, с запасом от лимита 32766. - Попутно исчезает окно, когда источник в поиске пуст: раньше между удалением и коммитом позиций поставщика в таблице не было. Проверено на проде на реальных 20 728 строках SunWell: два прогона подряд по существующим ключам — ровно то, что падало, — проходят оба. ## NEWS 🛠️ **Ночное обновление больше не спотыкается на крупном поставщике** Самый большой прайс — двадцать тысяч позиций — при ночном пересчёте иногда срывался, и цены по нему оставались вчерашними до следующей ночи. Причина была в том, как система перезаписывала данные: теперь она обновляет строки на месте, а не удаляет и создаёт заново. Побочный плюс: в момент обновления товары поставщика больше не исчезают из поиска ни на секунду. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ночь 29.09.2026: синк SunWell/EKF свалился на первой же строке
sunwell#0, оставшейся с предыдущего дня —UNIQUE constraint failed: product_offers.source, product_offers.source_key. Транзакция откатилась целиком, и 20 728 позиций крупнейшего поставщика сутки стояли непересчитанными; остальные пять поставщиков прошли нормально.Что установил разбор:
DELETE ... LIKE 'sunwell#%'убирает все 20 728 строк, иsunwell#0в той же транзакции после него не виден (проверено на проде);sunwell#0..20727, 20 728 уникальных, все попадают под шаблон удаления, дублей в батче нет;sync-suppliers, ни моих ручных юнитов той ночью;Гонка не доказана, а падение повторяемо и бьёт по самому большому прайсу — поэтому убран класс отказа, а не сценарий:
replace_source_offers()пишет апсертом (ON CONFLICT (source, source_key) DO UPDATE). Оставшаяся строка и параллельная вставка того же ключа больше не конфликтуют; строка переписывается целиком, иначе у неё сохранились бы старые цена и остаток.NOT IN (20 тыс. значений)упёрся бы в лимит параметров SQLite.NEWS
🛠️ Ночное обновление больше не спотыкается на крупном поставщике
Самый большой прайс — двадцать тысяч позиций — при ночном пересчёте иногда срывался, и цены по нему оставались вчерашними до следующей ночи. Причина была в том, как система перезаписывала данные: теперь она обновляет строки на месте, а не удаляет и создаёт заново. Побочный плюс: в момент обновления товары поставщика больше не исчезают из поиска ни на секунду.
Test plan
test_supplier_stock_unknown.py: повторный ре-синк по существующим ключам не падает (именно упавший сценарий), строка обновляется а не дублируется и подхватывает обнуление полей, пропавшая из выборки позиция уходит из поиска, прогон одного поставщика не вычищает соседнегоruff check .+ruff format --check .— чистоprocurement supplier sync: N офферов, ошибок у 0 поставщиков🤖 Generated with Claude Code