Skip to content

Commit

Permalink
RE2022-338 fix simultanious polling issues, matchId state errors, oth…
Browse files Browse the repository at this point in the history
…er backoff polling issues
  • Loading branch information
dauglyon committed May 16, 2024
1 parent 876116c commit 3874be5
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 12 deletions.
36 changes: 33 additions & 3 deletions src/common/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ declare global {
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

const pollLock: Set<string> = new Set();
export const useBackoffPolling = <
// eslint-disable-next-line @typescript-eslint/no-explicit-any
R extends UseQueryHookResult<QueryDefinition<unknown, any, any, unknown>>
Expand All @@ -30,18 +31,39 @@ export const useBackoffPolling = <
) => {
const opts = { baseInterval: 200, rate: 2, skipPoll: false, ...options };
const [count, setCount] = useState(0);
const pollLockStatus = useRef<boolean>(false);
const shouldPollCallback = useRef<typeof pollCondition>(pollCondition);
shouldPollCallback.current = pollCondition;

// Prevents us from polling the identical request from multiple components
useEffect(() => {
const lockId = result.requestId;
if (lockId && !pollLock.has(lockId)) {
pollLock.add(lockId);
pollLockStatus.current = true;
} else {
pollLockStatus.current = false;
}
return () => {
if (lockId && pollLock.has(lockId)) {
pollLock.delete(lockId);
pollLockStatus.current = false;
}
};
}, [result.requestId]);

useEffect(() => setCount((c) => c + 1), [result.fulfilledTimeStamp]);
const shouldPoll = useCallback(() => {
const should = !opts.skipPoll && shouldPollCallback.current(result, count);
const should =
!opts.skipPoll &&
pollLockStatus.current &&
shouldPollCallback.current(result, count);
if (!should) setCount(0);
return should;
}, [count, opts.skipPoll, result]);

useEffect(() => {
if (shouldPoll()) {
if (!result.isUninitialized && shouldPoll()) {
const pollTime = opts.baseInterval * Math.pow(opts.rate, count);
const now = Date.now();
const duration = Math.max(
Expand All @@ -56,7 +78,15 @@ export const useBackoffPolling = <
clearTimeout(timeout);
};
}
}, [opts.baseInterval, count, pollCondition, opts.rate, result, shouldPoll]);
}, [
opts.baseInterval,
count,
pollCondition,
opts.rate,
result,
shouldPoll,
result.isUninitialized,
]);

return result;
};
Expand Down
4 changes: 2 additions & 2 deletions src/features/collections/ExportModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import { useAppDispatch, useAppSelector } from '../../common/hooks';
import { uriEncodeTemplateTag as encode } from '../../common/utils/stringUtils';
import { NarrativeDoc } from '../../common/types/NarrativeDoc';
import { Modal } from '../layout/Modal';
import { useAppParam } from '../params/hooks';
import {
setLocalSelection,
useCurrentSelection,
useMatchId,
useSelectionId,
} from './collectionsSlice';
import classes from './Collections.module.scss';
Expand All @@ -30,7 +30,7 @@ import { useParamsForNarrativeDropdown } from './hooks';
export const ExportModal = ({ collectionId }: { collectionId: string }) => {
const dispatch = useAppDispatch();
const selectionId = useSelectionId(collectionId);
const matchId = useAppParam('match');
const matchId = useMatchId(collectionId);
const username = useAppSelector((state) => state.auth.username);
const currentSelection = useCurrentSelection(collectionId);

Expand Down
4 changes: 2 additions & 2 deletions src/features/collections/data_products/Biolog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { DataViewLink } from '../../../common/components';
import { Pagination, usePageBounds } from '../../../common/components/Table';
import { useAppDispatch } from '../../../common/hooks';
import { formatNumber } from '../../../common/utils/stringUtils';
import { useAppParam } from '../../params/hooks';
import classes from '../Collections.module.scss';
import {
useFilterContextState,
useGenerateSelectionId,
useMatchId,
} from '../collectionsSlice';
import { useFilterContexts } from '../Filters';
import { useProcessStatePolling } from '../hooks';
Expand Down Expand Up @@ -119,7 +119,7 @@ export const Biolog: FC<{
};

const useBiolog = (collection_id: string | undefined) => {
const matchId = useAppParam('match');
const matchId = useMatchId(collection_id);
const selId = useGenerateSelectionId(collection_id || '', {
skip: !collection_id,
});
Expand Down
38 changes: 36 additions & 2 deletions src/features/collections/data_products/GenomeAttribs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
RowSelectionState,
} from '@tanstack/react-table';
import { FC, useEffect, useMemo, useState } from 'react';
import { getGenomeAttribs } from '../../../common/api/collectionsApi';
import {
getGenomeAttribs,
getMatch,
getSelection,
} from '../../../common/api/collectionsApi';
import {
Pagination,
Table,
Expand All @@ -30,7 +34,8 @@ import { Grid, Paper, Stack, Tooltip, Typography } from '@mui/material';
import { formatNumber } from '../../../common/utils/stringUtils';
import { Link } from 'react-router-dom';
import { filterContextMode, useFilterContexts } from '../Filters';
import { useTableViewParams } from '../hooks';
import { useProcessStatePolling, useTableViewParams } from '../hooks';
import { skipToken } from '@reduxjs/toolkit/dist/query';

export const GenomeAttribs: FC<{
collection_id: string;
Expand Down Expand Up @@ -373,5 +378,34 @@ export const useGenomeAttribsCount = (
skip: !collection_id,
});

// Refetch count when the match changes and is done processing
const match = getMatch.useQuery(params.match_id ?? skipToken);
useProcessStatePolling(match, ['state']);

useEffect(() => {
if (match.data?.state === 'complete') {
result.refetch();
}
}, [match.data?.state, result]);

// Refetch count when the selection changes and is done processing
const selParams = useMemo(
() =>
params.selection_id !== undefined
? {
selection_id: params.selection_id,
}
: skipToken,
[params.selection_id]
);
const selection = getSelection.useQuery(selParams);
useProcessStatePolling(selection, ['state']);

useEffect(() => {
if (selection.data?.state === 'complete') {
result.refetch();
}
}, [selection.data?.state, result]);

return { count: result?.currentData?.count, result };
};
5 changes: 4 additions & 1 deletion src/features/collections/data_products/SampleAttribs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ export const SampleAttribs: FC<{
}),
[allCountParams]
);
const { data: matchCount } = getSampleAttribs.useQuery(matchCountParams);
const matchResult = getSampleAttribs.useQuery(matchCountParams);
useProcessStatePolling(matchResult, ['match_state']);
const matchCount = matchResult.data;

const selectCountParams = useMemo(
() => ({
...allCountParams,
Expand Down
9 changes: 7 additions & 2 deletions src/features/collections/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,14 @@ export const useProcessStatePolling = <
} else if (result.data[processStateKey] === 'complete') {
return false;
} else if (result.data[processStateKey] === 'failed') {
toast('ProcessState Polling Failed, see console for more info');
toast(
`"${result.endpointName}" ProcessState Polling Failed, see console for more info`
);
// eslint-disable-next-line no-console
console.error('ProcessState Polling Failed', { result });
console.error(
`"${result.endpointName}" ProcessState Polling Failed`,
{ result }
);
return false;
} else {
return false;
Expand Down

0 comments on commit 3874be5

Please sign in to comment.