perf(renderer): coalesce initial global task refreshes
This commit is contained in:
parent
47cea728b0
commit
174ad83b47
2 changed files with 40 additions and 1 deletions
|
|
@ -247,6 +247,7 @@ const pendingFreshTeamMemberActivityMetaRefreshes = new Set<string>();
|
||||||
const pendingTeamPendingReplyRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
const pendingTeamPendingReplyRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
let latestTeamsFetchRequestId = 0;
|
let latestTeamsFetchRequestId = 0;
|
||||||
let inFlightGlobalTasksRefresh: Promise<void> | null = null;
|
let inFlightGlobalTasksRefresh: Promise<void> | null = null;
|
||||||
|
let inFlightGlobalTasksRefreshScope: ContextRequestScope | null = null;
|
||||||
let pendingFreshGlobalTasksRefresh = false;
|
let pendingFreshGlobalTasksRefresh = false;
|
||||||
interface RefreshTeamDataOptions {
|
interface RefreshTeamDataOptions {
|
||||||
withDedup?: boolean;
|
withDedup?: boolean;
|
||||||
|
|
@ -1642,7 +1643,13 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
|
|
||||||
fetchAllTasks: async () => {
|
fetchAllTasks: async () => {
|
||||||
if (inFlightGlobalTasksRefresh) {
|
if (inFlightGlobalTasksRefresh) {
|
||||||
pendingFreshGlobalTasksRefresh = true;
|
const inFlightScope = inFlightGlobalTasksRefreshScope;
|
||||||
|
if (
|
||||||
|
get().globalTasksInitialized ||
|
||||||
|
(inFlightScope && !isContextRequestScopeCurrent(get, inFlightScope))
|
||||||
|
) {
|
||||||
|
pendingFreshGlobalTasksRefresh = true;
|
||||||
|
}
|
||||||
await inFlightGlobalTasksRefresh;
|
await inFlightGlobalTasksRefresh;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1658,6 +1665,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
set({ globalTasksLoading: true, globalTasksError: null });
|
set({ globalTasksLoading: true, globalTasksError: null });
|
||||||
}
|
}
|
||||||
const requestScope = captureContextRequestScope(get);
|
const requestScope = captureContextRequestScope(get);
|
||||||
|
inFlightGlobalTasksRefreshScope = requestScope;
|
||||||
const oldTasks = get().globalTasks;
|
const oldTasks = get().globalTasks;
|
||||||
try {
|
try {
|
||||||
const tasks = await withTimeout(
|
const tasks = await withTimeout(
|
||||||
|
|
@ -1706,6 +1714,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
const request = runRefresh().finally(() => {
|
const request = runRefresh().finally(() => {
|
||||||
if (inFlightGlobalTasksRefresh === request) {
|
if (inFlightGlobalTasksRefresh === request) {
|
||||||
inFlightGlobalTasksRefresh = null;
|
inFlightGlobalTasksRefresh = null;
|
||||||
|
inFlightGlobalTasksRefreshScope = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
inFlightGlobalTasksRefresh = request;
|
inFlightGlobalTasksRefresh = request;
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,36 @@ describe('team slice context races', () => {
|
||||||
expect(store.getState().globalTasksLoading).toBe(false);
|
expect(store.getState().globalTasksLoading).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('coalesces concurrent initial global task refreshes for the same context', async () => {
|
||||||
|
const store = createSliceStore();
|
||||||
|
const initialTasks = deferred<GlobalTaskLike[]>();
|
||||||
|
apiMock.teams.getAllTasks.mockReturnValueOnce(initialTasks.promise);
|
||||||
|
|
||||||
|
const firstFetch = store.getState().fetchAllTasks();
|
||||||
|
const secondFetch = store.getState().fetchAllTasks();
|
||||||
|
|
||||||
|
initialTasks.resolve([
|
||||||
|
{
|
||||||
|
id: 'initial-task',
|
||||||
|
subject: 'Initial task',
|
||||||
|
status: 'todo',
|
||||||
|
teamName: 'initial-team',
|
||||||
|
teamDisplayName: 'Initial Team',
|
||||||
|
projectPath: '/initial/project',
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([firstFetch, secondFetch]);
|
||||||
|
|
||||||
|
expect(apiMock.teams.getAllTasks).toHaveBeenCalledTimes(1);
|
||||||
|
expect(store.getState().globalTasks).toEqual([
|
||||||
|
expect.objectContaining({ id: 'initial-task', teamName: 'initial-team' }),
|
||||||
|
]);
|
||||||
|
expect(store.getState().globalTasksInitialized).toBe(true);
|
||||||
|
expect(store.getState().globalTasksLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores global tasks loaded before a context epoch reset with the same context id', async () => {
|
it('ignores global tasks loaded before a context epoch reset with the same context id', async () => {
|
||||||
const store = createSliceStore();
|
const store = createSliceStore();
|
||||||
const localTasks = deferred<GlobalTaskLike[]>();
|
const localTasks = deferred<GlobalTaskLike[]>();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue