[Website] Delay .zip Playground imports until OPFS sync is finished - #3847
Conversation
3de7988 to
50bc8ee
Compare
50bc8ee to
93a0e7f
Compare
| } | ||
| }; | ||
| doImport(); | ||
| }, [pendingZipFile, pendingZipTargetSlug, activeSite, playground, onClose]); |
There was a problem hiding this comment.
I wonder if there is a deeper cause we ought to be fixing here. We are already using a useEffect() hook to avoid duplicating import activity.
Why isn't it working? What is now changing to restart the import?
There was a problem hiding this comment.
I'm finding that the onClose handler keeps changing. If I remove that, from the effect dependency list, the apparent problem disappears and, from a user perspective, the import appears to succeed.
It seems like we we shouldn't base our effect on the value of a provided onClose prop, but I bet there is a reason it was added.
One thing to note:
Even though the import appears to succeed after removing onClose from the effect deps, there is the following error. I'm not sure yet whether it is related to removing onClose or another issue.
Error syncing saved Playground to OPFS NotFoundError: A requested file or directory could not be found at the time an operation was processed.
at newError (serialize-error.ts:115:5)
at Module.deserializeError (serialize-error.ts:274:8)
at Object.deserialize (api.ts:657:34)
at fromWireValue (comlink-sync.ts:958:45)
at async Object.mountOpfs (boot-playground-remote.ts:393:11)Caused by: Error: Comlink method call failed
at Object.deserialize (api.ts:668:32)
at fromWireValue (comlink-sync.ts:958:45)
at async Object.mountOpfs (boot-playground-remote.ts:393:11)Caused by: Error: Comlink method call failed
at Object.deserialize (api.ts:668:32)
at fromWireValue (comlink-sync.ts:958:45)
at async syncInitialOpfsFilesInBackground (boot-site-client.ts:423:3)
There was a problem hiding this comment.
To clarify, just removing onClose from the dependency list on trunk, without this PR, seems to stop the duplicate importing.
There was a problem hiding this comment.
Codex suggested that Autosaving new Playgrounds causes this problem, sounds somewhat plausible:
The failure is a race in the browser “Import .zip” flow:
- Import creates a new OPFS saved site.
- That new site first boots in MEMFS, then copies initial WordPress files to OPFS in the background.
- The import can start mutating /wordpress while that initial OPFS copy is still running.
- This can leave the site showing “Save failed” or persisted with stale/missing files.
There was a problem hiding this comment.
The OPFS NotFoundError doesn't happen with this PR's changes, but it happens when I just remove the onClose dependency from the import effect.
The failure is a race in the browser “Import .zip” flow:
That appears to be the case. Importing a zip calls createSiteForImport().
And createSiteForImport() is creates a new OPFS-backed site:
I believe this PR doesn't experience the OPFS NotFoundError because it delays the import while activeOpfsSyncStatus is "syncing". I think that is probably the right call. We could be even more explicit about setting an "importing" state or about a new site being completely ready for use, but I think this PR's current approach is a reasonable touch and maybe the right depth for now.
There was a problem hiding this comment.
note: I edited the above comment to make more sense.
There was a problem hiding this comment.
@brandonpayton I don't like when Codex amends commits, otherwise you could have seen the iterations that you went through yourself :) I will do a follow up PR today addressing your feedback.
brandonpayton
left a comment
There was a problem hiding this comment.
Thanks for working on this fix, @ashfame. It is testing well for me using the original failed import zip and an import zip I created locally.
I think this is the right touch for such a fix. Nice job!
I left one review suggestion that would be cool to address. Overall, I think this is good to go.
| wordPressFilesZip: pendingZipFile, | ||
| wordPressFilesZip: zipFile, | ||
| }); | ||
| await flushImportedWordPressFiles(playground); |
There was a problem hiding this comment.
It would be helpful to add an inline comment about why we are flushing, since we were not flushing before this PR.
It seems like a reasonable thing to do though.
|
@ashfame, I forgot to mention as part of the review: |
## What changed - Add an inline comment explaining why ZIP imports explicitly flush OPFS before reporting success. - Add Chromium E2E coverage for imported saved-site durability. ## E2E test coverage 1. Creates a temporary Playground with a known marker and saves it to OPFS. 2. Imports a generated ZIP containing a distinct marker into a new saved Playground. 3. Waits for exactly one success alert and resolves the imported site slug. 4. Immediately fresh-boots the imported slug from `?site-slug=<slug>`, discarding the runtime that performed the import. 5. Requests the imported marker from the fresh runtime and confirms that it was restored from saved storage. 6. Verifies that no “Save failed” or “Site failed” UI appears. 7. Switches to the original saved site, then back to the imported site, and verifies that the marker still loads. 8. Direct-loads the imported slug once more and verifies that the marker remains available. ## Why The previous assertion order requested the imported marker from the active import runtime before switching sites. Completing that request emits `request.end`, which can trigger background OPFS flushing and mask files that were not durable when import success was reported. Fresh-booting first checks the user-visible contract: once import reports success, the imported files can be restored from saved storage. This intentionally remains a black-box durability check; it does not couple the test to Comlink or assert which OPFS flush path completed. Follow-up to review feedback on #3847. --------- Co-authored-by: Brandon Payton <brandon@happycode.net>
What it does
Before this PR, importing a Playground
.zipcould start while the newly-created saved Playground was still autosaving to OPFS.After this PR, the import waits until OPFS sync finishes. This avoids modifying MEMFS file handles that are already targeted for OPFS synchronization.
Rationale
Importing a .zip file to restore a previous Playground was failing when started while the initial autosave was still running.
Testing instructions