Skip to content

Fix race condition when setting up server files - #229

Merged
fluiddot merged 5 commits into
trunkfrom
fix/setup-server-files-race-condition
Jun 12, 2024
Merged

Fix race condition when setting up server files#229
fluiddot merged 5 commits into
trunkfrom
fix/setup-server-files-race-condition

Conversation

@fluiddot

@fluiddot fluiddot commented Jun 11, 2024

Copy link
Copy Markdown
Contributor

Related to #76 (comment).

Proposed Changes

  • Move the call to setupWPServerFiles to the app's ready event handler. This will ensure that the setup finishes before creating the main window and let the user interact with the app.
  • Add unit test to cover the case.
  • Small refactor in the index.test.ts file to address issues with async code.

Testing Instructions

Note

This issue has been spotted when running E2E tests on Windows. It's unlikely that users have encountered this as it's produced by a combination of slow file disk operations and creating a site quickly after the app is launched.

  • Remove all site folders to ensure that the app starts on the onboarding screen.
  • Remove the folder $HOME/Library/Application Support/Studio/server-files.
  • Open the app and quickly add a site.
  • Observe the site is created successfully.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
@fluiddot
fluiddot requested review from a team June 11, 2024 10:43
@fluiddot fluiddot self-assigned this Jun 11, 2024
Comment thread src/tests/index.test.ts
Comment on lines +141 to +143
await new Promise( process.nextTick );
expect( createMainWindow ).not.toHaveBeenCalled();
expect( captureExceptionMock ).toHaveBeenCalled();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems it's not needed to wrap up this block with expect. If the function whenReady throws an exception it will make the test to fail.

cc @dcalhoun as you introduced this in #130.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My best guess is that I wrapped it while authoring the test prior to implementing the error catching logic. Removing this makes sense.

Comment thread src/tests/index.test.ts
it( 'should gracefully handle app ready failures when creating a window on activate', () => {
jest.isolateModules( async () => {
it( 'should gracefully handle app ready failures when creating a window on activate', async () => {
await jest.isolateModulesAsync( async () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following Jest documentation, we should use the async version of isolateModules when there's async logic involved.

@dcalhoun dcalhoun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unable to reproduce the referenced error in the trunk branch, but the proposed code changes LGTM. 🚀

Comment thread src/index.ts
} );
} );

await setupWPServerFiles().catch( Sentry.captureException );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose there is a (likely very small) possibility that the activate event fires before this promise resolves, as that event handler is adjacent to this scope. That would mean a user could still encounter the targeted error.

Seems like a very unlikely scenario that we do not need to address, but I share it nonetheless.

studio/src/index.ts

Lines 251 to 257 in 4aa60ff

app.on( 'activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if ( BrowserWindow.getAllWindows().length === 0 ) {
app.whenReady().then( createMainWindow ).catch( Sentry.captureException );
}
} );

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that would probably be an edge case. I tested adding a delay when executing the ready event, and triggering the ready event by cliking on the dock icon of the Studio app results in an exception. This could be easily addressed by adding a condition in activate event to only create the main window when all the logic performed in ready is complete. I'll do this in a different PR.

Error:

Error occurred in handler for 'getAppGlobals': Error: No handler registered for 'getAppGlobals'
    at WebContents.<anonymous> (node:electron/js2c/browser_init:2:78277)
    at WebContents.emit (node:events:514:28)
    at WebContents.emit (node:domain:488:12)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came to realize this might be more common than we expect based on the issue Automattic/dotcom-forge#8016. I'll try to tackle it asap.

Comment thread src/tests/index.test.ts
Comment on lines +141 to +143
await new Promise( process.nextTick );
expect( createMainWindow ).not.toHaveBeenCalled();
expect( captureExceptionMock ).toHaveBeenCalled();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My best guess is that I wrapped it while authoring the test prior to implementing the error catching logic. Removing this makes sense.

@fluiddot
fluiddot merged commit a2002e3 into trunk Jun 12, 2024
@fluiddot
fluiddot deleted the fix/setup-server-files-race-condition branch June 12, 2024 08:16
@fluiddot fluiddot mentioned this pull request Jun 12, 2024
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants