fix(xtrim): не триммить un-acked pending + null-гард parseStreamEntries (баг #3) #9

Merged
claude merged 1 commits from dev into main 2026-06-03 13:02:49 +00:00

1 Commits

Author SHA1 Message Date
coopops c8b7107c3a fix(xtrim): не триммить un-acked pending + гард null-записей в parseStreamEntries
CI / Typecheck (pull_request) Has been cancelled
CI / Unit tests (pull_request) Has been cancelled
CI / Integration tests (pull_request) Has been cancelled
CI / Build (pull_request) Has been cancelled
CI / Lint (pull_request) Has been cancelled
Баг #3: consumer падал навсегда с TypeError "Cannot read properties of null
(reading 'length')" и переставал читать стрим. Два корня:

1. XtrimSupervisor.trim() брал MINID = lastDeliveredId. Но un-acked pending
   записи старше lastDeliveredId (доставлены, не подтверждены) → XTRIM сносил
   собственные pending группы. Теперь нижняя граница trim — самый старый
   un-acked ID из XPENDING (новый метод RedisStore.xpendingMinId), а сравнение
   stream-ID числовое по <ms>-<seq>, не лексикографическое ('1000' < '999').

2. parseStreamEntries падал на null rawFields: при перечитывании PEL
   (XREADGROUP ... 0) Redis отдаёт [id, null] для обрезанных ID. Добавлен
   гард `if (!rawFields) continue` — обрезанные записи пропускаются.

Триггер: consumer отстаёт от писателя (бутстрап с genesis) → растёт pending →
XtrimSupervisor сносит pending → PEL отдаёт null → краш в бесконечном цикле.

Воспроизведено и проверено на реальном Redis (новый integration-тест
xtrim.integration.test.ts, гоняется в CI): trim сохраняет все un-acked pending,
PEL-перечитка не падает на null. Unit: 211 зелёных.

parser2 1.1.2→1.1.3. ship-reader без изменений.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:02:21 +00:00