Repository navigation
Persist pooled PHP writes to saved sites - #4260
Conversation
631fe16 to
f5fc044
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Persist writes made by pooled PHP replicas to autosaved OPFS sites by forwarding replica request completion back to the filesystem owner, and verify persistence via a Chromium E2E regression test.
Changes:
- Forward
request.endfrom replica runtimes asproxyfs.request.endon the source-of-truth runtime. - Trigger OPFS journal flushes on
proxyfs.request.endin addition to existing flush triggers. - Add a Chromium Playwright test that deterministically reproduces (and guards against) the pooled-write persistence regression.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/playground/website/playwright/e2e/opfs.spec.ts | Adds an E2E regression test and OPFS helper to assert pooled replica writes persist to OPFS. |
| packages/php-wasm/web/src/lib/directory-handle-mount.ts | Flush OPFS journal when the new proxyfs.request.end lifecycle event fires. |
| packages/php-wasm/universal/src/lib/universal-php.ts | Adds a new PHP event type for proxied request completion. |
| packages/php-wasm/universal/src/lib/proxy-file-system.ts | Dispatches proxyfs.request.end to the filesystem owner when a replica request ends. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (4)
packages/playground/website/playwright/e2e/opfs.spec.ts:515
- If the repo enables
useUnknownInCatchVariables,errorisunknownanderror?.nameis a type error. Consider typing the catch variable (e.g.,catch (error: any)) or narrowing it safely (e.g.,error instanceof DOMException && error.name === 'NotFoundError') before checking.name.
} catch (error) {
if (error?.name === 'NotFoundError') {
return undefined;
}
throw error;
}
packages/php-wasm/universal/src/lib/proxy-file-system.ts:229
- This adds a
request.endlistener onreplicawithout any corresponding removal/disposal path. IfproxyFileSystem()can be called multiple times for the same replica (or replicas can be torn down/replaced), this can leak listeners and cause duplicateproxyfs.request.enddispatches. Consider registering the handler with anAbortSignal/AbortController tied to the proxyFS lifecycle, or returning adispose()function that removes the listener (by keeping a named handler reference).
// Filesystem listeners belong to the source of truth. Notify them when a
// request using that filesystem through PROXYFS finishes on the replica.
replica.addEventListener('request.end', () => {
sourceOfTruth.dispatchEvent({ type: 'proxyfs.request.end' });
});
packages/playground/website/playwright/e2e/opfs.spec.ts:821
- The 3s poll timeout is fairly tight for OPFS flush/persistence on contended CI machines and may introduce test flakiness even if the behavior is correct. Consider increasing the timeout (or reusing an existing OPFS-sync/flush signal if available) to make this regression test reliably reflect correctness rather than timing.
await expect
.poll(
() =>
readOpfsWpContentFile(website.page, site.slug, markerName),
{ timeout: 3000 }
)
.toBe(markerContents);
packages/playground/website/playwright/e2e/opfs.spec.ts:768
- This loop polls every 1ms, which can cause unnecessary CPU usage in the WASM runtime during the test (especially under CI) with little benefit. Bumping the sleep interval (e.g., 10–50ms) reduces busy-waiting while still keeping the test deterministic.
code: `<?php
file_put_contents(${JSON.stringify(firstRequestBarrierPath)}, 'ready');
while (file_get_contents(${JSON.stringify(firstRequestBarrierPath)}) !== 'release') {
usleep(1000);
}
brandonpayton
left a comment
There was a problem hiding this comment.
@ashfame I'm marking this as Approved but left a couple of notes that would be good to follow up on before merge. Once you've followed up and CI is passing, please feel free to merge this yourself. Thank you!
What
Persist filesystem changes made by pooled PHP instances to autosaved Playground sites after the request completes.
This supersedes #4240 with the architecture Brandon proposed: PROXYFS propagates replica request completion to the filesystem owner, while the OPFS journal remains solely responsible for deciding when to flush.
Root cause
Playground web uses a pool of PHP instances. Secondary instances access shared paths through PROXYFS backed by the primary PHP instance. Their writes are captured by the primary instance's OPFS journal, but
request.endis emitted only by the secondary instance. The primary journal therefore has no completion signal for the proxied request, and recent changes can remain queued in memory instead of reaching OPFS.Deterministic regression proof
The Chromium test exercises the original saved-site symptom instead of asserting any event or flush machinery introduced by this PR:
request.endthat could accidentally flush it.primaryandsecondary, respectively.readFileAsText()call proves the marker reached the shared live VFS, then an independent read of the autosaved site's real OPFS directory checks the persisted bytes.At red SHA
4a5116926, the PR contained only this test. The Chromium storage job reached the final OPFS assertion on all four attempts and consistently receivedundefinedinstead of the marker contents; all other 60 tests passed. See the expected failed CI job.The regression test at the fixed SHA is byte-for-byte identical to the red version.
Implementation
proxyFileSystem()forwards a replica'srequest.endasproxyfs.request.endon the filesystem owner.journalFSEventsToOpfs()handles that lifecycle event through its existing single-flight background flush, alongside directrequest.endandfilesystem.writetriggers.This keeps pool-specific behavior out of Playground's worker and keeps OPFS flush ownership inside the journaling abstraction. It does not change export code or introduce an export lock.
Validation
b9013b91e.