Document unreconciled DM media expiry after SQL failure

This commit is contained in:
evgeny committed 2026-10-26 17:42:45 +01:00
1 parent 3dc5eb502d
commit dda4a780e3
3 files changed
+77 -1

No files matched your search

+60
View File
@@ -380,3 +380,63 @@ burst1/1,load8/8 PASS,134217728байт,exit0,175с. Строгие273 JSON/9452
Санитайзеры после rebase не повторялись:DM runtime не менялся,общий call_audio.h
в upstream изменяет только комментарий;новые платформенные Android callers этим
DM-профилем не проверяются. Upstream задачи/отчёты Android mute сохранены целиком.
10. Продолжение mailbox: SQL-ошибка оставляет завершённое TTL-назначение активным
База продолжения:3dc5eb502db6964fc6467385cc65db9d1df83c44,рабочее дерево было чистым.
Повторно сверены dm_mailbox.c/.h,dm_mailbox_media.c/.h и относящиеся к квитанциям,
контролю и TTL функции dm_media. Приём PUT проверяет точное тело,роль S и общую
группу;квитанция текста и удаление pending объединены транзакцией. Это чтение
соответствующих путей,не полная проверка всех SQL/allocator/платформенных отказов.
Подтверждён новый дефект:dm_mailbox_media_recv в EXPIRED сначала делает
UPDATE dm_media_holders SET released=1,затем отдельным statement обновляет job:
holder=0,stored=0,expired=1,retry_at=0. При отказе второго statement первое изменение
остаётся в БД. Таймер при недоступном B видит прежние holder и stored=1,поэтому
не выбирает нового M,не повторяет STORE и не сверяет expires с текущим временем.
На M custody уже удалена;dm_custody_done хранит tombstone,но таймер эту таблицу
не сканирует и не повторяет уведомление EXPIRED сам по себе.
Стенд:/tmp/dm-expiry-audit.c/.mk — отдельный вариант штатного test_dm_media.
Настоящие4 ядра A/B/S/M с подписанными мемберскими/административными блоками,
разными SQLite,реальными UDP/BGP/file_transfer и прежними проверками доставки.
Штатные quota-fault проверки идут в обоих прогонах одинаково;новая инъекция только
на S и только на UPDATE завершения EXPIRED,однократно SQLITE_IOERR в sqlite3_step.
Рабочие C,штатные тесты,объекты и libutun не подменялись;данные только в/tmp.
Оба прогона сначала проходят прямую доставку,offline custody и восстановление
S/M с выключенным A,побайтную доставку B и удаление промежуточной копии.
Затем B снова выключен;для третьего файла настроен TTL1с на M. M успевает принять
98385байт ciphertext,подтвердить хранение,по TTL удалить custody и послать EXPIRED.
Контроль без новой инъекции:phase9 PASS,exit0,allocations0->0. Для seq3 на S
holder0,stored0,expired1;назначение released1. На M нет custody,есть expired tombstone.
Fault:UPDATE завершается rc10,hit1,ошибка логируется вместе с точным SQL. После
ещё16 календарных секунд тестовых часов и нескольких успешных повторов PUT:
на S holder723190674248656275,stored1,expired0,expires1793032708,receiptNULL,body сохранён;
для того же UUID E20AC64A2660882CE73D68D48E2AFFA0 назначение этого M released1.
На M custody0,expired tombstone1. Phase8 FAIL,exit1,allocations0->0.
В runtime released=2 считает также прежнее успешно доставленное назначение;
независимый readonly SQL JOIN отдельно подтвердил released1 для проверяемого UUID.
Остановка/освобождение owners штатные,не маскируют сохранённое расхождение.
Окно отсутствия восстановления16с проверено исполнением;отсутствие пути восстановления
при продолжающемся offline B следует также из ветки таймера. Restart после этой
инъекции,возврат B и дополнительный storage не запускались;потеря до TTL не заявляется.
Это нарушение согласованности durable состояния после SQL-отказа,а не ошибка самого
удаления по TTL:копия M удалена в предусмотренный срок.
Направление исправления:идемпотентный восстанавливаемый переход EXPIRED с согласованным
состоянием job/holder;подтверждённое назначение проверять таймером по сохранённому expires.
Одного BEGIN/COMMIT вокруг двух UPDATE недостаточно:при rollback событие уже потреблено,
а M не повторит EXPIRED после удаления custody. Нужны отказные проверки обоих statement,
commit и восстановления после restart;поздний STORED не должен отменить завершение.
Сходная последовательность REJECT/DELETE_ACK замечена при чтении,но отдельный runtime
её отказов в этом продолжении не выполнен. Конкретная реализация пока не менялась.
Сборка отдельного стенда PASS. Логи:/tmp/dm-expiry-audit-{build,control,fault,validation}.log;
полные журналы сохранены,DM-выборки без транспортного шума — *-control-dm.log/*-fault-dm.log.
Прежний check205/санитайзеры относятся к исправлению UTF-8 и не покрывали этот отказ.
Новые fullcheck/sanitizer/GUI/Android/Windows/FreeBSD проверки не выполнялись.
По AGENTS.md:10 дальнейший аудит остановлен;задача открыта в doc/tasks.md.
В продолжении только комментарий контракта и отчёт/задача;частичные отметки сохранены.
+14
View File
@@ -4,6 +4,20 @@
## Текущие задачи
[ ] **DM media: восстанавливать завершение TTL после SQL-отказа при EXPIRED** — подтверждено 26.10.2026.
На S обработчик сначала сохраняет released=1, затем отдельно сбрасывает holder/stored и ставит expired=1.
Однократный SQLITE_IOERR второго UPDATE оставляет stored=1,holder=M,expired=0 при уже удалённом файле M.
Настоящие A/B/S/M, UDP/file_transfer и TTL1с:контроль phase9 PASS;отказ phase8 FAIL после16с повторов,
allocations0->0 в обоих прогонах. Независимое readonly чтение обеих БД подтвердило точный UUID/состояния.
Существующий таймер при offline B не пересматривает stored по expires и не повторяет STORE;
M уже удалил custody и сохранил tombstone, поэтому повтор EXPIRED от его таймера не возникает.
Нужен идемпотентный восстанавливаемый переход и сверка подтверждённых назначений по сохранённому expires;
одной транзакции недостаточно, если EXPIRED уже потреблён и последующее событие не придёт.
Добавить отказы обоих UPDATE/commit, повтор/restart, поздний STORED и корректное удаление без ложных квитанций.
Стенд /tmp/dm-expiry-audit.c/.mk;логи /tmp/dm-expiry-audit-{build,control,fault,validation}.log.
Подробности: [dm_audit_findings.txt](dm_audit_findings.txt),раздел10. По AGENTS.md:10 аудит остановлен;
runtime и штатные тесты не менялись, остальные отказные ветки REJECT/DELETE_ACK ещё не проверены отдельно.
[+] **DM: сохранять границы UTF-8 при усечении имени собеседника** — исправлено 26.10.2026.
Единый предел DM_PEER_NAME_MAX=63 байта; создание, загрузка, входящее автосоздание и JSON
копируют только целые code points. Повреждённый суффикс отбрасывается с WARN и точным смещением.
+3 -1
View File
@@ -24,7 +24,9 @@ int dm_mailbox_media_register(struct dm_mb_media* media, uint64_t group, uint64_
/* После проверки router-подписи и размера команды в dm_media: group/source — доверенные поля router.
* body заимствован на время вызова; RECEIPT=152, STORED=24, DELETE_ACK/EXPIRED=16, REJECT=17 байт.
* STORED/REJECT/ACK относятся к сохранённому назначению M; RECEIPT проверяется отдельно подписью B.
* SQL-ошибки оставляют задачу для повторов; отдельные обновления здесь не объединены в транзакцию. */
* SQL-ошибки логируются; отдельные обновления здесь не объединены в транзакцию.
* Известный дефект: отказ сброса job после EXPIRED оставляет stored/holder при released=1;
* пока B offline, таймер не восстанавливает переход. См. doc/dm_audit_findings.txt, раздел10. */
void dm_mailbox_media_recv(struct dm_mb_media* media, uint64_t group, uint64_t source,
uint8_t cmd, const uint8_t* body, size_t len);
/* Проверить 152-байтовую квитанцию и попытаться отправить доступным S общей группы.