@indigoai-us/hq-cloud 6.18.94 → 6.18.95
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/bin/sync-runner.d.ts +20 -1
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +25 -0
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/cli/reindex.d.ts +24 -0
- package/dist/cli/reindex.d.ts.map +1 -1
- package/dist/cli/reindex.js +26 -1
- package/dist/cli/reindex.js.map +1 -1
- package/dist/cli/share.d.ts +28 -5
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +142 -19
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/sync.d.ts +11 -1
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +56 -7
- package/dist/cli/sync.js.map +1 -1
- package/dist/hq-root-recovery.d.ts +37 -6
- package/dist/hq-root-recovery.d.ts.map +1 -1
- package/dist/hq-root-recovery.js +266 -22
- package/dist/hq-root-recovery.js.map +1 -1
- package/dist/journal.d.ts +11 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +23 -2
- package/dist/journal.js.map +1 -1
- package/dist/lib/cloud-authoritative.d.ts +10 -0
- package/dist/lib/cloud-authoritative.d.ts.map +1 -1
- package/dist/lib/cloud-authoritative.js +23 -0
- package/dist/lib/cloud-authoritative.js.map +1 -1
- package/dist/manifest-reconcile.d.ts +14 -0
- package/dist/manifest-reconcile.d.ts.map +1 -1
- package/dist/manifest-reconcile.js +20 -2
- package/dist/manifest-reconcile.js.map +1 -1
- package/dist/outposts/client.d.ts +6 -1
- package/dist/outposts/client.d.ts.map +1 -1
- package/dist/outposts/client.js +13 -1
- package/dist/outposts/client.js.map +1 -1
- package/dist/outposts/command.d.ts +1 -1
- package/dist/outposts/command.d.ts.map +1 -1
- package/dist/outposts/command.js +58 -2
- package/dist/outposts/command.js.map +1 -1
- package/dist/outposts/company.d.ts +15 -1
- package/dist/outposts/company.d.ts.map +1 -1
- package/dist/outposts/company.js +36 -1
- package/dist/outposts/company.js.map +1 -1
- package/dist/s3.d.ts +22 -0
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +44 -0
- package/dist/s3.js.map +1 -1
- package/dist/session-log-capture.d.ts.map +1 -1
- package/dist/session-log-capture.js +5 -2
- package/dist/session-log-capture.js.map +1 -1
- package/dist/sync/area-ledger.d.ts.map +1 -1
- package/dist/sync/area-ledger.js +3 -3
- package/dist/sync/area-ledger.js.map +1 -1
- package/dist/sync/durable-apply.js +2 -2
- package/dist/sync/durable-apply.js.map +1 -1
- package/dist/sync/per-area-maintenance.js +2 -2
- package/dist/sync/per-area-maintenance.js.map +1 -1
- package/dist/sync/reconcile-cursor.js +2 -2
- package/dist/sync/reconcile-cursor.js.map +1 -1
- package/dist/sync/secret-path-policy.d.ts +37 -11
- package/dist/sync/secret-path-policy.d.ts.map +1 -1
- package/dist/sync/secret-path-policy.js +68 -34
- package/dist/sync/secret-path-policy.js.map +1 -1
- package/dist/sync/snapshot-client.js +2 -2
- package/dist/sync/snapshot-client.js.map +1 -1
- package/dist/sync/state-store.d.ts +19 -0
- package/dist/sync/state-store.d.ts.map +1 -1
- package/dist/sync/state-store.js +184 -4
- package/dist/sync/state-store.js.map +1 -1
- package/dist/sync/uploader-finalization.d.ts.map +1 -1
- package/dist/sync/uploader-finalization.js +2 -2
- package/dist/sync/uploader-finalization.js.map +1 -1
- package/dist/test-fixtures/unknown-journal-generation.d.ts +9 -0
- package/dist/test-fixtures/unknown-journal-generation.d.ts.map +1 -0
- package/dist/test-fixtures/unknown-journal-generation.js +37 -0
- package/dist/test-fixtures/unknown-journal-generation.js.map +1 -0
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +3 -3
- package/dist/watcher.js.map +1 -1
- package/package.json +1 -1
package/dist/sync/state-store.js
CHANGED
|
@@ -79,6 +79,15 @@ const MALFORMED_LOCK_GRACE_MS = 250;
|
|
|
79
79
|
const SYNC_LOCK_TIMEOUT_MS = 5_000;
|
|
80
80
|
const ASYNC_LOCK_TIMEOUT_MS = 60_000;
|
|
81
81
|
const OPTIMISTIC_RECOVERY_MAX_RESAMPLES = 3;
|
|
82
|
+
/**
|
|
83
|
+
* Off-lock recovery passes before falling back to one read under append.lock.
|
|
84
|
+
* Each failed pass means a concurrent writer moved the generation or WAL
|
|
85
|
+
* between the read and its locked validation; without a ceiling a busy writer
|
|
86
|
+
* kept a one-shot sync spinning at full CPU for hours (feedback_b7c00a62).
|
|
87
|
+
*/
|
|
88
|
+
const OPTIMISTIC_RECOVERY_MAX_ATTEMPTS = 8;
|
|
89
|
+
/** Same ceiling for repairIfNeeded's plan loop, which then fails loudly. */
|
|
90
|
+
const MAINTENANCE_PLAN_MAX_ATTEMPTS = 8;
|
|
82
91
|
/** Base wall-clock budget for one contended fold. Scaled by WAL/record pressure. */
|
|
83
92
|
const COMPACTION_RETRY_BASE_BUDGET_MS = 50;
|
|
84
93
|
/** Append must not block indefinitely waiting to fold. */
|
|
@@ -333,6 +342,54 @@ export class StateStoreCorruptionError extends Error {
|
|
|
333
342
|
this.rejection = options.rejection;
|
|
334
343
|
}
|
|
335
344
|
}
|
|
345
|
+
export function readStateStoreRecoveryMarker(rootDir, scopeId) {
|
|
346
|
+
const markerPath = recoveryMarkerPath(stateDirectory(rootDir, digestScope(scopeId)));
|
|
347
|
+
try {
|
|
348
|
+
const parsed = JSON.parse(fs.readFileSync(markerPath, "utf8"));
|
|
349
|
+
if (!parsed || typeof parsed !== "object")
|
|
350
|
+
throw new Error(`state-store: invalid recovery marker at ${markerPath}`);
|
|
351
|
+
const marker = parsed;
|
|
352
|
+
if (marker.kind !== "rebuilt_from_prefix" || typeof marker.createdAt !== "string" || typeof marker.quarantinePath !== "string") {
|
|
353
|
+
throw new Error(`state-store: invalid recovery marker at ${markerPath}`);
|
|
354
|
+
}
|
|
355
|
+
return marker;
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
if (isMissing(error))
|
|
359
|
+
return undefined;
|
|
360
|
+
throw new Error(`state-store: cannot read recovery marker at ${markerPath}`, { cause: error });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
export function clearStateStoreRecoveryMarker(rootDir, scopeId) {
|
|
364
|
+
const dir = stateDirectory(rootDir, digestScope(scopeId));
|
|
365
|
+
const markerPath = recoveryMarkerPath(dir);
|
|
366
|
+
try {
|
|
367
|
+
fs.unlinkSync(markerPath);
|
|
368
|
+
fsyncDirectory(dir);
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
if (isMissing(error))
|
|
372
|
+
return;
|
|
373
|
+
throw new Error(`state-store: cannot clear recovery marker at ${markerPath}`, { cause: error });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
export class StateStoreIncompatibleGenerationError extends Error {
|
|
377
|
+
generation;
|
|
378
|
+
constructor(generation, options) {
|
|
379
|
+
super(`state-store: incompatible generation ${String(generation)}: a newer hq-cloud wrote it and this client must be upgraded before retrying. Generation files were left unchanged.`, options);
|
|
380
|
+
this.name = "StateStoreIncompatibleGenerationError";
|
|
381
|
+
this.generation = generation;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/** A reducer recognized the frame but does not recognize its WAL record type. */
|
|
385
|
+
export class StateStoreUnsupportedRecordTypeError extends Error {
|
|
386
|
+
recordType;
|
|
387
|
+
constructor(recordType, reducer) {
|
|
388
|
+
super(`${reducer}: unsupported WAL record type ${String(recordType)}`);
|
|
389
|
+
this.name = "StateStoreUnsupportedRecordTypeError";
|
|
390
|
+
this.recordType = recordType;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
336
393
|
/**
|
|
337
394
|
* A snapshot whose 8-byte magic this reader does not understand.
|
|
338
395
|
*
|
|
@@ -421,7 +478,15 @@ export class StateStore {
|
|
|
421
478
|
const dir = stateDirectory(normalized.rootDir, scopeDigest);
|
|
422
479
|
fs.mkdirSync(dir, { recursive: true });
|
|
423
480
|
while (true) {
|
|
424
|
-
|
|
481
|
+
let recovered;
|
|
482
|
+
try {
|
|
483
|
+
recovered = readNewestGenerationOptimistically(dir, scopeDigest, normalized.reduce, normalized.snapshotDecoder, normalized.snapshotMagics);
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
if (!normalized.recoverRejectedDelta || !isReducerRejectedGeneration(error))
|
|
487
|
+
throw error;
|
|
488
|
+
recovered = recoverRejectedDeltaGeneration(dir, scopeDigest, normalized);
|
|
489
|
+
}
|
|
425
490
|
if (recovered) {
|
|
426
491
|
const store = new StateStore(normalized, recovered, false);
|
|
427
492
|
// A process can die after its durable append but before append() gets
|
|
@@ -469,7 +534,15 @@ export class StateStore {
|
|
|
469
534
|
const dir = stateDirectory(normalized.rootDir, scopeDigest);
|
|
470
535
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
471
536
|
while (true) {
|
|
472
|
-
|
|
537
|
+
let recovered;
|
|
538
|
+
try {
|
|
539
|
+
recovered = await readNewestGenerationOptimisticallyAsync(dir, scopeDigest, normalized.reduce, normalized.snapshotDecoder, normalized.snapshotMagics);
|
|
540
|
+
}
|
|
541
|
+
catch (error) {
|
|
542
|
+
if (!normalized.recoverRejectedDelta || !isReducerRejectedGeneration(error))
|
|
543
|
+
throw error;
|
|
544
|
+
recovered = recoverRejectedDeltaGeneration(dir, scopeDigest, normalized);
|
|
545
|
+
}
|
|
473
546
|
if (recovered) {
|
|
474
547
|
const store = new StateStore(normalized, recovered, false);
|
|
475
548
|
// Match open(): a recovered over-budget WAL is compacted before this
|
|
@@ -589,7 +662,11 @@ export class StateStore {
|
|
|
589
662
|
if (!hasSnapshotGeneration(dir))
|
|
590
663
|
return { status: "missing" };
|
|
591
664
|
let plan;
|
|
665
|
+
let planAttempts = 0;
|
|
592
666
|
while (!plan) {
|
|
667
|
+
if (planAttempts++ >= MAINTENANCE_PLAN_MAX_ATTEMPTS) {
|
|
668
|
+
throw new StateStoreLockError("state-store maintenance did not converge: writers kept moving the store");
|
|
669
|
+
}
|
|
593
670
|
const preflight = withScopeLock(dir, () => {
|
|
594
671
|
cleanupUnpublishedInitialGeneration(dir);
|
|
595
672
|
const newestGeneration = highestCompleteGenerationOnDisk(dir);
|
|
@@ -1301,6 +1378,79 @@ export class StateStore {
|
|
|
1301
1378
|
this.observedHighestSnapshotGeneration = generation.observedHighestSnapshotGeneration ?? generation.generation;
|
|
1302
1379
|
}
|
|
1303
1380
|
}
|
|
1381
|
+
function isReducerRejectedGeneration(error) {
|
|
1382
|
+
if (!(error instanceof StateStoreCorruptionError))
|
|
1383
|
+
return false;
|
|
1384
|
+
const reducerError = error.cause;
|
|
1385
|
+
return reducerError instanceof StateStoreReducerError &&
|
|
1386
|
+
!(reducerError.cause instanceof StateStoreUnsupportedRecordTypeError);
|
|
1387
|
+
}
|
|
1388
|
+
function recoveryMarkerPath(dir) {
|
|
1389
|
+
return path.join(dir, "recovery-marker.json");
|
|
1390
|
+
}
|
|
1391
|
+
function recoverRejectedDeltaGeneration(dir, scopeDigest, options) {
|
|
1392
|
+
return withScopeLock(dir, () => {
|
|
1393
|
+
const generations = fs.readdirSync(dir)
|
|
1394
|
+
.map((name) => /^snapshot-(\d+)\.bin$/.exec(name)?.[1])
|
|
1395
|
+
.filter((value) => value !== undefined)
|
|
1396
|
+
.map(Number).filter(Number.isSafeInteger).sort((a, b) => b - a);
|
|
1397
|
+
const generation = highestCompleteGenerationOnDisk(dir);
|
|
1398
|
+
if (generation === undefined)
|
|
1399
|
+
throw new StateStoreCorruptionError("no complete generation available for rejected-delta recovery");
|
|
1400
|
+
const snapshot = readSnapshot(snapshotPath(dir, generation), scopeDigest, generation, options.snapshotDecoder, options.snapshotMagics);
|
|
1401
|
+
const sourceWalPath = walPath(dir, generation);
|
|
1402
|
+
const parsed = readWal(sourceWalPath, scopeDigest, generation, snapshot.payload.lastRecordSequence, snapshot.payload.state, options.reduce, 0, undefined, undefined, false, true);
|
|
1403
|
+
if (parsed.rejectedSuffixOffset === undefined) {
|
|
1404
|
+
return {
|
|
1405
|
+
generation,
|
|
1406
|
+
state: parsed.state,
|
|
1407
|
+
lastRecordSequence: parsed.lastRecordSequence,
|
|
1408
|
+
recordCount: parsed.recordCount,
|
|
1409
|
+
walBytes: parsed.walBytes,
|
|
1410
|
+
maxReadBytes: parsed.maxReadBytes,
|
|
1411
|
+
observedWalBytes: parsed.observedWalBytes,
|
|
1412
|
+
observedHighestSnapshotGeneration: generation,
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
const rejectedSequence = parsed.lastRecordSequence + 1;
|
|
1416
|
+
// The first replay may have partially mutated its state before rejection.
|
|
1417
|
+
// Re-decode the snapshot so the accepted-prefix replay starts from pristine bytes.
|
|
1418
|
+
const prefixSnapshot = readSnapshot(snapshotPath(dir, generation), scopeDigest, generation, options.snapshotDecoder, options.snapshotMagics);
|
|
1419
|
+
const prefix = readWal(sourceWalPath, scopeDigest, generation, prefixSnapshot.payload.lastRecordSequence, prefixSnapshot.payload.state, (state, record) => {
|
|
1420
|
+
if (record.sequence === rejectedSequence)
|
|
1421
|
+
throw new Error("stop before reducer-rejected record");
|
|
1422
|
+
return options.reduce(state, record);
|
|
1423
|
+
}, 0, undefined, undefined, false, true);
|
|
1424
|
+
if (prefix.rejectedSuffixOffset !== parsed.rejectedSuffixOffset) {
|
|
1425
|
+
throw new StateStoreCorruptionError(`generation ${generation} changed during rejected-delta recovery`);
|
|
1426
|
+
}
|
|
1427
|
+
const suffix = fs.readFileSync(sourceWalPath).subarray(parsed.rejectedSuffixOffset);
|
|
1428
|
+
const quarantinePath = path.join(dir, `wal-${generation}.rejected-${Date.now()}-${crypto.randomBytes(5).toString("hex")}.bin`);
|
|
1429
|
+
try {
|
|
1430
|
+
writeNewDurably(quarantinePath, suffix);
|
|
1431
|
+
const marker = {
|
|
1432
|
+
kind: "rebuilt_from_prefix",
|
|
1433
|
+
createdAt: new Date().toISOString(),
|
|
1434
|
+
quarantinePath,
|
|
1435
|
+
};
|
|
1436
|
+
writeDurableTempThenRename(dir, recoveryMarkerPath(dir), [Buffer.from(JSON.stringify(marker), "utf8")]);
|
|
1437
|
+
const next = {
|
|
1438
|
+
generation: Math.max(...generations) + 1,
|
|
1439
|
+
state: prefix.state,
|
|
1440
|
+
lastRecordSequence: prefix.lastRecordSequence,
|
|
1441
|
+
recordCount: 0,
|
|
1442
|
+
walBytes: WAL_HEADER_BYTES,
|
|
1443
|
+
maxReadBytes: parsed.maxReadBytes,
|
|
1444
|
+
};
|
|
1445
|
+
writeGeneration(dir, scopeDigest, next, options.snapshotEncoder);
|
|
1446
|
+
writeCurrentPointer(dir, next.generation);
|
|
1447
|
+
return next;
|
|
1448
|
+
}
|
|
1449
|
+
catch (error) {
|
|
1450
|
+
throw new Error(`state-store: rejected-delta recovery failed; inspect state path ${dir} and preserve its WAL files`, { cause: error });
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1304
1454
|
function readNewestGeneration(dir, scopeDigest, reduce, snapshotDecoder, snapshotMagics, onProgress, repairTornTail = true) {
|
|
1305
1455
|
const names = fs.readdirSync(dir);
|
|
1306
1456
|
const generations = names
|
|
@@ -1320,6 +1470,13 @@ function readNewestGeneration(dir, scopeDigest, reduce, snapshotDecoder, snapsho
|
|
|
1320
1470
|
}
|
|
1321
1471
|
catch (error) {
|
|
1322
1472
|
if (error instanceof StateStoreReducerError) {
|
|
1473
|
+
if (error.cause instanceof StateStoreUnsupportedRecordTypeError) {
|
|
1474
|
+
// The frame passed its integrity checks, and this reducer explicitly
|
|
1475
|
+
// identified a record type it does not know. Do not label that as
|
|
1476
|
+
// corruption: callers may quarantine corruption errors. Never fall
|
|
1477
|
+
// back to an older generation, which would lose newer writes.
|
|
1478
|
+
throw new StateStoreIncompatibleGenerationError(generation, { cause: error });
|
|
1479
|
+
}
|
|
1323
1480
|
// A CRC-valid record which the owning reducer cannot interpret is
|
|
1324
1481
|
// corrupt for that projection. Surface the public corruption contract
|
|
1325
1482
|
// so callers can quarantine/fail-close without depending on this
|
|
@@ -1378,7 +1535,16 @@ function readGeneration(dir, scopeDigest, generation, reduce, snapshotDecoder, s
|
|
|
1378
1535
|
*/
|
|
1379
1536
|
function readNewestGenerationOptimistically(dir, scopeDigest, reduce, snapshotDecoder, snapshotMagics, onProgress) {
|
|
1380
1537
|
let resamples = 0;
|
|
1538
|
+
let attempts = 0;
|
|
1381
1539
|
while (true) {
|
|
1540
|
+
if (attempts++ >= OPTIMISTIC_RECOVERY_MAX_ATTEMPTS) {
|
|
1541
|
+
// Writers kept moving the store under every optimistic pass. One read
|
|
1542
|
+
// under the lock always makes progress (and repairs a torn tail).
|
|
1543
|
+
return withScopeLock(dir, () => {
|
|
1544
|
+
cleanupUnpublishedInitialGeneration(dir);
|
|
1545
|
+
return readNewestGeneration(dir, scopeDigest, reduce, snapshotDecoder, snapshotMagics, onProgress, true);
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1382
1548
|
const candidate = withScopeLock(dir, () => {
|
|
1383
1549
|
cleanupUnpublishedInitialGeneration(dir);
|
|
1384
1550
|
return highestCompleteGenerationOnDisk(dir);
|
|
@@ -1409,7 +1575,16 @@ function readNewestGenerationOptimistically(dir, scopeDigest, reduce, snapshotDe
|
|
|
1409
1575
|
}
|
|
1410
1576
|
async function readNewestGenerationOptimisticallyAsync(dir, scopeDigest, reduce, snapshotDecoder, snapshotMagics, onProgress) {
|
|
1411
1577
|
let resamples = 0;
|
|
1578
|
+
let attempts = 0;
|
|
1412
1579
|
while (true) {
|
|
1580
|
+
if (attempts++ >= OPTIMISTIC_RECOVERY_MAX_ATTEMPTS) {
|
|
1581
|
+
// Writers kept moving the store under every optimistic pass. One read
|
|
1582
|
+
// under the lock always makes progress (and repairs a torn tail).
|
|
1583
|
+
return await withScopeLockAsync(dir, () => {
|
|
1584
|
+
cleanupUnpublishedInitialGeneration(dir);
|
|
1585
|
+
return readNewestGeneration(dir, scopeDigest, reduce, snapshotDecoder, snapshotMagics, onProgress, true);
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1413
1588
|
const candidate = await withScopeLockAsync(dir, () => {
|
|
1414
1589
|
cleanupUnpublishedInitialGeneration(dir);
|
|
1415
1590
|
return highestCompleteGenerationOnDisk(dir);
|
|
@@ -1844,7 +2019,7 @@ function readSnapshot(filePath, scopeDigest, generation, snapshotDecoder, snapsh
|
|
|
1844
2019
|
fs.closeSync(fd);
|
|
1845
2020
|
}
|
|
1846
2021
|
}
|
|
1847
|
-
function readWal(filePath, scopeDigest, generation, snapshotSequence, initialState, reduce, recoveryBytesBeforeWal = 0, recoveryTotalBytes, onProgress, repairTornTail = true) {
|
|
2022
|
+
function readWal(filePath, scopeDigest, generation, snapshotSequence, initialState, reduce, recoveryBytesBeforeWal = 0, recoveryTotalBytes, onProgress, repairTornTail = true, stopOnReducerRejection = false) {
|
|
1848
2023
|
let fd;
|
|
1849
2024
|
try {
|
|
1850
2025
|
fd = fs.openSync(filePath, "r");
|
|
@@ -1856,6 +2031,7 @@ function readWal(filePath, scopeDigest, generation, snapshotSequence, initialSta
|
|
|
1856
2031
|
throw new Error(`state-store: cannot read WAL generation ${generation}`, { cause: error });
|
|
1857
2032
|
}
|
|
1858
2033
|
let truncateBoundary;
|
|
2034
|
+
let rejectedSuffixOffset;
|
|
1859
2035
|
try {
|
|
1860
2036
|
const walBytes = fs.fstatSync(fd).size;
|
|
1861
2037
|
walReadHookForTest?.();
|
|
@@ -1916,7 +2092,10 @@ function readWal(filePath, scopeDigest, generation, snapshotSequence, initialSta
|
|
|
1916
2092
|
state = reduce(state, parsed.record);
|
|
1917
2093
|
}
|
|
1918
2094
|
catch (error) {
|
|
1919
|
-
|
|
2095
|
+
if (!stopOnReducerRejection)
|
|
2096
|
+
throw new StateStoreReducerError(parsed.record, error);
|
|
2097
|
+
rejectedSuffixOffset = offset;
|
|
2098
|
+
break;
|
|
1920
2099
|
}
|
|
1921
2100
|
walRecordReadHookForTest?.(parsed.record);
|
|
1922
2101
|
sequence = parsed.record.sequence;
|
|
@@ -1938,6 +2117,7 @@ function readWal(filePath, scopeDigest, generation, snapshotSequence, initialSta
|
|
|
1938
2117
|
walBytes: truncateBoundary ?? walBytes,
|
|
1939
2118
|
observedWalBytes: walBytes,
|
|
1940
2119
|
tornWalTailBoundary: truncateBoundary,
|
|
2120
|
+
rejectedSuffixOffset,
|
|
1941
2121
|
maxReadBytes,
|
|
1942
2122
|
};
|
|
1943
2123
|
}
|