Add EventBatcher transport#191
Conversation
…batching Introduces EventBatcher — a Transport decorator that accumulates identical events within a configurable time window and forwards one representative message per unique signature with CatcherMessage.count set to the total number of occurrences. Signature is computed from title, type, and per-frame coordinates (file, line, column, function) using null-byte delimiters. Lookup is O(1) via Map keyed on the signature string. Flush is triggered by whichever condition is met first: - time window expires (default 5 s from first event in window) - buffer reaches maxBufferSize distinct signatures (default 100) - flush() is called explicitly (e.g. on pagehide) BrowserCatcher wraps its transport in EventBatcher and registers a capture-phase pagehide listener to drain the buffer before the socket closes. Protocol: CatcherMessage.count is declared via module augmentation on @hawk.so/types. Server must increment totalCount by count (defaulting to 1) and create a single Repetition record per batch.
| * | ||
| * @param message - message to compute signature for | ||
| */ | ||
| function computeSignature<T extends CatcherMessageType>(message: CatcherMessage<T>): string { |
There was a problem hiding this comment.
lets make it work like in Grouper (without backtrace)
There was a problem hiding this comment.
Removed backtrace from key.
I think it's ok, if we still represent it as string concat, not hash as grouper does.
|
|
||
| if (existing !== undefined) { | ||
| existing.count++; | ||
| } else { |
There was a problem hiding this comment.
if event is not batched, send it immediately (or with small delay to wait for duplicates)
There was a problem hiding this comment.
Now sending events with 5sec delay to wait duplicates.
| * First occurrence is used as representative event for each batch. | ||
| * Context, user, and breadcrumbs of subsequent identical occurrences are not preserved. | ||
| */ | ||
| export class EventBatcher<T extends CatcherMessageType> implements Transport<T> { |
There was a problem hiding this comment.
lets call it "dedupe" instead of "batching" (because we will add batching later)
So lets the class EventDedupeTransport
| import type { BacktraceFrame, CatcherMessage, CatcherMessageType } from '@hawk.so/types'; | ||
| import type { Transport } from './transport'; | ||
|
|
||
| declare module '@hawk.so/types' { |
There was a problem hiding this comment.
should be added to the types package
- Renamed EventBatcher to EventDedupeTransport - removed backtrace from event key for debounce
d0c37a1 to
07cc623
Compare
| */ | ||
| constructor(transport: Transport<T>, options: EventDedupeTransportOptions = {}) { | ||
| this.transport = transport; | ||
| this.windowMs = options.windowMs ?? 5_000; |
There was a problem hiding this comment.
I think five 5 sec is too large delay. We can loose a lots of errors.
| this.windowMs = options.windowMs ?? 5_000; | |
| this.windowMs = options.windowMs ?? 2_500; |
| if (existing !== undefined) { | ||
| existing.count++; | ||
|
|
||
| return; |
There was a problem hiding this comment.
should we reschedule timer here?
Introduces EventBatcher — a Transport decorator that accumulates identical events within a configurable time window and forwards one representative message per unique signature with CatcherMessage.count set to the total number of occurrences.
Signature is computed from title, type, and per-frame coordinates (file, line, column, function) using null-byte delimiters. Lookup is O(1) via Map keyed on the signature string.
Flush is triggered by whichever condition is met first:
BrowserCatcher wraps its transport in EventBatcher and registers a capture-phase pagehide listener to drain the buffer before the socket closes.
Protocol: CatcherMessage.count is declared via module augmentation on @hawk.so/types. Server must increment totalCount by count (defaulting to 1) and create a single Repetition record per batch.