Skip to content

Commit

Permalink
[lib] Separate out bindKeyserverCall cache for each useKeyserverCall …
Browse files Browse the repository at this point in the history
…callsite

Summary:
The previous diff has one shortcoming, which is mentioned there:

> However, we still have an issue owing to the fact that the `useDerivedObject` will cache on a per-hook-invocation level, whereas `bindCallKeyserverEndpointSelector` is caching globally.

This diff resolves that by lifting the `bindCallKeyserverEndpointSelector` cache to also be on a per-hook level.

We can't make the cache global because the individual hook calls can have their params overriden.

Depends on D10465

Test Plan:
Before this stack, I was able to reproduce [ENG-3612](https://linear.app/comm/issue/ENG-3612/[native]-getting-huge-number-of-unhandled-promise-rejection-when) by going to the `ThreadSettings` screen in `native` while my local `keyserver` was down. After this stack, the issue no longer repros.

I also compiled a release build of the iOS app to my phone to confirm that there were no regressions in TTI, or the time it takes to open a `MessageList` and go back to the `ChatThreadList`.

Reviewers: inka, rohan

Reviewed By: inka

Subscribers: tomek

Differential Revision: https://phab.comm.dev/D10466
  • Loading branch information
Ashoat committed Dec 29, 2023
1 parent 61e99ee commit 141126a
Showing 1 changed file with 75 additions and 79 deletions.
154 changes: 75 additions & 79 deletions lib/utils/keyserver-call.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export type ActionFunc<Args: mixed, Return> = (
// _memoize memoizes the function by caching the result.
// The first argument of the memoized function is used as the map cache key.
const baseCreateBoundServerCallsSelector = (
// eslint-disable-next-line no-unused-vars
keyserverID: string,
): (BindServerCallsParams => CallServerEndpoint) =>
createSelector(
Expand Down Expand Up @@ -121,76 +120,67 @@ function useKeyserverCallInfos(keyserverInfos: {
);
}

type BindKeyserverCallParams = {
+dispatch: Dispatch,
+currentUserInfo: ?CurrentUserInfo,
+keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo },
};

const bindCallKeyserverEndpointSelector: BindKeyserverCallParams => <
Args: mixed,
Return,
>(
type BindCallKeyserverSelector = <Args: mixed, Return>(
keyserverCall: ActionFunc<Args, Return>,
) => Args => Promise<Return> = createSelector(
(state: BindKeyserverCallParams) => state.dispatch,
(state: BindKeyserverCallParams) => state.currentUserInfo,
(state: BindKeyserverCallParams) => state.keyserverCallInfos,
(
dispatch: Dispatch,
currentUserInfo: ?CurrentUserInfo,
keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo },
) => {
return _memoize(
<Args: mixed, Return>(
keyserverCall: ActionFunc<Args, Return>,
): (Args => Promise<Return>) => {
const callKeyserverEndpoint = (
endpoint: Endpoint,
requests: { +[keyserverID: string]: ?{ +[string]: mixed } },
options?: ?CallServerEndpointOptions,
) => {
const bindCallKeyserverEndpoint = (keyserverID: string) => {
const {
cookie,
urlPrefix,
sessionID,
isSocketConnected,
lastCommunicatedPlatformDetails,
} = keyserverCallInfos[keyserverID];

const boundCallServerEndpoint = createBoundServerCallsSelector(
keyserverID,
)({
dispatch,
currentUserInfo,
cookie,
urlPrefix,
sessionID,
isSocketConnected,
lastCommunicatedPlatformDetails,
keyserverID,
});

return boundCallServerEndpoint(
endpoint,
requests[keyserverID],
options,
);
) => Args => Promise<Return>;
function useBindCallKeyserverEndpointSelector(
dispatch: Dispatch,
currentUserInfo: ?CurrentUserInfo,
keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo },
): BindCallKeyserverSelector {
return React.useMemo(
() =>
_memoize(
<Args: mixed, Return>(
keyserverCall: ActionFunc<Args, Return>,
): (Args => Promise<Return>) => {
const callKeyserverEndpoint = (
endpoint: Endpoint,
requests: { +[keyserverID: string]: ?{ +[string]: mixed } },
options?: ?CallServerEndpointOptions,
) => {
const bindCallKeyserverEndpoint = (keyserverID: string) => {
const {
cookie,
urlPrefix,
sessionID,
isSocketConnected,
lastCommunicatedPlatformDetails,
} = keyserverCallInfos[keyserverID];

const boundCallServerEndpoint = createBoundServerCallsSelector(
keyserverID,
)({
dispatch,
currentUserInfo,
cookie,
urlPrefix,
sessionID,
isSocketConnected,
lastCommunicatedPlatformDetails,
keyserverID,
});

return boundCallServerEndpoint(
endpoint,
requests[keyserverID],
options,
);
};

const promises: { [string]: Promise<CallServerEndpoint> } = {};
for (const keyserverID in requests) {
promises[keyserverID] = bindCallKeyserverEndpoint(keyserverID);
}
return promiseAll(promises);
};

const promises: { [string]: Promise<CallServerEndpoint> } = {};
for (const keyserverID in requests) {
promises[keyserverID] = bindCallKeyserverEndpoint(keyserverID);
}
return promiseAll(promises);
};
const keyserverIDs = Object.keys(keyserverCallInfos);
return keyserverCall(callKeyserverEndpoint, keyserverIDs);
},
);
},
);
const keyserverIDs = Object.keys(keyserverCallInfos);
return keyserverCall(callKeyserverEndpoint, keyserverIDs);
},
),
[dispatch, currentUserInfo, keyserverCallInfos],
);
}

export type KeyserverCallParamOverride = Partial<{
+dispatch: Dispatch,
Expand All @@ -202,27 +192,33 @@ function useKeyserverCall<Args: mixed, Return>(
keyserverCall: ActionFunc<Args, Return>,
paramOverride?: ?KeyserverCallParamOverride,
): Args => Promise<Return> {
const dispatch = useDispatch();
const currentUserInfo = useSelector(state => state.currentUserInfo);
const baseDispatch = useDispatch();
const baseCurrentUserInfo = useSelector(state => state.currentUserInfo);

const keyserverInfos = useSelector(
state => state.keyserverStore.keyserverInfos,
);
const baseCombinedInfo = {
dispatch,
dispatch: baseDispatch,
currentUserInfo: baseCurrentUserInfo,
keyserverInfos,
currentUserInfo,
...paramOverride,
};

const { keyserverInfos: keyserverInfoPartials, ...restCombinedInfo } =
baseCombinedInfo;
const {
dispatch,
currentUserInfo,
keyserverInfos: keyserverInfoPartials,
} = baseCombinedInfo;
const keyserverCallInfos = useKeyserverCallInfos(keyserverInfoPartials);

const bindCallKeyserverEndpointToAction = bindCallKeyserverEndpointSelector({
...restCombinedInfo,
keyserverCallInfos,
});
const bindCallKeyserverEndpointToAction =
useBindCallKeyserverEndpointSelector(
dispatch,
currentUserInfo,
keyserverCallInfos,
);

return React.useMemo(
() => bindCallKeyserverEndpointToAction(keyserverCall),
[bindCallKeyserverEndpointToAction, keyserverCall],
Expand Down

0 comments on commit 141126a

Please sign in to comment.