Reference in New Issue
Block a user
Delete Branch "perf/435-collab-token-cache"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Кэширует collab-токен в
DocmostClient, чтобы реестр живых CollabSession (#400/#431) реально попадал. Реестр ключуется наwsUrl + " " + pageId + " " + token; клиент чеканил СВЕЖИЙ токен на КАЖДУЮ мутацию (in-app — пере-подписывает JWT с новым iat/exp раз в секунду; внешний MCP —POST /auth/collab-tokenна каждый вызов), поэтому token-компонент ключа менялся каждый раз → сессия почти никогда не переиспользовалась (connect-штормы, 25s таймауты, зомби-сессии). Теперь серия мутаций держит один токен → одну сессию.closes #435
collabTokenCache {token, mintedAt};getCollabTokenWithReauth(forceRefresh=false)смотрит кэш первым (гейт!forceRefresh && ttl>0 && свежесть), оборачивает ОБЕ ветки чеканки (provider-путь in-app агента И REST) черезrememberCollabToken(пустой токен не кэшируется);readCollabTokenTtlMs()— envMCP_COLLAB_TOKEN_TTL_MS, дефолт 5 мин (<< 24h жизни токена и ≤ max-age сессии, окно устаревания прав не расширяется сверх #431); ЯВНЫЙ0/отрицательное отключают кэш (rollback-knob), unset/непарсибельное → дефолт;forceRefresh=true(обход кэша → свежий токен), гард!forceRefreshограничивает ровно одним повтором;login()и в response-интерцепторе (гдеthis.tokenзануляется перед пере-логином). Единственные две точки установкиthis.tokenобе зануляют кэш.How verified
Прогон на стенде (worktree,
node --test):pnpm --filter @docmost/mcp build→ чисто (tsc).node --test test/mock/collab-token-cache.test.mjs→ 10/10 passed: попадание в пределах TTL чеканит один раз (provider+REST), пере-чеканка после TTL,TTL=0fetch-every-call (обе ветки), 401 обходит кэш (без петли),forceRefreshобходит тёплый кэш, REST 401 re-login+refetch,login()чистит кэш, две последовательные мутации → идентичный токен (стабильный ключ сессии — цель #435).node --test collab-token-cache + reauth + collab-session→ 38/38 passed (нет регресса reauth/session).markdown-converter.test.mjs(отступы вложенных списков) — ортогональны, подтверждено на базе.Внутренний цикл: 1 проход внутреннего ревью (APPROVE WITH SUGGESTIONS). Правка по ревью: уточнён docstring
readCollabTokenTtlMs— расхождение с поведением (непарсибельное значение даёт дефолт 5мин с ВКЛючённым кэшем, а не отключает; отключает только явный0/отрицательное). Необязательную дедупликацию конкурентных первых mint-ов сознательно не делал (не баг, не регресс; последовательный всплеск по странице сериализуетсяwithPageLockи попадает в кэш) — оставил форком для ревьюера.Checklist
CollabSession-реестр (#400/#431) ключуется на `wsUrl + " " + pageId + " " + token`. Клиент чеканил СВЕЖИЙ collab-токен на КАЖДУЮ мутацию (in-app — пере-подписывает JWT с новым iat/exp раз в секунду; внешний MCP — POST /auth/collab-token на каждый вызов), поэтому token-компонент ключа менялся каждый раз и сессия почти никогда не переиспользовалась (connect-штормы, 25s таймауты, зомби-сессии). - новое пер-инстансное поле collabTokenCache {token, mintedAt}; - getCollabTokenWithReauth(forceRefresh=false) сначала смотрит кэш (гейт !forceRefresh && ttl>0 && свежесть), оборачивает ОБЕ ветки чеканки (provider-путь in-app агента И REST POST /auth/collab-token), обе через rememberCollabToken (пустой токен не кэшируется); - readCollabTokenTtlMs() читает env MCP_COLLAB_TOKEN_TTL_MS свежо; дефолт 5 мин (сильно ниже 24h жизни токена и <= max-age сессии, окно устаревания прав не расширяется сверх #431); ЯВНЫЙ 0/отрицательное отключают кэш (rollback-knob = точный fetch-per-call), unset/непарсибельное -> дефолт; - reauth-ретрай (401/403) в обеих ветках рекурсит forceRefresh=true (обход кэша -> свежий токен), гард !forceRefresh ограничивает ровно одним повтором; - кэш сбрасывается на КАЖДОЙ смене идентичности: в login() и в response-интерцепторе (где this.token зануляется перед пере-логином) — инвариант-4 (изоляция идентичностей) сохранён, кэш пер-клиентный. Внутренний цикл: 1 проход внутреннего ревью (APPROVE WITH SUGGESTIONS); правка по ревью — уточнён docstring readCollabTokenTtlMs (расхождение с поведением: непарсибельное значение даёт дефолт 5мин с ВКЛючённым кэшем, а не отключает; отключает только явный 0/отрицательное). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Ревью — #439
perf(mcp): кэш collab-токена в клиенте — чтобы кэш CollabSession (#400) реально попадал (#435)Вердикт: PASS ✅
Полный веер из 9 аспектов по всему диффу PR (merge-base
ee33a293). Гейт зелёный (clean-room/home/claude/gm-ci): mcp build (tsc) 0, node --test 571/571 (+282collab-token-cache). drawio-wire коммит (f46d89ea) — тот же, что в #438, уже проверен (дрейф-гард зелёный).Безопасность токен-кеша — все guard'ы проверены (клик)
DocmostClient; клиент строится СВЕЖИМ на каждый запрос/чат-ход (buildDocmostClientper-call, не пул/синглтон) → кеш умирает с запросом. Статик/модульного кеша нет. Токен одного юзера не утечёт другому.login()— токен не переживает смену личности (тест 9). На 401/403 — кеш очищается (interceptor) +getCollabTokenWithReauth(forceRefresh=true)минует кеш (тест 4).(wsUrl,pageId,token)стабилен → registry-хит → сессия переиспользуется (mint-once в TTL — тест 6). Цель достигнута: токен-черн, ломавший кеш (#400/#431), устранён.Date.now()-mintedAt<ttl) верна, без await-гэпов check→use; service-account/stdio путь сохранён (тесты 8-9). Architecture (per-instance + env-TTL как #431), conventions, documentation, simplification — LGTM. Rollback-кнопка (MCP_COLLAB_TOKEN_TTL_MS=0отключает) протестирована (тест 3).⛔ DROP — кодеру НЕ делать (лог калибровки)
Security/correctness покрыты (изоляция, re-auth, login-clear, coherence, rollback-0); остаток — perf-completeness edges:
[below-threshold]low/med[test-coverage] env-парсинг garbage/negative не тестируется отдельно:Number.isFinite(raw) ? Math.max(0,raw) : 5min— импл верна, load-bearing кейс (0 отключает) ПОКРЫТ; непокрыты «abc»/«5min»→дефолт-ON и «-1»→off (вторичные, при поломке = перф-регресс, не корректность/безопасность). Рекомендация: добавить кейсы (env="abc"/"-1"/"" ) — дёшево.[below-threshold]low/med[stability] конкурентныеgetCollabTokenWithReauth()на пустом кеше оба минтят (нет dedup как уloginPromise). Смягчено: #431 сериализует мутации одной страницы черезwithPageLock→ фактически последовательно → кеш-хит; оба токена валидны. Perf-edge, не баг.DO/ESCALATE нет. Токен-кеш безопасен (per-user изоляция instance-scoped, re-auth/login чистят кеш, окно прав не расширено), цель #435 достигнута и покрыта, гейт зелёный. Approve.
9 аспектов. Per-user изоляция (per-request инстанс, нет статика), re-auth/login-clear, окно-прав-не-расширено, coherence #435 — проверены (+ комментарий staleness сверен мной). Остаток — perf-edge покрытие (env-garbage/concurrent), below-threshold. Escalate нет.