Refactor parseStreaming to use pendingBatches array

Was running into an issue where epg wouldn't process passed the first batch. Refactored the batching to use an array and added backpressure to keep memory usage low.
This commit is contained in:
avandeweghe
2026-01-31 19:31:03 -05:00
committed by GitHub
parent 80d2c4bfb3
commit bfc80738f4
+35 -9
View File
@@ -266,9 +266,11 @@ async function* fetchAndParseStreaming(url, batchSize = 1000) {
* @yields {{ channels: Array|null, programmes: Array, isLast: boolean }} * @yields {{ channels: Array|null, programmes: Array, isLast: boolean }}
*/ */
async function* parseStreaming(input, batchSize = 1000) { async function* parseStreaming(input, batchSize = 1000) {
const channels = []; let channels = [];
let programmeBatch = []; let programmeBatch = [];
let channelsYielded = false; let channelsYielded = false;
const maxPenmdingBatches = 4;
let paused = false;
// We need to convert SAX events to an async iterator // We need to convert SAX events to an async iterator
// This requires collecting events and yielding when batch is full // This requires collecting events and yielding when batch is full
@@ -279,7 +281,7 @@ async function* parseStreaming(input, batchSize = 1000) {
let currentObject = null; let currentObject = null;
let textBuffer = ''; let textBuffer = '';
let resolveNext = null; let resolveNext = null;
let pendingBatch = null; const pendingBatches = [];
let ended = false; let ended = false;
let error = null; let error = null;
@@ -345,13 +347,27 @@ async function* parseStreaming(input, batchSize = 1000) {
isLast: false isLast: false
}; };
channelsYielded = true; channelsYielded = true;
// allow channels array to be GC'd after included in first batch
try { if (channels && channels.length) channels = null; } catch (e) {}
programmeBatch = []; programmeBatch = [];
if (resolveNext) { if (resolveNext) {
resolveNext(batch); resolveNext(batch);
resolveNext = null; resolveNext = null;
} else { } else {
pendingBatch = batch; pendingBatches.push(batch);
// Apply backpressure when queue grows too large
try {
if (!paused && pendingBatches.length >= maxPenmdingBatches) {
if (input && typeof input.pause === 'function') {
input.pause();
paused = true;
}
}
} catch (e) {}
} }
} }
} }
@@ -398,7 +414,7 @@ async function* parseStreaming(input, batchSize = 1000) {
resolveNext(batch); resolveNext(batch);
resolveNext = null; resolveNext = null;
} else { } else {
pendingBatch = batch; pendingBatches.push(batch);
} }
}); });
@@ -413,10 +429,21 @@ async function* parseStreaming(input, batchSize = 1000) {
input.pipe(saxStream); input.pipe(saxStream);
// Yield batches as they become available // Yield batches as they become available
while (!ended || pendingBatch) {
if (pendingBatch) { while (!ended || pendingBatches.length > 0) {
const batch = pendingBatch; if (pendingBatches.length > 0) {
pendingBatch = null; const batch = pendingBatches.shift();
// If paused and queue drained below threshold, resume
try {
if (paused && pendingBatches.length < maxPenmdingBatches) {
if (input && typeof input.resume === 'function') {
input.resume();
paused = false;
}
}
} catch (e) {}
yield batch; yield batch;
if (batch.isLast) break; if (batch.isLast) break;
} else if (!ended) { } else if (!ended) {
@@ -445,4 +472,3 @@ module.exports = {
getProgrammesForChannel, getProgrammesForChannel,
getCurrentAndUpcoming getCurrentAndUpcoming
}; };