perf: App start improvement [INS-3957]#7492
Conversation
|
have some |
| } | ||
| }; | ||
|
|
||
| async function migrateProjectsUnderOrganization(personalOrganizationId: string, sessionId: string) { |
There was a problem hiding this comment.
its really dangerous to mess with migrations, I'd scope this out if you can without introducing more risk
There was a problem hiding this comment.
The origin migration logic is in /organizationId index route loader, after the initial entry change, the user will not match this index route, so we need to put the migration in action.If we could add some test to reduce the risk?
|
can you provide more evidence that this approach is addressing the lionshare of performance impact? from what i can see this PR is doing at least two things. in a perf PR please only focus on a single measurable improvement and give a rationale about why this perf change was chosen in context of other perf impacts which were also investigated. eg. |
Do you mean the two things is modity initial entry and add async task hooks? This is because if we only modify the initial entry, the logic in the |
| for (const task of asyncTaskList) { | ||
| if (task === AsyncTask.SyncOrganization) { | ||
| invariant(sessionId, 'sessionId is required'); | ||
| invariant(accountId, 'accountId is required'); | ||
| taskPromiseList.push(syncOrganization(sessionId, accountId)); | ||
| } | ||
|
|
||
| if (task === AsyncTask.MigrateProjects) { | ||
| invariant(personalOrganizationId, 'personalOrganizationId is required'); | ||
| invariant(sessionId, 'sessionId is required'); | ||
| taskPromiseList.push(migrateProjectsUnderOrganization(personalOrganizationId, sessionId)); | ||
| } | ||
|
|
||
| if (task === AsyncTask.SyncProjects) { | ||
| invariant(organizationId, 'organizationId is required'); | ||
| taskPromiseList.push(syncProjects(organizationId)); | ||
| } | ||
| } | ||
|
|
||
| await Promise.all(taskPromiseList); |
There was a problem hiding this comment.
Using signals with actions is a new concept. I still don't understand the benefit of this over creating 3 separate actions. This kind of out of the box thinking is cool but really hard to reason about. I don't want it in our happy path if I can help it.
There was a problem hiding this comment.
One reason is 3 seperate action will case loader rerun 3 times.So I merge into only action
| useEffect(() => { | ||
| // sync organizations | ||
| if (asyncTaskList.includes(AsyncTask.SyncOrganization)) { | ||
| console.log('running sync organization task'); | ||
| const submit = syncOrgsFetcher.submit; | ||
| submit({}, { | ||
| action: '/organization/sync', | ||
| method: 'POST', | ||
| }); | ||
| } | ||
| }, [asyncTaskList, syncOrgsFetcher.submit]); | ||
|
|
||
| useEffect(() => { | ||
| // sync projects under organization | ||
| if (asyncTaskList.includes(AsyncTask.SyncProjects)) { | ||
| console.log('running sync projects task'); | ||
| const submit = syncProjectsFetcher.submit; | ||
| submit({}, { | ||
| action: `/organization/${organizationId}/sync-projects`, | ||
| method: 'POST', | ||
| }); | ||
| } | ||
| }, [asyncTaskList, organizationId, syncProjectsFetcher.submit]); | ||
|
|
||
| useEffect(() => { | ||
| // migrate projects under personal organization | ||
| if (asyncTaskList.includes(AsyncTask.MigrateProjects)) { | ||
| console.log('running migrate projects task'); | ||
| const submit = migrateProjectsFetcher.submit; | ||
| submit({}, { | ||
| action: '/organization/migrate-projects', | ||
| method: 'POST', | ||
| }); | ||
| } | ||
| }, [asyncTaskList, migrateProjectsFetcher.submit]); | ||
|
|
There was a problem hiding this comment.
@gatzjames @jackkav if we fetch the existing action instead of combine to a new action, there are 3 action submit, and the loader will re-run 3 times.This will result in repeated requests in the project loader several times.I'd suggest merging into a new action to reduce the number of requests.

There was a problem hiding this comment.
using defer also can not cancel the fetch request in loader
39cc512 to
f364d71
Compare
filfreire
left a comment
There was a problem hiding this comment.
Some tests on my machine:
Latest beta packaged version:
- 956 ms loading screen until we see dashboard
- 1633 ms activity until fully loaded (affected by network loading of collections and status)
This PR (using packaged app, from npm run app-package):
- 428 ms loading screen until we see dashboard
- 1500 ms activity until fully loaded (affected by network loading of collections and status)
Loading time difference is noticeable.
Switching between Organizations also is fast, but there's a split second when it still is not showing all the unsynced collections right after going to another project/organization.
@filfreire Thank you very much for your test.Not showing all the unsynced files right away is as expected, because we return a deferred data in react-router loader and wait the promise in component render phase. |
| // ideas: | ||
| // 1. useEffect with controlled execution | ||
| // 2. use fetcher history state to avoid running the same task multiple times | ||
| // 3. create an event after logged in user is detected in initial entry and listen.once in this component |
There was a problem hiding this comment.
| // ideas: | |
| // 1. useEffect with controlled execution | |
| // 2. use fetcher history state to avoid running the same task multiple times | |
| // 3. create an event after logged in user is detected in initial entry and listen.once in this component | |
| // ideas: | |
| // 1. useEffect with controlled execution | |
| // 2. use fetcher history state to avoid running the same task multiple times | |
| // 3. create an event after logged in user is detected in initial entry and listen.once in this component | |
| // 4. rather than use an action hack to avoid fetching in the loader, we can now use a deferred promise in the loader since they are now non-blocking. |
There was a problem hiding this comment.
Try this:
// 4. rather than use an action hack to avoid fetching in the loader, we can now use a deferred promise in the loader since they are now non-blocking.
There was a problem hiding this comment.
I think this idea is not feasible for two reasons
- Because in current solution, we find best route in InitialEntry, and will skip the index route loader, so we are not able to keep fetching in the index route loader.
- If we don't find best route and keep using '/organization' as the initial entry, the logic is executed to index route loader. But the index route loader just use for refresh cache data and return a redirect, we can't return a deferred promise because no component connected to the loader .
There was a problem hiding this comment.
The async loading strategy of workspaces seems straightforward with low maintaining effort and which can work with organizations, projects loading in parallel. So I propose to move forward with this part at first.
| export const findPersonalOrganization = (organizations: Organization[], accountId: string) => { | ||
| return organizations.filter(isPersonalOrganization) | ||
| .find(organization => | ||
| isOwnerOfOrganization({ |
There was a problem hiding this comment.
nit: Probably need double check if It is good enough to say that it is a personal org.
| // ideas: | ||
| // 1. useEffect with controlled execution | ||
| // 2. use fetcher history state to avoid running the same task multiple times | ||
| // 3. create an event after logged in user is detected in initial entry and listen.once in this component |
There was a problem hiding this comment.
The async loading strategy of workspaces seems straightforward with low maintaining effort and which can work with organizations, projects loading in parallel. So I propose to move forward with this part at first.
12aa93d to
55b5f6c
Compare
55b5f6c to
4cc27bc
Compare
gatzjames
left a comment
There was a problem hiding this comment.
Great investigation on the pros/cons of different approaches to improve data fetching.
Let's clean up the code and get this merged!
| SyncOrganization, | ||
| MigrateProjects, | ||
| SyncProjects, | ||
| } |
There was a problem hiding this comment.
consider writing a unit test together for this? to help search for simplifications
There was a problem hiding this comment.
Good advice. Added to my todo list
close INS-4026 INS-3957
objective: Reduce the App start duration
Changes
Loading strategy
findBestRoutefunction to try to return the path with orgId and projectId in:return deferred value in project loader
Test
Other approaches we tried:
useEffectlogicrouter.subscribeexecutes many times in one navigation, we need use a flag to control execution times