From 3ce26e3df2038f2500f502823cbac4cbd2c28703 Mon Sep 17 00:00:00 2001 From: u9g Date: Tue, 8 Sep 2026 10:25:57 -0400 Subject: [PATCH] Throttle repeated packet-parse error logs in FullPacketParser A malformed or mismatched stream can make the same packet fail to parse on every frame it sends. FullPacketParser logged the full stack each time, so one persistent fault produced thousands of identical stacks a minute and buried every other log line -- a single session was seen writing ~15k identical PartialReadError stacks, tens of megabytes. Each distinct error (keyed on the top of its stack) is now logged in full on its first occurrence, then only at the 1st, 2nd, 4th, 8th, ... with a repeat count, so a persistent fault costs O(log n) lines instead of n. The same throttle covers the chunk-size-mismatch log two lines up. Counts are per parser instance; noErrorLogging still silences everything. --- src/serializer.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/serializer.js b/src/serializer.js index 8b52e95..9d35018 100644 --- a/src/serializer.js +++ b/src/serializer.js @@ -1,5 +1,15 @@ const Transform = require('readable-stream').Transform +// A malformed stream can make the same packet fail to parse for every frame it sends -- thousands +// of identical stacks a minute. Log the first occurrence of each in full, then only at 1, 2, 4, 8, +// ... so a persistent fault stays O(log n) instead of unbounded. Keyed per parser instance. +function logThrottled (counts, key, full) { + const n = (counts.get(key) || 0) + 1 + counts.set(key, n) + if (n === 1) console.log(full) + else if ((n & (n - 1)) === 0) console.log(`${key} (repeated ${n} times)`) +} + class Serializer extends Transform { constructor (proto, mainType) { super({ writableObjectMode: true }) @@ -62,6 +72,7 @@ class FullPacketParser extends Transform { this.proto = proto this.mainType = mainType this.noErrorLogging = noErrorLogging + this.errorCounts = new Map() } parsePacketBuffer (buffer) { @@ -73,13 +84,14 @@ class FullPacketParser extends Transform { try { packet = this.parsePacketBuffer(chunk) if (packet.metadata.size !== chunk.length && !this.noErrorLogging) { - console.log('Chunk size is ' + chunk.length + ' but only ' + packet.metadata.size + ' was read ; partial packet : ' + + logThrottled(this.errorCounts, 'chunk size mismatch', + 'Chunk size is ' + chunk.length + ' but only ' + packet.metadata.size + ' was read ; partial packet : ' + JSON.stringify(packet.data) + '; buffer :' + chunk.toString('hex')) } } catch (e) { if (e.partialReadError) { if (!this.noErrorLogging) { - console.log(e.stack) + logThrottled(this.errorCounts, e.stack.split('\n')[0], e.stack) } return cb() } else {