Files
iu 14db79632a chore(openspec): архивировать android-live-albums и fix-album-cover-readiness
Дельта android-live-albums: android-client (+3 требования: живое обновление
списка, догон при переподключении, потеря доступа) и realtime (порядок списка
теперь правило отображения клиента).

Дельта fix-album-cover-readiness: realtime (+1 требование: album.updated при
готовности вариантов фото-обложки).
2026-10-16 12:48:32 +03:00

5.1 KiB
Raw Permalink Blame History

Design

Context

См. proposal.md — мотивация. Что важно для подхода:

  • Ссылки на обложку формируются из photo_objects (роли thumb/medium), которые заполняются асинхронно, поэтому в объекте альбома (Store.GetByID джойнит обложку) они появляются не в момент загрузки, а когда варианты готовы.
  • Загрузка фото вызывает SetCoverIfEmpty, то есть первое фото становится обложкой ещё до генерации вариантов, а NotifyAlbumChanged на этом шаге публикует album.updated уже с cover_photo_id, но с пустыми ссылками.
  • Готовность вариантов обрабатывает Worker.notify (internal/photos/worker.go): он публикует только photo.updated в канал альбома и персональные потоки не трогает.
  • albums.Service уже умеет перечитать альбом, обогатить обложку (attachCover) и веерно опубликовать album.updated (NotifyAlbumChanged → PublishAlbumTo).

Goals / Non-Goals

Goals:

  • Доставить актуальную ссылку на обложку в персональные потоки, когда она появляется.
  • Не увеличивать поток событий при массовой загрузке.

Non-Goals:

  • Менять контракт photo.updated в канале альбома (открытый экран закрывает свой случай сам).
  • Клиентские обходные пути (рефетч списка по таймеру и т.п.).
  • Синхронная генерация вариантов при загрузке.

Decisions

1. Публикует albums.Service, а не воркер. Решение «объект альбома изменился» принимает владелец альбомов: добавляется NotifyCoverReady(ctx, albumID, photoID), который перечитывает альбом и публикует album.updated, только если cover_photo_id == photoID (иначе no-op), переиспользуя NotifyAlbumChanged. Воркер остаётся про фото и лишь сообщает о готовности. Альтернатива — строить album.updated прямо в воркере: тогда photos пришлось бы знать форму объекта альбома, обложку и аудиторию.

2. Условие — совпадение с текущей обложкой. Готовность вариантов обычного фото объект альбома не меняет. Без условия пришлось бы на каждое фото перечитывать альбом и веерно публиковать album.updated всем участникам — лишняя нагрузка при массовой загрузке и лишние события клиентам. Альтернатива — «публиковать всегда»: проще, но шумно; отвергнуто.

3. Проверка по cover_photo_id, а не по «в альбоме ещё нет ссылки на обложку». Так закрываются оба сценария: и автопоставленная обложка первой загрузкой, и явная установка обложки на фото, варианты которого ещё не готовы.

Risks / Trade-offs

  • Двойное чтение альбома (GetByID для проверки обложки и внутри PublishAlbumTo) → дополнительное чтение точечное (только для готовности вариантов фото-обложки), принято; при необходимости схлопывается в один публикующий вызов.
  • Гонка со сменой обложки между публикацией photo.created и готовностью вариантов → проверка по актуальному cover_photo_id отсекает лишнюю публикацию, а новая обложка получит своё событие при готовности её вариантов.
  • Аудитория недоступна (ошибка в members.Audience) → событие не публикуется; клиент догоняет состояние при переподключении (refresh) — существующее поведение веера.

Migration Plan

Изменение аддитивное: схема и API не меняются, нужен только перезапуск бэкенда (логика и статика в одном бинарнике). Откат — предыдущий бинарник.