Skip to content

Commit

Permalink
[lib] Add handler component for retrying holder processing
Browse files Browse the repository at this point in the history
Summary:
Address [[ https://linear.app/comm/issue/ENG-9290/add-handler-for-retrying-processing-of-failed-holders | ENG-9290 ]].
Added handler component that watches for `NOT_ESTABLISHED` and `NOT_REMOVED` holders and retries their processing.

Test Plan:
- Started a DM thread and set image avatar.
- Changed client Blob Service URL to a fake one.
- Reset the avatar back to emoji. This caused holder removal that failed.
- Used console logs in the handler to see that it retries every 5s.
- After restoring real Blob URL, it successfully removed that holder.

Reviewers: tomek, kamil, inka

Reviewed By: tomek

Subscribers: ashoat

Differential Revision: https://phab.comm.dev/D13653
  • Loading branch information
barthap committed Oct 14, 2024
1 parent e43c6b9 commit 4c7e525
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/actions/holder-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,4 @@ function useProcessBlobHolders(): ProcessHolders {
);
}

export { useClearAllHolders, useProcessBlobHolders };
export { processHoldersAction, useClearAllHolders, useProcessBlobHolders };
78 changes: 78 additions & 0 deletions lib/handlers/holders-handler.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// @flow

import invariant from 'invariant';
import _throttle from 'lodash/throttle.js';
import * as React from 'react';

import {
processHoldersAction,
processHoldersActionTypes,
type ProcessHoldersStartedPayload,
} from '../actions/holder-actions.js';
import { isLoggedIn } from '../selectors/user-selectors.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
import { useSelector } from '../utils/redux-utils.js';

const retryInterval = 30000; // ms

function HoldersHandler(): React.Node {
const dispatchActionPromise = useDispatchActionPromise();
const identityContext = React.useContext(IdentityClientContext);
const getAuthMetadata = identityContext?.getAuthMetadata;

const loggedIn = useSelector(isLoggedIn);
const storedHolders = useSelector(state => state.holderStore.storedHolders);

const itemsToProcess = React.useMemo(() => {
const holdersToAdd = [],
holdersToRemove = [];

for (const blobHash in storedHolders) {
const { status, holder } = storedHolders[blobHash];
if (status === 'NOT_ESTABLISHED') {
holdersToAdd.push({ blobHash, holder });
} else if (status === 'NOT_REMOVED') {
holdersToRemove.push({ blobHash, holder });
}
}
return { holdersToAdd, holdersToRemove };
}, [storedHolders]);

const performHoldersProcessing: (
input: ProcessHoldersStartedPayload,
) => Promise<void> = React.useMemo(
() =>
_throttle(async (input: ProcessHoldersStartedPayload) => {
invariant(getAuthMetadata, 'Identity context not set');
const authMetadata = await getAuthMetadata();

void dispatchActionPromise(
processHoldersActionTypes,
processHoldersAction(input, authMetadata),
undefined,
input,
);
}, retryInterval),
[getAuthMetadata, dispatchActionPromise],
);

const shouldStartProcessing =
itemsToProcess.holdersToAdd.length !== 0 ||
itemsToProcess.holdersToRemove.length !== 0;

React.useEffect(() => {
if (!loggedIn || !shouldStartProcessing) {
return;
}

void performHoldersProcessing(itemsToProcess);
}, [
itemsToProcess,
loggedIn,
performHoldersProcessing,
shouldStartProcessing,
]);
}

export { HoldersHandler };
2 changes: 2 additions & 0 deletions native/root.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js';
import { StaffContextProvider } from 'lib/components/staff-provider.react.js';
import { UserIdentityCacheProvider } from 'lib/components/user-identity-cache.react.js';
import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js';
import { HoldersHandler } from 'lib/handlers/holders-handler.react.js';
import { InitialStateSharingHandler } from 'lib/handlers/initial-state-sharing-handler.react.js';
import { TunnelbrokerDeviceTokenHandler } from 'lib/handlers/tunnelbroker-device-token-handler.react.js';
import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js';
Expand Down Expand Up @@ -295,6 +296,7 @@ function Root() {
<DBOpsHandler />
<UserInfosHandler />
<TunnelbrokerDeviceTokenHandler />
<HoldersHandler />
</>
);
let navigation;
Expand Down
2 changes: 2 additions & 0 deletions web/app.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import PlatformDetailsSynchronizer from 'lib/components/platform-details-synchro
import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js';
import { StaffContextProvider } from 'lib/components/staff-provider.react.js';
import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js';
import { HoldersHandler } from 'lib/handlers/holders-handler.react.js';
import { TunnelbrokerDeviceTokenHandler } from 'lib/handlers/tunnelbroker-device-token-handler.react.js';
import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js';
import { IdentitySearchProvider } from 'lib/identity-search/identity-search-context.js';
Expand Down Expand Up @@ -258,6 +259,7 @@ class App extends React.PureComponent<Props> {
<FarcasterDataHandler />
<AutoJoinCommunityHandler />
<DMActivityHandler />
<HoldersHandler />
{content}
</ChatMentionContextProvider>
</MessageSearchStateProvider>
Expand Down

0 comments on commit 4c7e525

Please sign in to comment.