Skip to content

O(n²) performance in ServerEventParser.parse() causes event loss for large SSE payloads #48

@liefran-sim

Description

@liefran-sim

Description

ServerEventParser.parse() has O(n²) performance characteristics when receiving large SSE events delivered in small chunks (e.g., URLSession's ~8KB didReceiveData callbacks). This causes severe delays and can result in events being lost when combined with application-level timeouts.

Root Cause

In the current implementation:

mutating func parse(_ data: Data) -> [EVEvent] {
    let (separatedMessages, remainingData) = splitBuffer(for: buffer + data)
    // ...
}

Two issues:

  1. buffer + data creates a new Data allocation on every call, copying the entire buffer contents. As the buffer grows (e.g., 8KB → 16KB → 24KB → ... → 1.5MB), each call copies more data.

  2. splitBuffer scans the entire buffer for the \n\n separator on every chunk, even when the separator is unlikely to be present in the new data.

For a 1.5MB SSE event arriving in ~180 chunks of 8KB:

  • Total work: sum of scanning 8KB + 16KB + 24KB + ... + 1.5MB ≈ 135MB of data copying/scanning
  • This is O(n²) where n = total event size

Impact

In our production app, SSE search responses contain 200-400 flight inventories per event (~1.5MB). The parser takes 15-20 seconds to consume all chunks, causing our 30-second search timeout to fire before the final event's \n\n delimiter is processed — resulting in lost events.

Proposed Fix

  1. Use buffer.append(data) instead of buffer + data — in-place append with amortized O(1) when capacity is sufficient
  2. Only scan the newly added tail region (+ small overlap for boundary-crossing separators) to detect if a separator is present
  3. Only call splitBuffer when a separator is actually detected in the new data

This reduces the per-chunk work from O(buffer_size) to O(chunk_size), making the overall complexity O(n) instead of O(n²).

Environment

  • EventSource version: 0.1.7
  • iOS 16+
  • Payload: ~1.5MB SSE events arriving in ~8KB URLSession chunks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions