Skip to content

Commit 48d45df

Browse files
committed
Strip down comments
1 parent 195ef11 commit 48d45df

7 files changed

Lines changed: 59 additions & 100 deletions

File tree

pnpm-workspace.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
# pnpm settings (pnpm ≥10.30 reads these from here, NOT from the
2-
# package.json "pnpm" field — overrides silently moved during the
3-
# 2026-06-12 git-dependency migration after we noticed the old location
4-
# was being ignored).
1+
# pnpm settings. They must live here: pnpm ≥10.3x silently ignores the
2+
# package.json "pnpm" field.
53

64
# ADR-021: force a single @automerge/automerge so the CJS and ESM module
75
# graphs share one Wasm instance. Two copies = two Wasm heaps = documents

src/commands.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,10 @@ async function setupCommandContext(
190190
* Safely shutdown a repository with proper error handling
191191
*/
192192
async function safeRepoShutdown(repo: Repo): Promise<void> {
193-
// The historical 3s pre-shutdown grace sleep is gone: since the
194-
// feed-macrotasks shutdown quiesce pass (ADR-023/ADR-024),
195-
// `repo.shutdown()` itself awaits in-flight sync rounds and runs a
196-
// final bounded round for un-broadcast commits — deterministically
197-
// doing what the sleep only hoped for. Verified 2026-06-12: online
198-
// init + clone byte-identical and the two-repo suite green with no
199-
// grace. PUSHWORK_SYNC_GRACE_MS remains as an escape hatch for
200-
// debugging delivery issues against slow/flaky relays.
193+
// `repo.shutdown()` quiesces outbound sync itself (awaits in-flight
194+
// rounds, then a final bounded round for un-broadcast commits), so no
195+
// grace sleep is needed. PUSHWORK_SYNC_GRACE_MS remains as an escape
196+
// hatch for debugging delivery against slow or flaky relays.
201197
const graceMs = Number(process.env.PUSHWORK_SYNC_GRACE_MS ?? 0);
202198
if (Number.isFinite(graceMs) && graceMs > 0) {
203199
await new Promise((resolve) => setTimeout(resolve, graceMs));

src/core/change-detection.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,15 @@ export class ChangeDetector {
5555
}
5656

5757
/**
58-
* Detect all changes between local filesystem and snapshot
58+
* Detect all changes between local filesystem and snapshot.
59+
*
60+
* Shard-mode (PUSHWORK_PARALLEL_INGEST=2) params:
61+
* - `freshPaths`: paths just pushed by worker-owned repos. Their snapshot
62+
* heads are already current; materializing their docs on the main
63+
* thread would reinstate the serial cost the workers exist to avoid,
64+
* so both local and remote detection skip them for this run.
65+
* - `deferRemoteContent`: emit URL-only changes for new remote files
66+
* (no doc materialization); shard-pull workers fetch them.
5967
*/
6068
async detectChanges(
6169
snapshot: SyncSnapshot,
@@ -110,9 +118,7 @@ export class ChangeDetector {
110118
await Promise.all(
111119
Array.from(currentFiles.entries()).map(
112120
async ([relativePath, fileInfo]) => {
113-
// Paths just written by this run with known-current heads
114-
// (shared-nothing worker ingest). Reading their docs here
115-
// would materialize them on the main thread for no signal.
121+
// Worker-pushed this run; see detectChanges docs.
116122
if (freshPaths?.has(relativePath)) return
117123

118124
const snapshotEntry = snapshot.files.get(relativePath)
@@ -290,10 +296,8 @@ export class ChangeDetector {
290296
await Promise.all(
291297
Array.from(snapshot.files.entries()).map(
292298
async ([relativePath, snapshotEntry]) => {
293-
// Paths just pushed via shared-nothing worker repos: heads
294-
// in the snapshot are current, and materializing the doc
295-
// here would defeat the experiment. Concurrent remote
296-
// edits are deferred to the next sync.
299+
// Worker-pushed this run; see detectChanges docs. Genuinely
300+
// concurrent remote edits surface on the next sync.
297301
if (freshPaths?.has(relativePath)) return
298302

299303
// Find the file's current entry in the remote directory hierarchy
@@ -461,12 +465,10 @@ export class ChangeDetector {
461465
// This is a remote file not in our snapshot
462466
const localContent = await this.getLocalContent(entryPath)
463467

464-
// EXPERIMENT (shared-nothing clone): when deferring,
465-
// don't materialize the file doc here — emit a
466-
// deferred change carrying only the entry URL, to be
467-
// fetched + written by a shard-pull worker. Only the
468-
// clean clone case (no local copy) is deferrable;
469-
// local/remote coexistence needs content to compare.
468+
// Deferred fetch (shard mode): emit a URL-only change for
469+
// a shard-pull worker. Only the clean case (no local
470+
// copy) is deferrable — coexistence needs content here
471+
// to compare.
470472
if (deferRemoteContent && localContent === null) {
471473
changes.push({
472474
path: entryPath,

src/core/sync-engine.ts

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,9 @@ export class SyncEngine {
144144
// Map from path to handle for leaf-first sync ordering
145145
// Path depth determines sync order (deepest first)
146146
private handlesByPath: Map<string, DocHandle<unknown>> = new Map()
147-
// Paths pushed by shard-ingest workers this run (shared-nothing
148-
// experiment). These docs were created, persisted, and uploaded in
149-
// worker-owned repos; the main thread must NOT materialize them —
150-
// detect:post and the snapshot head-update loop skip these paths.
147+
// Paths handled by worker-owned repos this run (shard mode). The main
148+
// thread must NOT materialize these docs — detect:post and the snapshot
149+
// head-update loop skip them; their reported heads are already current.
151150
private workerPushedPaths: Set<string> = new Set()
152151
private config: DirectoryConfig
153152

@@ -549,9 +548,7 @@ export class SyncEngine {
549548
undefined,
550549
currentFiles,
551550
undefined,
552-
// Shared-nothing experiment: don't materialize new remote
553-
// file docs during discovery; shard-pull workers fetch them.
554-
parallelIngestMode() === "shard"
551+
parallelIngestMode() === "shard" // defer remote fetches to workers
555552
)
556553
)
557554

@@ -709,24 +706,16 @@ export class SyncEngine {
709706
debug(`sync: excluding ${deletedPaths.size} deleted paths from re-detection`)
710707
}
711708
debug("sync: re-detecting changes after network sync")
712-
// Shared-nothing experiment: paths pushed via worker-owned repos
713-
// must not be re-checked here — that would repo.find (and thus
714-
// materialize) every doc on the main thread, reinstating the
715-
// serial cost the workers exist to avoid. Their snapshot heads
716-
// were written from worker reports moments ago; genuinely
717-
// concurrent remote edits get picked up on the next sync.
718709
const freshChanges = await profileAsync("detect:post", () =>
719710
this.changeDetector.detectChanges(
720711
snapshot,
721712
deletedPaths,
722713
currentFiles,
714+
// Skip worker-pushed paths (see workerPushedPaths).
723715
this.workerPushedPaths.size > 0
724716
? this.workerPushedPaths
725717
: undefined,
726-
// Shared-nothing experiment: emit deferred (URL-only)
727-
// changes for new remote files; pull routes them to
728-
// shard-pull workers.
729-
parallelIngestMode() === "shard"
718+
parallelIngestMode() === "shard" // defer remote fetches to workers
730719
)
731720
)
732721
const freshRemoteChanges = freshChanges.filter(
@@ -759,9 +748,7 @@ export class SyncEngine {
759748
// can't find the entries to splice out).
760749
await profileAsync("snapshot:head-update", async () => {
761750
for (const [filePath, snapshotEntry] of snapshot.files.entries()) {
762-
// Shared-nothing experiment: worker-pushed docs aren't (and
763-
// must not be) materialized on the main thread; their heads
764-
// were recorded from the worker reports and are current.
751+
// Worker-pushed heads are already current (see workerPushedPaths).
765752
if (this.workerPushedPaths.has(filePath)) continue
766753
try {
767754
const handle = await this.repo.find(getPlainUrl(snapshotEntry.url))
@@ -883,16 +870,11 @@ export class SyncEngine {
883870
debug(`push: ${localChanges.length} local changes (${newFiles.length} new, ${modifiedFiles.length} modified, ${deletedFiles.length} deleted)`)
884871
out.update(`Pushing ${localChanges.length} local changes (${newFiles.length} new, ${modifiedFiles.length} modified, ${deletedFiles.length} deleted)`)
885872

886-
// EXPERIMENT (PUSHWORK_PARALLEL_INGEST=2, "shard"): build + persist +
887-
// upload the docs for NEW files in worker-owned repos (darn-style
888-
// parallel leaf phase), then proceed with the normal serial directory
889-
// phase below. Workers own full repos (own Wasm/storage-writes/
890-
// socket); the main thread never materializes the docs, only records
891-
// {url, heads}. Files whose worker failed aren't in the map and fall
892-
// back to main-thread creation in the per-file loop.
893-
// (The "import" variant — workers shipping doc bytes for main-thread
894-
// repo.import — was measured wall-neutral and removed; see
895-
// .ignore/PARALLEL_INGEST_EXPERIMENT.md.)
873+
// Shard mode (PUSHWORK_PARALLEL_INGEST=2): NEW files are built,
874+
// persisted, and uploaded by worker-owned repos; the serial directory
875+
// phase below stitches the reported {url, heads} in. Files whose
876+
// worker failed are absent from the map and fall back to main-thread
877+
// creation. See ingestNewFilesInShardedRepos.
896878
const ingestMode = newFiles.length > 1 ? parallelIngestMode() : null
897879
let shardResults = new Map<string, {url: AutomergeUrl; heads: UrlHeads}>()
898880
if (ingestMode === "shard") {
@@ -1168,10 +1150,8 @@ export class SyncEngine {
11681150
c.changeType === ChangeType.BOTH_CHANGED
11691151
)
11701152

1171-
// EXPERIMENT (shared-nothing clone): deferred changes carry only
1172-
// an entry URL — fetch + materialize + write them in shard-pull
1173-
// workers. Failed paths fall back to the normal serial loop by
1174-
// re-fetching content on the main thread.
1153+
// Deferred changes (shard mode) carry only an entry URL; shard-pull
1154+
// workers fetch + write them. Failures fall back to the serial loop.
11751155
const deferred = remoteChanges.filter(c => c.deferredFetch)
11761156
const immediate = remoteChanges.filter(c => !c.deferredFetch)
11771157
let fallbackPaths: Set<string> | undefined
@@ -1230,14 +1210,13 @@ export class SyncEngine {
12301210
}
12311211

12321212
/**
1233-
* EXPERIMENT (shared-nothing clone): pull deferred remote files in
1234-
* worker-owned repos. Workers download/load + materialize the docs
1235-
* with their own Wasm instances, write the local files themselves,
1236-
* and report `{url, heads, contentHash}`; the main thread records
1237-
* snapshot entries without ever materializing the documents.
1213+
* Shard-mode pull: fetch deferred remote files in worker-owned repos.
1214+
* Workers materialize the docs with their own Wasm instances, write the
1215+
* local files themselves, and report `{url, heads, contentHash}`; the
1216+
* main thread records snapshot entries without materializing anything.
12381217
*
1239-
* Returns the set of paths the workers could NOT handle (caller runs
1240-
* the main-thread fallback for those).
1218+
* Returns the paths the workers could not handle (caller runs the
1219+
* main-thread fallback for those).
12411220
*/
12421221
private async pullRemoteFilesInShardedRepos(
12431222
deferred: DetectedChange[],
@@ -1507,18 +1486,18 @@ export class SyncEngine {
15071486
}
15081487

15091488
/**
1510-
* EXPERIMENT (shared-nothing): ingest new files in worker-owned repos.
1489+
* Shard-mode push: ingest new files in worker-owned repos.
15111490
*
1512-
* Workers build, persist (shared storage directory, disjoint doc
1513-
* keys), and upload their shard with their OWN Repo + Wasm + socket,
1514-
* reporting only `{url, heads}`. The main thread never materializes
1515-
* these docs; the per-file loop stitches the URLs into directory
1516-
* entries and the snapshot.
1491+
* Workers build, persist (shared storage directory, disjoint doc keys),
1492+
* and upload their shard with their own Repo + Wasm + socket, reporting
1493+
* only `{url, heads}`. The main thread never materializes these docs;
1494+
* the per-file loop stitches the URLs into directory entries and the
1495+
* snapshot.
15171496
*
15181497
* Paths that failed or did not converge to the server are absent from
15191498
* the returned map, so the per-file loop falls back to main-thread
1520-
* `createRemoteFile` for them (the worker's copy becomes an
1521-
* unreferenced orphan in storage, same class as deleted-file orphans).
1499+
* `createRemoteFile` (the worker's copy becomes an unreferenced storage
1500+
* orphan, same class as deleted-file orphans).
15221501
*/
15231502
private async ingestNewFilesInShardedRepos(
15241503
newFiles: DetectedChange[],

src/types/documents.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,9 @@ export interface DetectedChange {
8989
/** New remote URL when the remote document was replaced (artifact URL change) */
9090
remoteUrl?: AutomergeUrl
9191
/**
92-
* EXPERIMENT (shared-nothing clone): the remote document was NOT
93-
* materialized during discovery — `remoteContent`/`remoteHead` are
94-
* unset and `remoteUrl` carries the directory entry URL. These
95-
* changes are routed to shard-pull workers instead of the normal
96-
* pull loop.
92+
* Shard mode: the remote doc was not materialized during discovery —
93+
* `remoteContent`/`remoteHead` are unset, `remoteUrl` carries the entry
94+
* URL, and the change is routed to a shard-pull worker.
9795
*/
9896
deferredFetch?: boolean
9997
}

src/utils/fs.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,6 @@ export async function movePath(
241241
await fs.rename(sourcePath, destPath)
242242
}
243243

244-
// (calculateContentHash removed 2026-06-12: it was an unused duplicate of
245-
// utils/content.ts's contentHash — use that.)
246-
247244
/**
248245
* Get MIME type for file
249246
*/

src/utils/ingest-pool.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/**
2-
* Worker pools for the shared-nothing parallel-ingest experiment
2+
* Worker pools for shared-nothing parallel ingest
33
* (PUSHWORK_PARALLEL_INGEST=2 / "shard"): workers own full Repos (own
44
* Wasm, own storage writes, own socket) and report only {relPath, url,
5-
* heads}; the main thread never materializes the docs.
5+
* heads}; the main thread never materializes the docs. Background and
6+
* measurements: .ignore/PARALLEL_INGEST_EXPERIMENT.md.
67
*
78
* Worker scripts run as compiled CommonJS, so a build (`npm run build`)
89
* must exist even when the engine itself runs from src via tsx.
9-
*
10-
* (A second variant — "import" mode, workers shipping doc bytes for the
11-
* main thread to repo.import — was measured wall-neutral and deleted
12-
* 2026-06-12; see .ignore/PARALLEL_INGEST_EXPERIMENT.md.)
1310
*/
1411

1512
import * as fs from "node:fs"
@@ -30,10 +27,7 @@ export interface IngestTask {
3027

3128
export type ParallelIngestMode = "shard" | null
3229

33-
/**
34-
* PUSHWORK_PARALLEL_INGEST=2 (or "shard") enables shared-nothing shard
35-
* ingest. ("1"/"import" was the refuted repo.import variant, removed.)
36-
*/
30+
/** PUSHWORK_PARALLEL_INGEST=2 (or "shard") enables shard ingest. */
3731
export function parallelIngestMode(): ParallelIngestMode {
3832
switch (process.env.PUSHWORK_PARALLEL_INGEST) {
3933
case "2":
@@ -91,9 +85,7 @@ export async function runShardIngest(
9185
protocol: SyncProtocol,
9286
tasks: IngestTask[],
9387
workerCount = ingestWorkerCount(),
94-
// Test seam: inject a stub worker script (e.g. one that exits without
95-
// reporting) to exercise the pool's failure paths without Wasm.
96-
workerScript?: string
88+
workerScript?: string // test seam: stub script exercising failure paths
9789
): Promise<ShardRunOutcome> {
9890
const script = workerScript ?? resolveShardWorkerScript()
9991
const count = Math.min(workerCount, tasks.length)
@@ -241,10 +233,7 @@ export async function runShardPull(
241233
resolve()
242234
})
243235
worker.on("exit", () => {
244-
// Exit-before-report is a failure regardless of exit code: a
245-
// worker that process.exit(0)s without posting still lost its
246-
// shard. (fail() is a no-op once the report has arrived, which
247-
// covers the eager-terminate exit after a successful report.)
236+
// Exit-before-report = failure (see runShardIngest).
248237
fail()
249238
resolve()
250239
})

0 commit comments

Comments
 (0)