Skip to content

Batch history store appends into one update per tick - #108

Merged
ctoth merged 2 commits into
masterfrom
agent/issue-102-store-batching
Aug 12, 2026
Merged

Batch history store appends into one update per tick#108
ctoth merged 2 commits into
masterfrom
agent/issue-102-store-batching

Conversation

@daiverd

@daiverd daiverd commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #102

outputStore and channelHistoryStore previously rebuilt and sliced their capped backing arrays for every message, paying O(n) allocation and subscriber work at the busiest times.

Changes

  • Queue same-tick appends with ids assigned at enqueue time and flush each burst from one microtask.
  • Notify subscribers with the complete burst before synchronously publishing the capped retained snapshot, so oversized frames do not lose output or per-channel history.
  • Keep Output's existing imperative id-based consumption; switch useChannelHistory to the direct Zustand subscription so React cannot coalesce away the full-burst delivery notification.
  • Preserve synchronous reset() semantics by discarding pending entries and resetting counters; an already-queued flush then no-ops.

TDD and verification

  • Red: a 501-entry output burst delivered only 500 entries, and a 500-message channel-A burst followed by 1,000 channel-B messages lost channel A entirely.
  • Green: both consumer-level regressions pass while retained snapshots remain capped at 500 and 1,000 entries.
  • Full suite: 1,065/1,065 tests pass.
  • tsc --noEmit and the production Vite build pass.

outputStore and channelHistoryStore rebuilt their entire capped
backing array on every message: each append spread the existing
entries into a new array and sliced it, costing two allocations of up
to 500/1000 elements per line, paid precisely when the client is
busiest.

Appends within a tick are now queued with ids assigned at enqueue time
and flushed by a single queueMicrotask set(): one concat, sliced only
when actually over the cap, and one store notification per burst
instead of one of each per message. output.tsx already consumes
entries incrementally by id and handles multiple entries per
notification, so consumers are unchanged. reset() discards pending
entries synchronously; an already-queued flush then no-ops.

Tests that asserted store contents synchronously now await the
microtask flush.

Fixes #102

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ctoth
ctoth merged commit 49e6b1d into master Aug 12, 2026
1 check passed
github-actions Bot pushed a commit that referenced this pull request Aug 12, 2026
Batch history store appends into one update per tick 49e6b1d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

outputStore/channelHistoryStore: O(n) array rebuild on every message

2 participants