@thru/replay 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -418,12 +418,26 @@ var ReplayStream = class {
418
418
  this.logger.info(
419
419
  `replay entering BACKFILLING state (startSlot=${startSlot}, safetyMargin=${safetyMargin})`
420
420
  );
421
+ let emptyPageRetries = 0;
422
+ const MAX_EMPTY_PAGE_RETRIES = 10;
421
423
  while (!backfillDone) {
422
- const page = await fetchBackfill({ startSlot, cursor });
424
+ const page = await currentFetchBackfill({ startSlot, cursor });
423
425
  if (!page.items.length && !page.cursor && !page.done) {
424
- this.logger.warn("empty backfill page without cursor; retrying");
426
+ emptyPageRetries++;
427
+ if (emptyPageRetries > MAX_EMPTY_PAGE_RETRIES) {
428
+ this.logger.error(
429
+ `backfill returned ${MAX_EMPTY_PAGE_RETRIES} consecutive empty pages; treating as done`
430
+ );
431
+ break;
432
+ }
433
+ const backoffMs = calculateBackoff(emptyPageRetries - 1, DEFAULT_RETRY_CONFIG);
434
+ this.logger.warn(
435
+ `empty backfill page without cursor; retrying in ${backoffMs}ms (${emptyPageRetries}/${MAX_EMPTY_PAGE_RETRIES})`
436
+ );
437
+ await delay(backoffMs);
425
438
  continue;
426
439
  }
440
+ emptyPageRetries = 0;
427
441
  const sorted = [...page.items].sort(
428
442
  (a, b) => compareBigint(extractSlot(a), extractSlot(b))
429
443
  );
@@ -549,19 +563,20 @@ var ReplayStream = class {
549
563
  async *miniBackfill(fromSlot, fetchBackfill, extractSlot, keyOf, seenItem, recordEmission) {
550
564
  this.logger.info(`mini-backfill starting from slot ${fromSlot}`);
551
565
  const MINI_BACKFILL_TIMEOUT = 3e4;
552
- const startTime = Date.now();
566
+ let lastProgressTime = Date.now();
553
567
  let cursor;
554
568
  let itemsYielded = 0;
555
569
  try {
556
570
  while (true) {
557
- if (Date.now() - startTime > MINI_BACKFILL_TIMEOUT) {
558
- this.logger.warn(`mini-backfill timed out after ${MINI_BACKFILL_TIMEOUT}ms`);
571
+ if (Date.now() - lastProgressTime > MINI_BACKFILL_TIMEOUT) {
572
+ this.logger.warn(`mini-backfill timed out after ${MINI_BACKFILL_TIMEOUT}ms with no progress`);
559
573
  break;
560
574
  }
561
575
  const page = await fetchBackfill({ startSlot: fromSlot, cursor });
562
576
  const sorted = [...page.items].sort(
563
577
  (a, b) => compareBigint(extractSlot(a), extractSlot(b))
564
578
  );
579
+ let pageYielded = 0;
565
580
  for (const item of sorted) {
566
581
  const slot = extractSlot(item);
567
582
  const key = keyOf(item);
@@ -571,9 +586,11 @@ var ReplayStream = class {
571
586
  }
572
587
  recordEmission(slot, key);
573
588
  itemsYielded++;
589
+ pageYielded++;
574
590
  this.metrics.emittedReconnect += 1;
575
591
  yield item;
576
592
  }
593
+ if (pageYielded > 0) lastProgressTime = Date.now();
577
594
  cursor = page.cursor;
578
595
  if (page.done || cursor === void 0) break;
579
596
  }
@@ -587,7 +604,12 @@ var ReplayStream = class {
587
604
  };
588
605
  async function safeClose(pump) {
589
606
  try {
590
- await pump.close();
607
+ const result = await Promise.race([
608
+ pump.close().then(() => "closed"),
609
+ new Promise((resolve) => setTimeout(() => resolve("timeout"), 5e3))
610
+ ]);
611
+ if (result === "timeout") {
612
+ }
591
613
  } catch {
592
614
  }
593
615
  }
@@ -1074,6 +1096,7 @@ async function* createAccountsByOwnerReplay(options) {
1074
1096
  const streamBuffer = [];
1075
1097
  let streamDone = false;
1076
1098
  let streamError = null;
1099
+ let lastActivityTime = Date.now();
1077
1100
  try {
1078
1101
  cleanupTimer = setInterval(() => {
1079
1102
  assembler.cleanup();
@@ -1083,6 +1106,7 @@ async function* createAccountsByOwnerReplay(options) {
1083
1106
  const streamProcessor = (async () => {
1084
1107
  try {
1085
1108
  for await (const response of stream) {
1109
+ lastActivityTime = Date.now();
1086
1110
  const event = processResponseMulti(response, assembler);
1087
1111
  if (event) {
1088
1112
  if (event.type === "account") {
@@ -1172,6 +1196,7 @@ async function* createAccountsByOwnerReplay(options) {
1172
1196
  let retryAttempt = 0;
1173
1197
  let currentStream = stream;
1174
1198
  let currentStreamProcessor = streamProcessor;
1199
+ lastActivityTime = Date.now();
1175
1200
  const createStreamProcessor = () => {
1176
1201
  if (clientFactory) {
1177
1202
  try {
@@ -1187,6 +1212,7 @@ async function* createAccountsByOwnerReplay(options) {
1187
1212
  try {
1188
1213
  for await (const response of newStream) {
1189
1214
  retryAttempt = 0;
1215
+ lastActivityTime = Date.now();
1190
1216
  const event = processResponseMulti(response, assembler);
1191
1217
  if (event) {
1192
1218
  if (event.type === "account") {
@@ -1207,7 +1233,18 @@ async function* createAccountsByOwnerReplay(options) {
1207
1233
  return { stream: newStream, processor: newProcessor };
1208
1234
  };
1209
1235
  while (true) {
1236
+ const hadEvents = streamBuffer.length > 0;
1210
1237
  yield* yieldStreamBuffer();
1238
+ if (hadEvents) {
1239
+ lastActivityTime = Date.now();
1240
+ }
1241
+ if (!streamDone && Date.now() - lastActivityTime > retryConfig.connectionTimeoutMs) {
1242
+ logger.warn(
1243
+ `[account-stream] no activity for ${retryConfig.connectionTimeoutMs}ms; forcing reconnection`
1244
+ );
1245
+ streamDone = true;
1246
+ streamError = new Error(`Operation timed out after ${retryConfig.connectionTimeoutMs}ms`);
1247
+ }
1211
1248
  if (streamDone) {
1212
1249
  if (streamError) {
1213
1250
  const backoffMs = calculateBackoff(retryAttempt, retryConfig);
@@ -1219,6 +1256,7 @@ async function* createAccountsByOwnerReplay(options) {
1219
1256
  streamDone = false;
1220
1257
  streamError = null;
1221
1258
  streamBuffer.length = 0;
1259
+ lastActivityTime = Date.now();
1222
1260
  const { stream: newStream, processor: newProcessor } = createStreamProcessor();
1223
1261
  currentStream = newStream;
1224
1262
  currentStreamProcessor = newProcessor;
@@ -1226,6 +1264,7 @@ async function* createAccountsByOwnerReplay(options) {
1226
1264
  } else {
1227
1265
  logger.warn("[account-stream] stream ended unexpectedly; reconnecting...");
1228
1266
  streamDone = false;
1267
+ lastActivityTime = Date.now();
1229
1268
  const { stream: newStream, processor: newProcessor } = createStreamProcessor();
1230
1269
  currentStream = newStream;
1231
1270
  currentStreamProcessor = newProcessor;