From 1c17ff5dd938a72fa0c1f32cd8b595d996d5b8da Mon Sep 17 00:00:00 2001 From: jessopb <36554050+jessopb@users.noreply.github.com> Date: Fri, 12 Aug 2022 17:23:04 -0400 Subject: [PATCH] doFetchModBlockedList: don't block ui thread (#7674) * doFetchModBlockedList: don't block ui thread doFetchModBlockedList is blocking the ui thread. Duplicate data in `doFetchModBlockedList::blockListsPerChannel` to about 1000. The tab is dead when function hits, about 4s after reload. - Yield occasionally using the `setTimeout` method. - Doing a chunk size of 1 for now so we don't have to yield the inner loop as well (seems good enough). This is just based on a relatively large blocklist size. - Can't do `await` in a callback, so must change the `forEach` to a `for`. * yield thread in storeList Co-authored-by: infinite-persistence --- ui/redux/actions/comments.js | 119 +++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/ui/redux/actions/comments.js b/ui/redux/actions/comments.js index 375a96757..5bd0d483d 100644 --- a/ui/redux/actions/comments.js +++ b/ui/redux/actions/comments.js @@ -1143,6 +1143,9 @@ export function doCommentModUnBlockAsModerator(commenterUri: string, creatorUri: export function doFetchModBlockedList() { return async (dispatch: Dispatch, getState: GetState) => { + const LOOP_CHUNK_SIZE = 1; + const yieldThread = () => new Promise((resolve) => setTimeout(resolve)); + const state = getState(); const myChannels = selectMyChannelClaims(state); if (!myChannels) { @@ -1172,7 +1175,7 @@ export function doFetchModBlockedList() { }) ) ) - .then((res) => { + .then(async (res) => { let personalBlockList = []; let adminBlockList = []; let moderatorBlockList = []; @@ -1185,66 +1188,76 @@ export function doFetchModBlockedList() { const adminTimeoutMap = {}; const moderatorTimeoutMap = {}; - const blockListsPerChannel = res.map((r) => r.value); - blockListsPerChannel - .sort((a, b) => { - return 1; - }) - .forEach((channelBlockLists) => { - const storeList = (fetchedList, blockedList, timeoutMap, blockedByMap) => { - if (fetchedList) { - fetchedList.forEach((blockedChannel) => { - if (blockedChannel.blocked_channel_name) { - const channelUri = buildURI({ - channelName: blockedChannel.blocked_channel_name, - channelClaimId: blockedChannel.blocked_channel_id, - }); + const blockListsPerChannel = []; + for (let i = 0; i < res.length; ++i) { + blockListsPerChannel.push(res[i].value); + if (i % 2 === 0) { + await yieldThread(); + } + } + for (let i = 0; i < blockListsPerChannel.length; ++i) { + const storeList = async (fetchedList, blockedList, timeoutMap, blockedByMap) => { + if (fetchedList) { + for (let j = 0; j < fetchedList.length; ++j) { + const blockedChannel = fetchedList[j]; + if (j > 0 && i % LOOP_CHUNK_SIZE === 0) { + await yieldThread(); + } + if (blockedChannel.blocked_channel_name) { + const channelUri = buildURI({ + channelName: blockedChannel.blocked_channel_name, + channelClaimId: blockedChannel.blocked_channel_id, + }); - if (!blockedList.find((blockedChannel) => isURIEqual(blockedChannel.channelUri, channelUri))) { - blockedList.push({ channelUri, blockedAt: blockedChannel.blocked_at }); + if (!blockedList.find((blockedChannel) => isURIEqual(blockedChannel.channelUri, channelUri))) { + blockedList.push({ channelUri, blockedAt: blockedChannel.blocked_at }); - if (blockedChannel.banned_for) { - timeoutMap[channelUri] = { - blockedAt: blockedChannel.blocked_at, - bannedFor: blockedChannel.banned_for, - banRemaining: blockedChannel.ban_remaining, - }; - } - } - - if (blockedByMap !== undefined) { - const blockedByChannelUri = buildURI({ - channelName: blockedChannel.blocked_by_channel_name, - channelClaimId: blockedChannel.blocked_by_channel_id, - }); - - if (blockedByMap[channelUri]) { - if (!blockedByMap[channelUri].includes(blockedByChannelUri)) { - blockedByMap[channelUri].push(blockedByChannelUri); - } - } else { - blockedByMap[channelUri] = [blockedByChannelUri]; - } + if (blockedChannel.banned_for) { + timeoutMap[channelUri] = { + blockedAt: blockedChannel.blocked_at, + bannedFor: blockedChannel.banned_for, + banRemaining: blockedChannel.ban_remaining, + }; } } - }); + + if (blockedByMap !== undefined) { + const blockedByChannelUri = buildURI({ + channelName: blockedChannel.blocked_by_channel_name, + channelClaimId: blockedChannel.blocked_by_channel_id, + }); + + if (blockedByMap[channelUri]) { + if (!blockedByMap[channelUri].includes(blockedByChannelUri)) { + blockedByMap[channelUri].push(blockedByChannelUri); + } + } else { + blockedByMap[channelUri] = [blockedByChannelUri]; + } + } + } } - }; + } + }; - const blocked_channels = channelBlockLists && channelBlockLists.blocked_channels; - const globally_blocked_channels = channelBlockLists && channelBlockLists.globally_blocked_channels; - const delegated_blocked_channels = channelBlockLists && channelBlockLists.delegated_blocked_channels; + const channelBlockLists = blockListsPerChannel[i]; + const blocked_channels = channelBlockLists && channelBlockLists.blocked_channels; + const globally_blocked_channels = channelBlockLists && channelBlockLists.globally_blocked_channels; + const delegated_blocked_channels = channelBlockLists && channelBlockLists.delegated_blocked_channels; - storeList(blocked_channels, personalBlockList, personalTimeoutMap); - storeList(globally_blocked_channels, adminBlockList, adminTimeoutMap); - storeList( - delegated_blocked_channels, - moderatorBlockList, - moderatorTimeoutMap, - moderatorBlockListDelegatorsMap - ); - }); + if (i > 0 && i % LOOP_CHUNK_SIZE === 0) { + await yieldThread(); + } + await storeList(blocked_channels, personalBlockList, personalTimeoutMap); + await storeList(globally_blocked_channels, adminBlockList, adminTimeoutMap); + await storeList( + delegated_blocked_channels, + moderatorBlockList, + moderatorTimeoutMap, + moderatorBlockListDelegatorsMap + ); + } dispatch({ type: ACTIONS.COMMENT_MODERATION_BLOCK_LIST_COMPLETED, data: {