Глава 16

Добавьте progress, cancellation, notifications и cache

Production-клиент отличается от демо тем, что управляет неидеальными сценариями. Он ограничивает время, отменяет ненужную работу, показывает прогресс, обновляет discovery по notifications и кэширует результаты по server hints. Каждый из этих механизмов кажется мелочью, пока не окажется, что без него система либо висит, либо отдаёт устаревшие данные.

Кэш начинается с честного ключа - он должен включать authorization context, иначе приватные данные утекут между пользователями.

cacheKey = authorizationContext + method + normalizedParams

if result.cacheScope === "public":
  shared cache допустим только для действительно общих данных
if result.cacheScope === "private":
  cache изолирован по токену/tenant/user
expiresAt = receivedAt + result.ttlMs
list_changed => invalidate затронутую коллекцию

Отмена тоже требует дисциплины: сигнал доходит до сервера, тот перестаёт слать progress, но уже совершённый побочный эффект сам не откатывается.

host aborts user request
  ├─ stdio: send notifications/cancelled for request id
  └─ HTTP: abort request stream

server checks signal between expensive steps
server stops emitting progress
domain side effect is not rolled back automatically

Собрать все четыре механизма вместе помогает таблица - зачем каждый и где его главная оговорка.

Механизм · Зачем · Главная оговорка

  • Progress - UX и watchdog для долгого call; Не является durable task history
  • Cancellation - Остановить ненужное вычисление; Не отменяет уже committed side effect
  • List-changed - Инвалидировать discovery cache; Modern HTTP требует listen subscription
  • ttlMs - Freshness hint; 0 означает stale, не "запрет cache"
  • cacheScope - Public/private reuse boundary; Server отвечает за правдивую классификацию

Оговорки здесь важнее самих механизмов. Progress - это UX и watchdog, а не durable-история задачи. Cancellation останавливает вычисление, но не отменяет committed side effect. list_changed инвалидирует discovery-кэш, но в современном HTTP требует listen-subscription. А ttlMs: 0 означает "данные stale", а не "кэш запрещён".

Cancel не равен rollback. Если инструмент отправил платёж, письмо или деплой, отмена протокольного запроса не возвращает систему назад. Idempotency, commit status и reconciliation проектируются на уровне домена - протокол здесь бессилен по определению.

Прогресс и отмена - это ещё не настоящая длинная работа. Когда операции нужен дополнительный ввод или она идёт минутами, в дело вступают явные механизмы: MRTR и tasks.

Ссылки