Verify history migration results and checkpoint revision races
This commit is contained in:
1 parent
bf94b240c8
commit
750ccf638d
3 files changed
+105
-4
No files matched your search
@@ -1,6 +1,9 @@
|
||||
member_sync / merkle_sync — аудит контрактов, 26.10.2026
|
||||
|
||||
Текущий статус: дефекты удаления корректной истории и доставки db_sync исправлены
|
||||
Текущий статус: согласованный перенос истории на MESSAGES Merkle реализован (раздел13).
|
||||
ASAN/UBSan/LSan10/10 PASS; окончательный общий прогон после уточнения stream-фикстур идёт.
|
||||
Результаты прежних этапов ниже не являются проверкой нового переноса.
|
||||
Дефекты удаления корректной истории и доставки db_sync исправлены
|
||||
после rebase на03b05094 (раздел11). Общий аудит db_sync остаётся частичным.
|
||||
Новый check.sh:203/203, proxy9/9,burst1/1,load8/8,exit0,195с; STUN PASS отдельно и в общем наборе.
|
||||
ASAN/UBSan/LSan текущего исправления:8/8 PASS с detect_leaks=1.
|
||||
@@ -819,3 +822,85 @@ round7,stateSERVE,READY в очереди. Применение старых PAG
|
||||
логическое обоснование требует п.0 и согласованного индекса, а не опирается на неизменённый старый движок.
|
||||
новая модель и её тесты ещё не реализованы. Перенос нельзя объявлять проверенным текущими203/203
|
||||
и8/8: эти результаты относятся к уже выполненному исправлению раздела11.
|
||||
|
||||
13. Реализация MESSAGES, свежий handshake и ограниченные транзакции
|
||||
|
||||
Пользователь одобрил вариантБ раздела12, уточнив два обязательных условия:
|
||||
- Не ограничивать исторические сообщения по deleted автора: ограничение входа в группу отдельно.
|
||||
- Не фиксировать всю новую историю одной транзакцией: короткая локальная вставка/страница,
|
||||
сеть вне транзакции, первичная проверка и построение индекса ограниченными порциями.
|
||||
|
||||
Реализовано:
|
||||
- db_sync оставляет общую SQLite, подписанные записи, публичное API и PUSH/ACK(message_id).
|
||||
Собственный bulk-автомат, chain_hash, sparse-поиск и сетевой OFFSET удалены.
|
||||
MT_DATA_MESSAGES=2 использует общий merkle_sync; namespace=group_id, ключ=message_id[32].
|
||||
Канонический лист включает ключ/timestamp/author/длину/точное тело/signature и исключает local id,
|
||||
attrs/flags/доставку. Одинаковая подписанная история не зависит от локального порядка получения.
|
||||
- Wire v4:55-байтовый заголовок с двумя случайными128-битными round_id/accept_id.
|
||||
Старые PAGE/DONE/ERROR после cancel/start не проходят новый challenge; дубликаты BEGIN/READY
|
||||
не сбрасывают применение. Ошибка RNG обеих сторон даёт MT_ERR_IO до установки нового раунда.
|
||||
Полные корни и revision подтверждаются общими CHECK/DONE; равный count не означает успех.
|
||||
- Страница MESSAGES проверяет полный32-байтовый курсор, лист, строгий порядок и подписи до BEGIN.
|
||||
До16 записей и ограничение размера wire; данные/индекс/next_id фиксируются атомарно.
|
||||
Обычная вставка меняет один путь дерева в своей короткой транзакции.
|
||||
Preflight проверяет до16 строк вне write-lock, затем сверяет собственные байты в коротком commit.
|
||||
Второй этап строит до16 листьев/путей за commit; timer1 tick возвращает управление I/O между шагами.
|
||||
Число строк ограничено, но жёсткий временной предел commit не обещается: диск/наполнение листа влияют.
|
||||
- VALID/BAD_FORMAT/BAD_SIGNATURE/DEFERRED/ERROR сохранены. Удаляются только доказанно неверные
|
||||
сохранённые байты; SQL/OpenSSL/NOMEM/неизвестный ключ не удаляют историю и не публикуют пустой корень.
|
||||
Ошибка оставляет pending; поздний общий ключ, включая событие другого канала, возобновляет preflight.
|
||||
Ключ автора читается независимо от deleted; допуск пересылающего READY-пира проверяется отдельно.
|
||||
- Миграция схемы сохраняет подписанные строки и local_attrs/flags/счётчики доставки.
|
||||
Производные message_id/bucket вычисляются заново; ошибка DDL/copy/COMMIT откатывает перестройку.
|
||||
Подпись неправильной длины — доказанный неверный формат, корректной длины проверяется preflight.
|
||||
- Новые data_ops apply_page/can_run/on_result; узкая отмена dataset/ns и dataset/peer.
|
||||
MESSAGES сохраняет sound_on-разрешение спящего пира; остальные модели используют default sleep policy.
|
||||
Отзыв/возврат допуска и sleep/sound переоценивают сессии на прежнем UP, не затрагивая чужие datasets.
|
||||
Android retry сохраняет отменяемое ожидание активной фазы, inline callback отказа памяти не рекурсивен.
|
||||
- on_insert bulk отложен за пределы data_ops и видит committed страницу; rollback отменяет уведомления.
|
||||
Done получает только MT_OK, включая автоматические раунды; самоотписка, удаление следующей подписки
|
||||
и канала безопасны. Remove сохраняет таблицу, отменяя только её MESSAGES/таймеры/уведомления.
|
||||
- TTL исправлен на миллисекунды Unix и удаляет до16 собственных неподтверждённых строк за commit.
|
||||
Удаление и очистка MESSAGES-индекса атомарны; далее корень gated/pending до восстановления.
|
||||
Это уточнение реализации к плану: массовая очистка сбрасывает производный индекс вместо обновления
|
||||
каждого удалённого пути. Не создаёт tombstone; другая сохранённая копия может вернуться при обмене.
|
||||
|
||||
Проверки новой реализации:
|
||||
- test_db_sync:реальный ETCP/READY,2/3 узла, PUSH/ACK и случайные слияния;200 одинаковых записей,
|
||||
затем отличие первой при равном count и сдвиг local id на900. Итог201 на обоих, полный корень одинаков.
|
||||
- test_db_sync_integrity:11 введённых crypto/SQL/COMMIT/BLOB/timer отказов, реальные подписи/триггеры;
|
||||
сохранность хорошей копии, доказанная порча/формат0/1/63/65/255/4001, два канала, индекс/next_id rollback.
|
||||
Плотный лист40 записей, несколько страниц, вставка до курсора требует следующей сверки, replay без дублей.
|
||||
Отказ второй записи и COMMIT страницы откатывает все строки/корень/уведомления; on_insert только после commit.
|
||||
Подписи preflight проверяются при sqlite3_get_autocommit=1. Разные id/attrs/flags/доставка не меняют корень.
|
||||
История deleted автора выдаётся; поздний ключ через событие другого канала сохраняет строку и запускает индекс.
|
||||
TTL дренирует19 expired в двух коротких commit и оставляет fresh/ACKed. Tracked allocations восстановлены.
|
||||
- test_merkle_protocol:старые токены/фазы/курсоры, reattach, дубликаты BEGIN/READY, RNG обеих сторон,
|
||||
backpressure, timeout, apply/SQLite faults, модельный sleep-допуск и восстановление на том же соединении.
|
||||
- test_db_sync_schema:сохранность полей, повторный restart, rollback DDL после copy, очистка только доказанной порчи.
|
||||
- test_dm_invite:настоящий JOIN и UTUN_TEST_HISTORY_TIMEOUT=1; принудительно потерянный обмен30с
|
||||
завершается timeout, повтор восстанавливает историю, тест exit0.
|
||||
- Общий первый прогон199/203:две изолированные db_sync-фикстуры не создавали общий движок,
|
||||
purge-фикстура использовала недопущенного peer2; исправлены setup/teardown и реальный допущенный peer.
|
||||
Повтор202/203, STUN PASS:stream-сценарий зависел от standby предыдущих сценариев.
|
||||
DEBUG и SQL-диагностика показали новые версии только у автора и sleep=1 на центральном пиру;
|
||||
wake-only PASS. Тест явно восстанавливает activity центрального узла и пробуждает lifecycle-получателя
|
||||
после сохранённой проверки перехода forced-active → standby. Production stream не менялся.
|
||||
- ASAN/UBSan/LSan10/10 PASS,detect_leaks=1:db_sync,integrity,schema,merkle_protocol,merkle_sync,
|
||||
member_adapter,chat_feedback,chat_poll,chat_poll_network,dm_invite. Core/libuasync/Opus инструментированы
|
||||
согласованно в /tmp/utun-member-purge-sanitizers. Нативные/ASAN-объекты не смешивались.
|
||||
Отдельной Makefile-цели test_member_sync нет: исключена ошибочная попытка implicit build;
|
||||
соответствующие проверки входят в member_adapter. Новая sanitizer-сборка/запуск10 целей exit0.
|
||||
- Временная цель через tests/Makefile: /tmp/history-standby-check.c/.mk, макрос UTUN_HAVE_STANDBY,
|
||||
настоящий history-код/SQLite/uasync и управляемый standby callback. Inline OOM даёт один retry без
|
||||
рекурсии, wake освобождает ожидание, allocations восстановлены;exit0. Это не запуск Android-приложения.
|
||||
|
||||
Документы db_sync.h/db_sync_arch.md и merkle_sync.h/merkle_sync_doc.md приведены к реализации.
|
||||
Ограничения:индекс согласован при записи через модель; произвольная внешняя порча SQLite
|
||||
выявляется при соответствующем чтении/перепроверке, непрерывный scrub всех байтов не заявляется.
|
||||
Тесты на несколько тысяч записей, отдельные изменения строго между CHECK/DONE и платформенные
|
||||
GUI/Android/Windows/FreeBSD прогоны в этом этапе не выполнялись. Общий аудит чата частичный.
|
||||
Логи: /tmp/history-merkle-asan-results.log, /tmp/history-merkle-asan-test*.log,
|
||||
/tmp/history-merkle-final-test_db_sync_integrity.log, /tmp/history-merkle-final-test_merkle_protocol.log,
|
||||
/tmp/history-merkle-dm-timeout.log, /tmp/history-standby-check.log.
|
||||
Окончательный общий прогон /tmp/history-merkle-verified-check.log пока выполняется.
|
||||
@@ -209,10 +209,11 @@ int main(void) {
|
||||
uint32_t records_a = 0, records_b = 0;
|
||||
|
||||
uint64_t t_insert = get_time_tb();
|
||||
int large = getenv("UTUN_TEST_HISTORY_LARGE") != NULL;
|
||||
for (int b = 0; b < TOTAL_BATCHES && test_phase == 0; b++) {
|
||||
int n = rand() % (MAX_PER_BATCH + 1);
|
||||
int n = large ? 250 : rand() % (MAX_PER_BATCH + 1);
|
||||
if (n == 0) continue;
|
||||
int side = rand() & 1;
|
||||
int side = large ? b & 1 : rand() & 1;
|
||||
struct DB_SYNC_INSTANCE* si = side ? si_b : si_a;
|
||||
uint64_t node = side ? NODE_ID_B : NODE_ID_A;
|
||||
|
||||
|
||||
@@ -285,6 +285,21 @@ static void test_send_rejection(void) {
|
||||
t_destroy(&f);
|
||||
puts("PASS: transport rejection retains ownership; packet is freed exactly once");
|
||||
}
|
||||
static void test_checkpoint_revision(void) {
|
||||
for (int changed = 0; changed < 2; changed++) {
|
||||
struct fixture f; t_init(&f);
|
||||
struct ms_session* s = t_session(&f); s->state = MS_SERVE; s->waiting = 0;
|
||||
uint8_t root[32] = {0};
|
||||
t_receive(&f, MS_CHECK, 7, 0, root, sizeof(root));
|
||||
assert(s->tx && s->tx->dgram[9] == MS_DONE && !f.callbacks);
|
||||
if (changed) merkle_sync_changed(&f.inst, MT_DATA_MEMBERS, "1001");
|
||||
uasync_poll(f.inst.ua, 0);
|
||||
if (changed) assert(!f.callbacks && s->requests && s->revision != s->verified_revision);
|
||||
else assert(f.callbacks == 1 && f.result == MT_OK && !s->requests);
|
||||
t_destroy(&f);
|
||||
}
|
||||
puts("PASS: change between CHECK and DONE handoff retains the request for a new checkpoint");
|
||||
}
|
||||
|
||||
static void t_cancel_done(uint64_t peer, const char* ns, int result, void* arg) {
|
||||
struct fixture* f = arg;
|
||||
@@ -478,7 +493,7 @@ int main(void) {
|
||||
size_t baseline = u_get_allocated_count();
|
||||
test_malformed_and_old_round(); test_rejected_data(); test_sibling_walk(); test_cancel_waiters();
|
||||
test_page_size_and_cursor(); test_readonly_tree(); test_backpressure_and_disconnect(); test_peer_restart();
|
||||
test_send_rejection();
|
||||
test_send_rejection(); test_checkpoint_revision();
|
||||
test_error_handoff(); test_blocked_error_timeout(); test_pull_lifecycle();
|
||||
test_dataset_isolation(); test_full_cursor(); test_permission_rounds(); test_model_policy(); test_reattach_tokens(); test_handshake_failures();
|
||||
assert(u_get_allocated_count() == baseline);
|
||||
|
||||
Reference in new issue
Block a user