open-webui/backend/open_webui/socket
Claude 1964c39ac1
fix(stream): disjoint key namespaces, O(N) fence drain
- Seq key moved from `{prefix}:stream:{user}:{msg}:seq` to
  `{prefix}:streamseq:{user}:{msg}`. A message_id containing a literal
  `:seq` suffix would otherwise make its stream key equal to another
  message's seq key, which under a user-controlled message_id would
  collide INCR against an XADD on the same Redis key.

- clearResumeFence swap-and-iterate instead of shift() in a loop.
  Array.shift is O(n) per call, so the old drain was O(n²). Practical
  frame counts during a fence window are small (dozens worst-case) so
  this was never a user-visible problem, but the fix is two lines and
  removes a Big-O footgun. Concurrency behavior unchanged: the batch
  is captured via reference-swap so frames arriving during an await
  continue buffering into the fresh empty array still present in the
  map, and the outer while loop drains those in the next iteration.

Not addressed:
- Concurrent-emitter out-of-seq live frames — already documented as a
  known limitation with a block comment at get_event_emitter; fix
  requires distributed locking.
- Prune resumeSeqByMessageId on per-message terminal events —
  deliberately removed two rounds ago because it caused continuation-
  reuses-message_id to replay duplicate content; the trade-off
  (bounded int-per-message memory vs. correctness) is already
  captured in that commit message.
2026-04-15 07:24:45 +00:00
..
main.py fix(stream): disjoint key namespaces, O(N) fence drain 2026-04-15 07:24:45 +00:00
utils.py refac 2026-03-22 05:48:05 -05:00