@dench.com/cli 2.8.6-staging.178 → 2.8.7-staging.189
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/fs-daemon-pull-queue.ts +35 -0
- package/fs-daemon.ts +38 -20
- package/package.json +2 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialize pull-queue drains without dropping notifications that arrive
|
|
3
|
+
* while a drain is already in progress.
|
|
4
|
+
*/
|
|
5
|
+
export function createPullQueueDrain(args: {
|
|
6
|
+
processRequests: () => Promise<void>;
|
|
7
|
+
onError: (error: unknown) => void;
|
|
8
|
+
}): () => Promise<void> {
|
|
9
|
+
let inFlight: Promise<void> | null = null;
|
|
10
|
+
let rerunRequested = false;
|
|
11
|
+
|
|
12
|
+
return function drain(): Promise<void> {
|
|
13
|
+
if (inFlight) {
|
|
14
|
+
rerunRequested = true;
|
|
15
|
+
return inFlight;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
inFlight = (async () => {
|
|
19
|
+
try {
|
|
20
|
+
do {
|
|
21
|
+
rerunRequested = false;
|
|
22
|
+
try {
|
|
23
|
+
await args.processRequests();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
args.onError(error);
|
|
26
|
+
}
|
|
27
|
+
} while (rerunRequested);
|
|
28
|
+
} finally {
|
|
29
|
+
inFlight = null;
|
|
30
|
+
}
|
|
31
|
+
})();
|
|
32
|
+
|
|
33
|
+
return inFlight;
|
|
34
|
+
};
|
|
35
|
+
}
|
package/fs-daemon.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
mkdir,
|
|
57
57
|
readdir,
|
|
58
58
|
readFile,
|
|
59
|
+
rename,
|
|
59
60
|
stat,
|
|
60
61
|
unlink,
|
|
61
62
|
writeFile,
|
|
@@ -76,6 +77,7 @@ import {
|
|
|
76
77
|
type MountInfo,
|
|
77
78
|
readMountInfo,
|
|
78
79
|
} from "./fs-daemon-mount";
|
|
80
|
+
import { createPullQueueDrain } from "./fs-daemon-pull-queue";
|
|
79
81
|
|
|
80
82
|
type FileTreeSyncRow = {
|
|
81
83
|
path: string;
|
|
@@ -731,7 +733,34 @@ async function runDaemon(daemonArgs: Args): Promise<void> {
|
|
|
731
733
|
status.recordBreakerEvent(breakerState);
|
|
732
734
|
void status.flush();
|
|
733
735
|
});
|
|
736
|
+
|
|
737
|
+
// Register the pull signal before publishing the daemon status. `fs pull`
|
|
738
|
+
// treats that status as readiness; publishing first left a startup window
|
|
739
|
+
// where SIGUSR2 was lost (or terminated the daemon) and the request timed
|
|
740
|
+
// out. A queued rerun also prevents signals received mid-drain from being
|
|
741
|
+
// coalesced away.
|
|
742
|
+
const drainPullRequests = createPullQueueDrain({
|
|
743
|
+
processRequests: () =>
|
|
744
|
+
processPullRequests({
|
|
745
|
+
client,
|
|
746
|
+
tracker,
|
|
747
|
+
workspace: daemonArgs.workspace,
|
|
748
|
+
}),
|
|
749
|
+
onError: (error) =>
|
|
750
|
+
console.error(
|
|
751
|
+
`[dench-fs-daemon] pull queue failed: ${
|
|
752
|
+
error instanceof Error ? error.message : String(error)
|
|
753
|
+
}`,
|
|
754
|
+
),
|
|
755
|
+
});
|
|
756
|
+
process.on("SIGUSR2", () => {
|
|
757
|
+
console.log(`[dench-fs-daemon] SIGUSR2 received — processing pull queue`);
|
|
758
|
+
void drainPullRequests();
|
|
759
|
+
});
|
|
734
760
|
await status.flush();
|
|
761
|
+
// Recover requests left behind if a previous daemon exited after the
|
|
762
|
+
// request file was written but before it could process the signal.
|
|
763
|
+
void drainPullRequests();
|
|
735
764
|
|
|
736
765
|
let chokidar: typeof import("chokidar");
|
|
737
766
|
try {
|
|
@@ -940,21 +969,6 @@ async function runDaemon(daemonArgs: Args): Promise<void> {
|
|
|
940
969
|
.catch(() => undefined);
|
|
941
970
|
});
|
|
942
971
|
|
|
943
|
-
process.on("SIGUSR2", () => {
|
|
944
|
-
console.log(`[dench-fs-daemon] SIGUSR2 received — processing pull queue`);
|
|
945
|
-
void processPullRequests({
|
|
946
|
-
client,
|
|
947
|
-
tracker,
|
|
948
|
-
workspace: daemonArgs.workspace,
|
|
949
|
-
}).catch((error) =>
|
|
950
|
-
console.error(
|
|
951
|
-
`[dench-fs-daemon] pull queue failed: ${
|
|
952
|
-
error instanceof Error ? error.message : String(error)
|
|
953
|
-
}`,
|
|
954
|
-
),
|
|
955
|
-
);
|
|
956
|
-
});
|
|
957
|
-
|
|
958
972
|
process.on("SIGTERM", () => {
|
|
959
973
|
reconciler.stop();
|
|
960
974
|
unsubscribe();
|
|
@@ -1505,11 +1519,15 @@ async function runPull(argv: string[]): Promise<void> {
|
|
|
1505
1519
|
await mkdir(PULL_REQUEST_DIR, { recursive: true });
|
|
1506
1520
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1507
1521
|
const request: PullRequest = { id, paths, requestedAt: Date.now() };
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
"utf-8"
|
|
1512
|
-
|
|
1522
|
+
const requestPath = `${PULL_REQUEST_DIR}/request-${id}.json`;
|
|
1523
|
+
const temporaryRequestPath = `${requestPath}.${process.pid}.tmp`;
|
|
1524
|
+
try {
|
|
1525
|
+
await writeFile(temporaryRequestPath, JSON.stringify(request), "utf-8");
|
|
1526
|
+
await rename(temporaryRequestPath, requestPath);
|
|
1527
|
+
} catch (error) {
|
|
1528
|
+
await unlink(temporaryRequestPath).catch(() => undefined);
|
|
1529
|
+
throw error;
|
|
1530
|
+
}
|
|
1513
1531
|
process.kill(status.pid, "SIGUSR2");
|
|
1514
1532
|
if (!wait) {
|
|
1515
1533
|
const response = { ok: true, id, queued: paths };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dench.com/cli",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.7-staging.189",
|
|
4
4
|
"description": "Dench agent workspace CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"fs-daemon-binary.ts",
|
|
18
18
|
"fs-daemon-flush.ts",
|
|
19
19
|
"fs-daemon-mount.ts",
|
|
20
|
+
"fs-daemon-pull-queue.ts",
|
|
20
21
|
"home-sync",
|
|
21
22
|
"home-sync.ts",
|
|
22
23
|
"home-sync-lib.ts",
|