iOS PWA: «Сохранить» застревает, редактор недоступен, продиктованный текст теряется

21 октября 2026 года при редактировании существующего поста в моем self-hosted инстансе Discourse возникла периодическая проблема. Композер застрял в состоянии Сохранение (Saving), и я не смог вернуться к редактору, чтобы восстановить продиктованный текст.

После этого я изучил логи доступа Caddy и nginx, а также историю ревизий постов в Discourse.

Окружение

  • Клиент: iPhone 16 Pro, PWA Safari на домашнем экране

  • iOS: 27.0.1

  • Discourse: 2026.10.0-latest +406 commits, как показано в админ-панели позже в тот же день (доступно 162 новых изменения)

  • Хостинг: self-hosted, частный учебный архив

  • Обратный прокси: Caddy v2.11.4, предоставляющий HTTP/3 поверх QUIC

  • Бэкенд: Discourse nginx за Caddy с использованием Unix сокета

  • Сеть: сначала университетский Wi-Fi, затем мобильная сеть EE

Что произошло

Я редактировал пост, содержащий изображение слайда физики.

Под изображением я продиктовал дополнительные математические заметки, включая дроби, и затем быстро нажал Сохранить правку (Save Edit).

Голубая горизонтальная полоса Сохранение оставалась видимой внизу экрана и не завершалась.

Я попытался коснуться полосы Сохранения, но это не открыло композер заново. В результате я не мог выделить или скопировать несохраненный текст в другое место.

Я ждал дольше, чем обычно.

Только после того, как операция сохранения уже зависла, я вручную отключил Wi-Fi, надеясь, что переключение на мобильную связь позволит запросу завершиться.

Интерфейс не восстановился сразу. Я также столкнулся с буферизацией при попытке навигации с помощью логотипа сайта.

Когда я снова смог редактировать пост, продиктованные дроби отсутствовали.

Серверное расследование

Соответствующее время составляло приблизительно 12:20 BST 21 октября 2026 года.

Мои логи Caddy показывают:

Время (BST) Запрос Результат
12:19:39 POST /drafts.json 200
12:19:40 POST /uploads.json 200
12:19:44 POST /drafts.json 200
12:20:08 PUT /posts/70695 Статус Caddy 0
12:20:55 PUT /posts/70695 200

Неудачный PUT использовал HTTP/3. Caddy зафиксировал длительность приблизительно 110 мс, тогда как nginx записал ответ HTTP 400 без выполнения контроллера обновления поста в Discourse.

Множество других запросов, исходящих из мобильного соединения, также получили статус Caddy 0 примерно в то же время.

Сам Caddy не перезапускался. Docker сообщает о нулевом количестве перезапусков с 15 октября, а в операционных логах нет соответствующих событий перезагрузки или завершения работы.

Восстановление черновика

Незадолго до инцидента были успешные запросы на сохранение черновика.

Однако я помню предыдущее содержимое как слайд с изображением без дополнительных продиктованных текстов. Я ввел дроби позже и вскоре после этого нажал «Сохранить правку».

Поэтому я не могу установить, что отсутствующая диктовка была включена в один из успешных автосохранений.

Когда я впоследствии проверил ревизии поста, успешная ревизия в 12:20:55 содержала изображение, но не продиктованные дроби.

Я также проверил текущие записи черновиков, но ни старый, ни новый ключ черновика темы не имели остаточного черновика на момент проверки.

Ожидаемое поведение

Если запрос на редактирование зависает или прерывается, я ожидаю, что композер сохранит свое несохраненное содержимое и предоставит возможность повторить попытку, отменить или вернуться к редактированию.

В частности, интерфейс в идеале не должен делать несохраненный текст недоступным неопределенно долго, показывая состояние «Сохранение».

Фактическое поведение

Композер оставался недоступным, касание полосы «Сохранение» не открывало его заново, и продиктованный текст больше не был доступен при возобновлении редактирования.

Вопросы

  1. Ожидается ли, что композер останется недоступным неопределенно долго, пока запрос на редактирование находится в ожидании?

  2. Существует ли механизм тайм-аута или восстановления, предназначенный для возобновления редактирования, когда сетевой запрос прерван?

  3. Должна ли прерванная операция «Сохранить правку» сохранять текст в памяти или содержимое черновика, даже если запрос никогда не достигает контроллера обновления поста Rails?

  4. Будет ли какая-либо клиентская журналирование или инструментация полезной, если это произойдет снова на iOS?

Я понимаю, что изменение сети может объяснить окончательные отмены запросов. Однако интерфейс «Сохранение» уже завис до того, как я вручную сменил сети.

Я не установил, исходила ли первоначальная задержка из JavaScript Discourse, сетевых компонентов iOS/WebKit или университетского соединения.

Связанные обсуждения

У меня есть существующее обсуждение MPTCP / непрерывности соединения iOS, где я описал этот же инцидент с точки зрения транспорта.

Я также ранее сообщал о том, что отредактированные посты иногда остаются устаревшими в DiscourseHub на iOS. В этой отдельной проблеме правки успешно достигали сервера, но уже отрендеренный пост не обновлялся до обновления страницы. Другой пользователь также сообщил об этом поведении.

Я не знаю, разделяют ли эти две проблемы общую причину. Текущий инцидент отличается тем, что первоначальный запрос на редактирование поста не был успешно обработан.

Я также нашел два более ранних сообщения о композере, застрявшем в режиме «Сохранение»:

В обоих случаях были идентифицированы разные причины, и я не предполагаю, что они объясняют этот инцидент.

Я прикрепил ZIP-архив discourse_ios_save_evidence_2026-10-21_domain_redacted.zip (6,3 КБ), содержащий выбранные, отредактированные диагностические выгрузки Caddy/nginx, информацию о ревизиях и хронологию, описанную выше.

Я буду признателен за любые советы относительно того, как композер должен восстанавливаться из зависшего запроса, особенно когда несохраненные изменения нельзя скопировать до прерывания сети.

Дополнительное обновление после некоторых проблем с PWA на iOS 28 октября 2026 года, теперь на iOS 27.2 (iPhone 16 Pro).

Это были не точные воспроизведения исходной проблемы с потерей диктовки, но я проверил логи Caddy и nginx, чтобы понять, что произошло.

10:58 GMT — зависание операции с темой

Покидая библиотеку, я заметил, что операция с темой, похоже, зависла в интерфейсе.

Caddy зафиксировал два запроса HTTP/3 к POST /t/13515/merge-topic:

  • 10:58:16: статус 0, длительность примерно 0,15 мс.
  • 10:58:41: HTTP 200, длительность примерно 595 мс.

Успешный запрос также отображается в nginx как topics/merge_topic, HTTP 200.

Это подтверждает, что более поздний запрос был успешно обработан, но не устанавливает причину, по которой ранний запрос завершился без полного ответа.

12:26 GMT — проблемы с загрузкой PWA

Находясь в аудитории, я не смог нормально загрузить PWA.

Caddy зафиксировал четыре запроса HTTP/3 для основного JavaScript-бандла Discourse, завершившихся между 12:27:05 и 12:27:42.

Все они вернули HTTP 200 и записали 627 219 байт ответа, но их длительность составила примерно 31,4, 27,1, 29,4 и 17,3 секунды.

Обычно этот же ресурс загружался менее чем за секунду, как до этого интервала, так и после него.

12:42 GMT — дальнейшая проблема с загрузкой PWA

Ещё один запрос HTTP/3 к тому же JavaScript-бандлу занял примерно 24,3 секунды.

В 12:42:41 Caddy также записал неполный ответ обратного прокси HTTP/2:

aborting with incomplete response
writing: write tcp ...: connection reset by peer

Апстримом выступало существующее Unix-сокет соединение с nginx. Ошибка произошла, когда Caddy писал в TCP-соединение со стороны клиента, а не из-за идентифицированного сбоя Unix-сокета.

Логи не позволяют установить, была ли эта перезагрузка инициирована iOS или сетевой инфраструктурой между клиентом и VPS.

Во время двух инцидентов в аудитории я не изменял сетевые настройки iPhone вручную.

Связь с первоначальным отчетом

Я понимаю, что эти проблемы с загрузкой и операцией с темой могут иметь другую причину, чем исходная проблема с сохранением правки.

Однако они демонстрируют, что PWA на iOS столкнулась со значительными задержками запросов и прерванным ответом в ситуациях, когда я полагаюсь на нее для редактирования заметок.

Мой главный вопрос остается прежним: ожидается ли, что композер сохраняет и делает доступным несохраненный текст, когда запрос Save Edit зависает, а не оставляет его недоступным за состоянием Saving.

Буду признателен за любые указания относительно полезных диагностических инструментов на стороне клиента или ожидаемого поведения восстановления композера.