perf(xmpp): fix slow MAM/MUC-MAM and move OMEMO decryption off the main
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

thread

- Replace O(n^2) origin-id/stanza-id lookups in AppModel with hash
  indexes
  and stop rebuilding the whole index on every unseen stanza-id, so
  archive
  application no longer grows quadratically with history size.
- Run OMEMO decryption on a serial background queue instead of the main
  actor, and decrypt archived stanzas in small batches with a shorter
  yield,
  keeping the UI responsive during large archive catch-ups.
- Show the "Синхронизация истории…" banner only for the one-page
  bootstrap
  window; incremental backlog catch-up continues silently in the
  background.
- Fix MUC-MAM: publish decoded group mutations at the end of room
  catch-up;
  they were previously accumulated and then silently dropped.
- Retry a failed catch-up pass automatically (bounded) and lengthen the
  query/apply timeouts so a single slow page no longer leaves history
  unloaded until the next app activation.
This commit is contained in:
wt
2026-08-17 15:32:05 +07:00
parent dee7e94e92
commit 9b56d2ebcf
8 changed files with 329 additions and 167 deletions
+15 -9
View File
@@ -39,7 +39,9 @@ fallback на plaintext. UI сразу показывает optimistic message
Входящая stanza может прийти напрямую, через carbons или MAM. Сервис определяет
peer, расшифровывает payload, использует `origin-id`/stanza id для дедупликации и
передаёт value-type envelope в `AppModel`.
передаёт value-type envelope в `AppModel`. Дедупликация выполняется по
хеш-индексам (`origin-id`, `stanza-id`, `id`), поэтому стоимость применения пачки
не растёт квадратично с размером архива.
Свежая установка начинает MAM с ограниченной последней страницы RSM, а не с
самого старого сообщения. Инкрементальные проходы используют сохранённый
@@ -48,14 +50,18 @@ peer, расшифровывает payload, использует `origin-id`/sta
Каждая stanza проходит проверку `queryid` и источника личного архива. Martin
публикует MAM-результаты на parser queue: ограниченный inbox принимает там всю
страницу и делает единственный hand-off на main actor после финального IQ.
Дешифрование выполняется по одной stanza с уступкой event loop, а декодированные
изменения становятся видимыми только одной атомарной пачкой вместе с новой
контрольной точкой. Один foreground-проход имеет конечный бюджет страниц и
времени; ошибка страницы не перезапускает весь архив сама. При уходе приложения
с экрана и на время записи/отправки видеосообщения активный MAM-запрос
закрывается, чтобы дешифрование истории не конкурировало с камерой и upload.
Только после commit `AppModel` обновляет SwiftUI, локальный snapshot и Apple
Watch; незавершённый проход при timeout/disconnect отбрасывается целиком.
Расшифровка OMEMO выполняется на отдельной серийной фоновой очереди, поэтому
Signal-криптография не блокирует главный поток; построение envelope и публикация
остаются на main actor. Видимый индикатор «Синхронизация истории…» показывается
только на первичной странице, а инкрементальная догрузка backlog продолжается в
фоне молча, публикуя декодированные изменения атомарными пачками по мере
готовности страниц. MUC-архив проходит тем же путём и флашит декодированные
групповые сообщения по завершении catch-up комнаты. Ошибка отдельной страницы
автоматически повторяется ограниченное число раз. При уходе приложения с экрана
и на время записи/отправки видеосообщения активный MAM-запрос закрывается, чтобы
дешифрование истории не конкурировало с камерой и upload. Только после commit
`AppModel` обновляет SwiftUI, локальный snapshot и Apple Watch; незавершённый
проход при timeout/disconnect отбрасывается целиком.
Редактирование реализовано стандартным XEP-0308: исправление получает новый id и
`<replace>` с id исходного сообщения. `AppModel` проверяет совпадение bare JID