Skip to content

fix(telemetry): serialize each event once at admission - #5422

Open
youtsuhodev wants to merge 1 commit into
openfrontio:mainfrom
youtsuhodev:fix/telemetry-single-pass-serialization
Open

youtsuhodev wants to merge 1 commit into
openfrontio:mainfrom
youtsuhodev:fix/telemetry-single-pass-serialization

Conversation

@youtsuhodev

Copy link
Copy Markdown

Description:

Serialize each telemetry event exactly once, at admission, instead of three
times across the emit → flush path.

BufferedMatchTelemetryEmitter.emit() previously ran JSON.stringify(event)
and then JSON.parse(serialized) purely to deep-clone the event for its
admission snapshot, and sendBatch() later re-stringified every queued event
inside the batch envelope. For intent_observed — emitted per accepted/rejected
intent, up to 10/s per client — this put three serialization passes on a hot
path.

Changes in src/server/telemetry/BufferedMatchTelemetryEmitter.ts:

  • QueueEntry now stores the admission-time JSON string (serialized) instead
    of the event object. That string is both the immutable snapshot and the exact
    wire payload, so the JSON.parse clone is gone.
  • emit() computes bytes from that single string; the byte budget, oversized
    event, and circular-reference (droppedSerialization) behavior are unchanged.
  • sendBatch() splices the pre-serialized fragments into the envelope by
    concatenation (same key order and values as the previous JSON.stringify of
    the object), removing the second serialization of every event.

Net effect: two serialization passes removed per intent, no JS deep clone, and
the large intent object is no longer retained in the queue.

Scope: performance-only, no wire-format or signature change. Verified the emitted
batch is byte-for-byte compatible with the previous envelope (HMAC signature is
computed over the body exactly as before).

Testing: npx vitest tests/server/telemetry tests/server/MatchTelemetryIntegration.test.ts --run
→ 37 passed. oxlint and eslint clean on the changed file. No new tests were
added: the existing suite already covers the affected paths (immutable admission
snapshot after caller mutation, exact serialized byte budget, unserializable/
circular event dropped without throwing, FIFO batch + exact HMAC).
No src/core changes.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory

Please put your Discord username so you can be contacted if a bug or regression is found:

lilian.looters

emit() previously ran JSON.stringify then JSON.parse to deep-clone every event, and sendBatch() re-stringified the whole batch. Store the admission-time JSON string as the immutable snapshot and splice it into the batch envelope, dropping two serialization passes per intent from the hot path.
@CLAassistant

CLAassistant commented Sep 14, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: ba953e38-2337-4e26-9384-11977f956760

📥 Commits

Reviewing files that changed from the base of the PR and between 1e973bb and 1ac9d2a.

📒 Files selected for processing (1)
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

BufferedMatchTelemetryEmitter now serializes each telemetry event once during emission. The queue stores that serialized JSON and its byte length. Batch submission joins the stored JSON fragments into the request body without re-stringifying event objects.

Changes

Telemetry serialization

Layer / File(s) Summary
Serialized queue and batch submission
src/server/telemetry/BufferedMatchTelemetryEmitter.ts
QueueEntry stores serialized JSON instead of an event object. emit removes the parse round-trip. sendBatch joins the stored JSON fragments into the request envelope.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Refactor

Suggested reviewers: celant

Merge Risk: ⚪ Minimal · up to 1ac9d

The telemetry optimization preserves the existing event payload and delivery behavior, so it is mergeable with normal checks.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: serializing each telemetry event once during admission.
Description check ✅ Passed The description directly explains the serialization change, preserved behavior, performance impact, testing, and scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

JSON rests in the queue,
Bytes keep the count in view,
Events cross the wire,
No second parse required,
Batches join their parts,
Telemetry keeps its course.

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the small-fix Small fix (≤ 50 lines) — auto-applied by PR gate label Sep 14, 2026
@github-actions

Copy link
Copy Markdown

🤖 Claude Code Review

Verdict: ✅ No issues found — this is a clean, correct performance-only refactor.

Findings by severity: Critical: 0 · High: 0 · Medium: 0 · Low: 0

Summary:

The change to src/server/telemetry/BufferedMatchTelemetryEmitter.ts replaces the JSON.stringifyJSON.parse deep-clone in emit() and the second JSON.stringify of the whole batch in sendBatch() with a single admission-time serialization, storing QueueEntry.serialized: string instead of QueueEntry.event. The batch envelope is now built by string concatenation instead of object serialization.

I independently verified:

  • Byte-for-byte compatibility: the hand-built envelope (schemaVersion, batchId, createdAt, server, events) preserves the exact key order and encoding of the original JSON.stringify(...) call. batchId and server are still passed through JSON.stringify, so quoting/escaping is unchanged; createdAt (a number) serializes identically either way.
  • JSON-injection safety: each entry.serialized fragment is guaranteed to be the output of a prior successful JSON.stringify(event) call (validated at admission before being queued), so it's always a single, syntactically valid, self-balanced JSON value — joining fragments with , inside [...] cannot break out of the array structure.
  • Immutability guarantee preserved: the admission-time string is naturally immune to later mutation of the original event object, same as the old deep-clone-via-parse approach.
  • Byte accounting, drop paths, and HMAC signing are unchanged — the signature is still computed over the exact body string sent, and the droppedSerialization/serialization_failed error paths remain reachable.
  • CLAUDE.md compliance: the only changed file lives under src/server/, not src/core/, so the 'all src/core changes must include tests' rule doesn't apply. No user-visible text is touched, so the i18n rule is not implicated. No violations found.

No high-signal bugs, logic errors, or CLAUDE.md violations were identified in this diff.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

small-fix Small fix (≤ 50 lines) — auto-applied by PR gate

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

2 participants