@indigoai-us/hq-cloud 6.18.41-alpha.1 → 6.18.41-alpha.2
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-watch-loop.d.ts +16 -1
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +49 -15
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.test.js +124 -1
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ import { PresignObjectIO, S3SdkObjectIO } from "../object-io.js";
|
|
|
21
21
|
import { BandwidthGovernor, setBandwidthGovernorForTesting, } from "../bandwidth.js";
|
|
22
22
|
import { POST_SYNC_FLAG_KEYS, } from "./sync-runner-post-sync-flags.js";
|
|
23
23
|
import { FakeClock, TreeWatcher, createWatchPathFilter, } from "../watcher.js";
|
|
24
|
-
import { adaptivePollMs, advanceMonotonicDeadline, DEFAULT_EVENT_BATCH_LIMIT, DEFAULT_FAST_LANE_MAX_BYTES, DEFAULT_FAST_LANE_MAX_FILE_BYTES, DEFAULT_FAST_LANE_MAX_FILES, DEFAULT_FULL_RECONCILE_MS, EVENT_BATCH_LIMIT_ENV, FAST_LANE_MAX_BYTES_ENV, FAST_LANE_MAX_FILES_ENV, FULL_RECONCILE_MS_ENV, isFastLocalBatch, isLargeLocalUpload, localUploadSizeClass, isChoreLocalPush, isSessionLogsPushOnlyPath, isFastReceiverBatch, partitionLocalBatch, journalEntriesForWatcherDelete, resolveEventBatchLimit, resolveFullReconcileMs, resolveWatchPollMs, CHORE_STARVATION_CAP_MS, CooperativePassScheduler, allowConcurrentHandoffForGuardedWork, REALTIME_ADMISSION_DEADLINE_MS, REALTIME_DRAIN_BURST_MS, REALTIME_WORKER_OCCUPANCY_CAP, REALTIME_LARGE_UPLOAD_THRESHOLD_BYTES, RealtimeAdmissionTimeoutError, setRealtimeAdmissionDeadlineMsForTesting, setRealtimeDrainBurstMsForTesting, normalizeCoordinatorPaths, shouldQuarantineWatcherDeleteIntents, shouldUseAreaStorage, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS, } from "./sync-runner-watch-loop.js";
|
|
24
|
+
import { adaptivePollMs, advanceMonotonicDeadline, DEFAULT_EVENT_BATCH_LIMIT, DEFAULT_FAST_LANE_MAX_BYTES, DEFAULT_FAST_LANE_MAX_FILE_BYTES, DEFAULT_FAST_LANE_MAX_FILES, DEFAULT_FULL_RECONCILE_MS, EVENT_BATCH_LIMIT_ENV, FAST_LANE_MAX_BYTES_ENV, FAST_LANE_MAX_FILES_ENV, FULL_RECONCILE_MS_ENV, isFastLocalBatch, isLargeLocalUpload, localUploadSizeClass, isChoreLocalPush, isSessionLogsPushOnlyPath, isFastReceiverBatch, partitionLocalBatch, journalEntriesForWatcherDelete, resolveEventBatchLimit, resolveFullReconcileMs, resolveWatchPollMs, CHORE_STARVATION_CAP_MS, CooperativePassScheduler, realtimeBlockerLabel, allowConcurrentHandoffForGuardedWork, REALTIME_ADMISSION_DEADLINE_MS, REALTIME_DRAIN_BURST_MS, REALTIME_WORKER_OCCUPANCY_CAP, REALTIME_LARGE_UPLOAD_THRESHOLD_BYTES, RealtimeAdmissionTimeoutError, setRealtimeAdmissionDeadlineMsForTesting, setRealtimeDrainBurstMsForTesting, normalizeCoordinatorPaths, shouldQuarantineWatcherDeleteIntents, shouldUseAreaStorage, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS, } from "./sync-runner-watch-loop.js";
|
|
25
25
|
import { FullPassCapController, resetFullPassCapLogForTest, } from "../sync/full-pass-caps.js";
|
|
26
26
|
import { AreaLedgerMigration, JournalStore, getJournalPath, openJournalStateStoreForMigration, PERSONAL_VAULT_JOURNAL_SLUG, readJournal, readJournalForPass, readJournalScoped, readJournalScopedAsync, writeJournal, } from "../journal.js";
|
|
27
27
|
import { AreaLedger } from "../sync/area-ledger.js";
|
|
@@ -10381,6 +10381,86 @@ describe("runRunnerWithLoop — U58 realtime scheduler guard", () => {
|
|
|
10381
10381
|
process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
|
|
10382
10382
|
}
|
|
10383
10383
|
});
|
|
10384
|
+
it("REGRESSION: a wake drain held behind a running chore names that chore", async () => {
|
|
10385
|
+
const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
10386
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
|
|
10387
|
+
const watcher = makeWatcherStub();
|
|
10388
|
+
let triggerShutdown = () => { };
|
|
10389
|
+
let releaseCheckpoint = () => { };
|
|
10390
|
+
const checkpointGate = new Promise((resolve) => { releaseCheckpoint = resolve; });
|
|
10391
|
+
let markChoreHeld = () => { };
|
|
10392
|
+
const choreHeld = new Promise((resolve) => { markChoreHeld = resolve; });
|
|
10393
|
+
let passes = 0;
|
|
10394
|
+
let runRealtime = null;
|
|
10395
|
+
const timings = [];
|
|
10396
|
+
const stderr = vi.spyOn(process.stderr, "write").mockImplementation((chunk) => {
|
|
10397
|
+
if (typeof chunk === "string" && chunk.startsWith("{")) {
|
|
10398
|
+
try {
|
|
10399
|
+
const record = JSON.parse(chunk);
|
|
10400
|
+
if (record.event === "sync.realtime.timing")
|
|
10401
|
+
timings.push(record);
|
|
10402
|
+
}
|
|
10403
|
+
catch {
|
|
10404
|
+
// Non-telemetry diagnostics are outside this assertion.
|
|
10405
|
+
}
|
|
10406
|
+
}
|
|
10407
|
+
return true;
|
|
10408
|
+
});
|
|
10409
|
+
const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
|
|
10410
|
+
runPass: vi.fn().mockImplementation(async (_argv, checkpoint) => {
|
|
10411
|
+
passes += 1;
|
|
10412
|
+
if (passes === 2) {
|
|
10413
|
+
markChoreHeld();
|
|
10414
|
+
await checkpointGate;
|
|
10415
|
+
await checkpoint?.();
|
|
10416
|
+
}
|
|
10417
|
+
return completedPassOutcome();
|
|
10418
|
+
}),
|
|
10419
|
+
runPassSupportsCooperativeCheckpoint: true,
|
|
10420
|
+
clock: new FakeClock(),
|
|
10421
|
+
createWatcher: () => watcher,
|
|
10422
|
+
sleep: makeSteppableSleep().sleep,
|
|
10423
|
+
onShutdownSignal: (handler) => {
|
|
10424
|
+
triggerShutdown = handler;
|
|
10425
|
+
return () => { };
|
|
10426
|
+
},
|
|
10427
|
+
startRealtimeScheduler: async (opts) => {
|
|
10428
|
+
runRealtime = () => opts.runGuarded(async () => undefined);
|
|
10429
|
+
return {
|
|
10430
|
+
signal: () => undefined,
|
|
10431
|
+
checkHighWater: async () => undefined,
|
|
10432
|
+
dispose: async () => undefined,
|
|
10433
|
+
};
|
|
10434
|
+
},
|
|
10435
|
+
});
|
|
10436
|
+
try {
|
|
10437
|
+
await flushLoopMicrotasks(30);
|
|
10438
|
+
watcher.emit();
|
|
10439
|
+
await choreHeld;
|
|
10440
|
+
await flushLoopMicrotasks(10);
|
|
10441
|
+
const wake = runRealtime();
|
|
10442
|
+
await flushLoopMicrotasks(20);
|
|
10443
|
+
releaseCheckpoint();
|
|
10444
|
+
await wake;
|
|
10445
|
+
await flushLoopMicrotasks(20);
|
|
10446
|
+
const wakeTimings = timings.filter((record) => record.source === "wake");
|
|
10447
|
+
expect(wakeTimings).toHaveLength(1);
|
|
10448
|
+
expect(wakeTimings[0]).toMatchObject({
|
|
10449
|
+
schemaVersion: 6,
|
|
10450
|
+
operationGateBlockedBy: "chore:full-reconcile",
|
|
10451
|
+
});
|
|
10452
|
+
}
|
|
10453
|
+
finally {
|
|
10454
|
+
releaseCheckpoint();
|
|
10455
|
+
triggerShutdown();
|
|
10456
|
+
await loop;
|
|
10457
|
+
stderr.mockRestore();
|
|
10458
|
+
if (previousCoordinator === undefined)
|
|
10459
|
+
delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
10460
|
+
else
|
|
10461
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
|
|
10462
|
+
}
|
|
10463
|
+
});
|
|
10384
10464
|
it("feeds a watcher-originated V2 drain through the same single-flight guard as the poll", async () => {
|
|
10385
10465
|
const watcher = makeWatcherStub();
|
|
10386
10466
|
let triggerShutdown = () => { };
|
|
@@ -11385,6 +11465,49 @@ describe("CooperativePassScheduler", () => {
|
|
|
11385
11465
|
releaseSecond();
|
|
11386
11466
|
await Promise.all([second, later]);
|
|
11387
11467
|
});
|
|
11468
|
+
it("names the hand-off occupant that refuses admission, then the chore lending the slot", async () => {
|
|
11469
|
+
const scheduler = new CooperativePassScheduler(() => 0);
|
|
11470
|
+
const heldBy = [];
|
|
11471
|
+
let releaseCheckpoint = () => { };
|
|
11472
|
+
const checkpointGate = new Promise((resolve) => { releaseCheckpoint = resolve; });
|
|
11473
|
+
let releaseOccupant = () => { };
|
|
11474
|
+
const occupantGate = new Promise((resolve) => { releaseOccupant = resolve; });
|
|
11475
|
+
let markOccupantStarted = () => { };
|
|
11476
|
+
const occupantStarted = new Promise((resolve) => { markOccupantStarted = resolve; });
|
|
11477
|
+
const chore = scheduler.submit(async ({ checkpoint }) => {
|
|
11478
|
+
await checkpointGate;
|
|
11479
|
+
await checkpoint();
|
|
11480
|
+
return 0;
|
|
11481
|
+
}, "chore", "slow", false, undefined, true, false, undefined, "unknown", undefined, "full-reconcile");
|
|
11482
|
+
await Promise.resolve();
|
|
11483
|
+
// A coordinator-fenced push waits for the chore, then runs as a hand-off occupant.
|
|
11484
|
+
const occupant = scheduler.submit(async () => {
|
|
11485
|
+
markOccupantStarted();
|
|
11486
|
+
await occupantGate;
|
|
11487
|
+
return 0;
|
|
11488
|
+
}, "realtime", "slow", false, undefined, false, true, "push", "small");
|
|
11489
|
+
releaseCheckpoint();
|
|
11490
|
+
await occupantStarted;
|
|
11491
|
+
expect(scheduler.handoffActive).toBe(true);
|
|
11492
|
+
// A second push cannot share the occupant's direction, so the occupant holds it.
|
|
11493
|
+
const samePush = scheduler.submit(async () => 0, "realtime", "slow", false, undefined, false, true, "push", "small", (blocker) => heldBy.push(blocker));
|
|
11494
|
+
// A legacy (unfenced) op cannot join the hand-off at all; the lending chore holds it.
|
|
11495
|
+
const legacy = scheduler.submit(async () => 0, "realtime", "slow", false, undefined, false, false, undefined, "unknown", (blocker) => heldBy.push(blocker));
|
|
11496
|
+
expect(heldBy).toEqual([
|
|
11497
|
+
{ workClass: "realtime", lane: "slow" },
|
|
11498
|
+
{ workClass: "chore", lane: "slow", choreKind: "full-reconcile" },
|
|
11499
|
+
]);
|
|
11500
|
+
releaseOccupant();
|
|
11501
|
+
await Promise.all([chore, occupant, samePush, legacy]);
|
|
11502
|
+
});
|
|
11503
|
+
it("labels an unknown chore kind as chore:unscoped instead of failing the timing record", () => {
|
|
11504
|
+
expect(realtimeBlockerLabel({ workClass: "chore", lane: "slow", choreKind: "full-reconcile" }))
|
|
11505
|
+
.toBe("chore:full-reconcile");
|
|
11506
|
+
expect(realtimeBlockerLabel({ workClass: "chore", lane: "slow", choreKind: "future-kind" }))
|
|
11507
|
+
.toBe("chore:unscoped");
|
|
11508
|
+
expect(realtimeBlockerLabel({ workClass: "chore", lane: "slow" })).toBe("chore:unscoped");
|
|
11509
|
+
expect(realtimeBlockerLabel({ workClass: "realtime", lane: "fast" })).toBe("realtime:fast");
|
|
11510
|
+
});
|
|
11388
11511
|
it("refuses to wait past 2x the authorization timeout when the chore never yields", async () => {
|
|
11389
11512
|
setRealtimeAdmissionDeadlineMsForTesting(40);
|
|
11390
11513
|
const scheduler = new CooperativePassScheduler(() => 0);
|