Skip to content

fix(perf): paginate GET /streams/pending to prevent unbounded memory fetch - #475

Merged
Xhristin3 merged 1 commit into
XStreamRollz:mainfrom
irefavor15-dotcom:fix/337-poll-pagination
Jul 29, 2026
Merged

fix(perf): paginate GET /streams/pending to prevent unbounded memory fetch#475
Xhristin3 merged 1 commit into
XStreamRollz:mainfrom
irefavor15-dotcom:fix/337-poll-pagination

Conversation

@irefavor15-dotcom

Copy link
Copy Markdown
Contributor

Summary

Closes #337

The processing worker called GET /streams/pending with no pagination. During a backlog (e.g. after downtime) this returned all pending events in a single HTTP response, causing an unbounded memory spike and unpredictable worker footprint.

Changes

api/src/streams/streams.controller.ts

New GET /streams/pending endpoint with limit and cursor query params:

  • limit: batch size (default 100, max 1000)
  • cursor: offset for the next page (default 0)
  • Returns { data: PendingStreamEvent[], nextCursor: number | null }
  • nextCursor is null when the returned batch is smaller than limit (final page)

api/src/streams/streams.service.ts

Added getPendingEvents(limit, offset) delegating to the repository layer.

api/src/streams/repository/streams-db.repository.ts

SQL implementation: queries stream_data ordered by timestamp ASC (FIFO) with LIMIT / OFFSET.

api/src/streams/repository/streams.repository.ts (in-memory)

Stub returning { data: [], nextCursor: null } — the in-memory repository has no stream_data store and is used only in unit tests.

xstreamroll-processing/src/config.ts

Added POLL_BATCH_SIZE to the zod schema (default '100', transformed to a positive integer).

xstreamroll-processing/src/worker.ts

Rewrote pollOnce() to paginate:

  • Starts at cursor=0, fetches up to POLL_BATCH_SIZE events per request
  • Advances cursor via nextCursor until it is null or until the high-watermark is reached (back-pressure)
  • Backward-compatible: still accepts plain-array responses from the old un-paginated endpoint

xstreamroll-processing/.env.example

Documents POLL_BATCH_SIZE with description and trade-offs.

Behaviour

Scenario Before After
Backlog of 10 000 events 10 000 events in one response (~memory spike) ≤100 events per batch, loops until drained
High-watermark reached mid-poll N/A Stops paging, logs a warning, waits for next interval
Last page N/A nextCursor is null → loop exits cleanly

Testing

  • TypeScript compile passes (type-check runs on push)
  • Worker test suite unaffected (mocks remain compatible with both response shapes)

…fetch (XStreamRollz#337)

- Add GET /streams/pending endpoint to StreamsController with limit/cursor
  query params (limit default 100, max 1000). Returns { data, nextCursor }
  where nextCursor is null on the final page.
- Implement getPendingEvents() in StreamsService, StreamsRepository (stub),
  and StreamsDbRepository (queries stream_data ordered by timestamp ASC).
- Add POLL_BATCH_SIZE env var (default 100) to config.ts zod schema.
- Rewrite worker pollOnce() to paginate: fetches batches in a loop,
  advancing cursor until nextCursor is null or the high-watermark is
  reached (back-pressure guard). Legacy plain-array responses are
  still accepted for backward compatibility.
- Document POLL_BATCH_SIZE in xstreamroll-processing/.env.example.

Closes XStreamRollz#337
@Xhristin3
Xhristin3 merged commit f91f53c into XStreamRollz:main Jul 29, 2026
3 of 5 checks passed
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.

perf: Polling worker fetches all pending events in one unbounded request — add pagination

2 participants