diff --git a/keyserver/src/creators/message-creator.js b/keyserver/src/creators/message-creator.js
--- a/keyserver/src/creators/message-creator.js
+++ b/keyserver/src/creators/message-creator.js
@@ -5,7 +5,10 @@
 
 import { permissionLookup } from 'lib/permissions/thread-permissions.js';
 import { type Device, type PushUserInfo } from 'lib/push/send-utils.js';
-import { generateNotifUserInfoPromise } from 'lib/push/utils.js';
+import {
+  fetchMessageNotifyType,
+  generateNotifUserInfo,
+} from 'lib/push/utils.js';
 import {
   rawMessageInfoFromMessageData,
   shimUnsupportedRawMessageInfos,
@@ -425,6 +428,17 @@
       messageInfosPerUser[userID] = userMessageInfos;
     }
 
+    const { userNotMemberOfSubthreads } = preUserPushInfo;
+    const messagesPromise = fetchMessageNotifyType({
+      newMessageInfos: messageInfos,
+      messageDatas,
+      threadsToMessageIndices,
+      userNotMemberOfSubthreads,
+      fetchMessageInfoByID: (messageID: string) =>
+        fetchMessageInfoByID(viewer, messageID),
+      userID,
+    });
+
     latestMessagesPerUser.set(
       userID,
       determineLatestMessagesPerThread(
@@ -435,36 +449,29 @@
       ),
     );
 
-    const { userNotMemberOfSubthreads } = preUserPushInfo;
     const userDevices = [...preUserPushInfo.devices.values()];
     if (userDevices.length === 0) {
       continue;
     }
 
-    const userPushInfoPromise = generateNotifUserInfoPromise({
-      messageNotifyType: messageNotifyTypes.NOTIF_AND_SET_UNREAD,
-      devices: userDevices,
-      newMessageInfos: messageInfos,
-      messageDatas,
-      threadsToMessageIndices,
-      threadIDs: [...preUserPushInfo.notFocusedThreadIDs],
-      userNotMemberOfSubthreads,
-      fetchMessageInfoByID: (messageID: string) =>
-        fetchMessageInfoByID(viewer, messageID),
-      userID,
-    });
-    const userRescindInfoPromise = generateNotifUserInfoPromise({
-      messageNotifyType: messageNotifyTypes.RESCIND,
-      devices: userDevices,
-      newMessageInfos: messageInfos,
-      messageDatas,
-      threadsToMessageIndices,
-      threadIDs: [...preUserPushInfo.notFocusedThreadIDs],
-      userNotMemberOfSubthreads,
-      fetchMessageInfoByID: (messageID: string) =>
-        fetchMessageInfoByID(viewer, messageID),
-      userID,
-    });
+    const userPushInfoPromise = (async () => {
+      const messages = await messagesPromise;
+      return generateNotifUserInfo({
+        messageNotifyType: messageNotifyTypes.NOTIF_AND_SET_UNREAD,
+        messages,
+        devices: userDevices,
+        threadIDs: [...preUserPushInfo.notFocusedThreadIDs],
+      });
+    })();
+    const userRescindInfoPromise = (async () => {
+      const messages = await messagesPromise;
+      return generateNotifUserInfo({
+        messageNotifyType: messageNotifyTypes.RESCIND,
+        messages,
+        devices: userDevices,
+        threadIDs: [...preUserPushInfo.notFocusedThreadIDs],
+      });
+    })();
 
     userPushInfoPromises[userID] = userPushInfoPromise;
     userRescindInfoPromises[userID] = userRescindInfoPromise;
diff --git a/lib/push/send-utils.js b/lib/push/send-utils.js
--- a/lib/push/send-utils.js
+++ b/lib/push/send-utils.js
@@ -17,7 +17,8 @@
 import {
   stringToVersionKey,
   getDevicesByPlatform,
-  generateNotifUserInfoPromise,
+  fetchMessageNotifyType,
+  generateNotifUserInfo,
   userAllowsNotif,
 } from './utils.js';
 import { createWebNotification } from './web-notif-creators.js';
@@ -216,18 +217,23 @@
   for (const userID in pushUserThreadInfos) {
     const pushUserThreadInfo = pushUserThreadInfos[userID];
 
+    const messagesPromise = fetchMessageNotifyType({
+      newMessageInfos,
+      messageDatas,
+      threadsToMessageIndices,
+      userNotMemberOfSubthreads: new Set(),
+      fetchMessageInfoByID: (messageID: string) =>
+        (async () => messageInfos[messageID])(),
+      userID,
+    });
+
     userPushInfoPromises[userID] = (async () => {
-      const pushInfosWithoutSubscriptions = await generateNotifUserInfoPromise({
+      const messages = await messagesPromise;
+      const pushInfosWithoutSubscriptions = generateNotifUserInfo({
         messageNotifyType: messageNotifyTypes.NOTIF_AND_SET_UNREAD,
+        messages,
         devices: pushUserThreadInfo.devices,
-        newMessageInfos,
-        messageDatas,
-        threadsToMessageIndices,
         threadIDs: Object.keys(pushUserThreadInfo.threadsWithSubscriptions),
-        userNotMemberOfSubthreads: new Set(),
-        fetchMessageInfoByID: (messageID: string) =>
-          (async () => messageInfos[messageID])(),
-        userID,
       });
       if (!pushInfosWithoutSubscriptions) {
         return null;
@@ -239,17 +245,12 @@
     })();
 
     userRescindInfoPromises[userID] = (async () => {
-      const pushInfosWithoutSubscriptions = await generateNotifUserInfoPromise({
+      const messages = await messagesPromise;
+      const pushInfosWithoutSubscriptions = generateNotifUserInfo({
         messageNotifyType: messageNotifyTypes.RESCIND,
+        messages,
         devices: pushUserThreadInfo.devices,
-        newMessageInfos,
-        messageDatas,
-        threadsToMessageIndices,
         threadIDs: Object.keys(pushUserThreadInfo.threadsWithSubscriptions),
-        userNotMemberOfSubthreads: new Set(),
-        fetchMessageInfoByID: (messageID: string) =>
-          (async () => messageInfos[messageID])(),
-        userID,
       });
       if (!pushInfosWithoutSubscriptions) {
         return null;
@@ -1325,7 +1326,6 @@
 export {
   preparePushNotifs,
   prepareOwnDevicesPushNotifs,
-  generateNotifUserInfoPromise,
   pushInfoToCollapsableNotifInfo,
   mergeUserToCollapsableInfo,
 };
diff --git a/lib/push/utils.js b/lib/push/utils.js
--- a/lib/push/utils.js
+++ b/lib/push/utils.js
@@ -5,7 +5,10 @@
 import type { Device, ThreadSubscriptionWithRole } from './send-utils.js';
 import { oldValidUsernameRegex } from '../shared/account-utils.js';
 import { isUserMentioned } from '../shared/mention-utils.js';
-import { type MessageNotifyType } from '../shared/messages/message-spec.js';
+import {
+  type MessageNotifyType,
+  messageNotifyTypes,
+} from '../shared/messages/message-spec.js';
 import { messageSpecs } from '../shared/messages/message-specs.js';
 import type { Platform } from '../types/device-types.js';
 import { messageTypes } from '../types/message-types-enum.js';
@@ -89,96 +92,125 @@
   return byPlatform;
 }
 
-type GenerateNotifUserInfoPromiseInputData = {
-  +messageNotifyType: MessageNotifyType,
-  +devices: $ReadOnlyArray<Device>,
+type FetchMessageNotifyTypeInputData = {
   +newMessageInfos: $ReadOnlyArray<RawMessageInfo>,
   +messageDatas: $ReadOnlyArray<MessageData>,
   +threadsToMessageIndices: $ReadOnlyMap<string, number[]>,
-  +threadIDs: $ReadOnlyArray<string>,
   +userNotMemberOfSubthreads: Set<string>,
   +fetchMessageInfoByID: (messageID: string) => Promise<any>,
   +userID: string,
 };
-
-async function generateNotifUserInfoPromise(
-  inputData: GenerateNotifUserInfoPromiseInputData,
-): Promise<?{
-  devices: $ReadOnlyArray<Device>,
-  messageDatas: Array<MessageData>,
-  messageInfos: Array<RawMessageInfo>,
-}> {
+type FetchMessageNotifyTypeReturnInstance = {
+  +messageNotifyType: MessageNotifyType,
+  +messageInfo: RawMessageInfo,
+  +messageData: MessageData,
+};
+async function fetchMessageNotifyType(
+  inputData: FetchMessageNotifyTypeInputData,
+): Promise<Map<string, Array<FetchMessageNotifyTypeReturnInstance>>> {
   const {
-    messageNotifyType,
-    devices,
     newMessageInfos,
     messageDatas,
     threadsToMessageIndices,
-    threadIDs,
     userNotMemberOfSubthreads,
-    userID,
     fetchMessageInfoByID,
+    userID,
   } = inputData;
 
-  const promises: Array<
-    Promise<?{
-      +messageInfo: RawMessageInfo,
-      +messageData: MessageData,
-    }>,
-  > = [];
-
-  for (const threadID of threadIDs) {
-    const messageIndices = threadsToMessageIndices.get(threadID);
-    invariant(messageIndices, `indices should exist for thread ${threadID}`);
-
+  const promises: Array<Promise<?FetchMessageNotifyTypeReturnInstance>> = [];
+  for (const [, messageIndices] of threadsToMessageIndices) {
     promises.push(
       ...messageIndices.map(async messageIndex => {
         const messageInfo = newMessageInfos[messageIndex];
         if (messageInfo.creatorID === userID) {
+          // We don't need to notify the message author about their message
           return undefined;
         }
 
+        const messageData = messageDatas[messageIndex];
+
         const { type } = messageInfo;
         const { getMessageNotifyType } = messageSpecs[type];
 
-        if (!getMessageNotifyType) {
-          return undefined;
+        let messageNotifyType = messageNotifyTypes.SET_UNREAD;
+        if (getMessageNotifyType) {
+          messageNotifyType = await getMessageNotifyType(
+            messageInfo,
+            messageData,
+            {
+              notifTargetUserID: userID,
+              userNotMemberOfSubthreads,
+              fetchMessageInfoByID,
+            },
+          );
         }
 
-        const messageData = messageDatas[messageIndex];
-        const thisMessageNotifyType = await getMessageNotifyType(
+        return {
+          messageNotifyType,
           messageInfo,
           messageData,
-          {
-            notifTargetUserID: userID,
-            userNotMemberOfSubthreads,
-            fetchMessageInfoByID,
-          },
-        );
-
-        return thisMessageNotifyType === messageNotifyType
-          ? { messageInfo, messageData }
-          : undefined;
+        };
       }),
     );
   }
+  const results = await Promise.all(promises);
 
-  const messagesToNotify = await Promise.all(promises);
-  const filteredMessagesToNotify = messagesToNotify.filter(Boolean);
+  const returnMap = new Map<
+    string,
+    Array<FetchMessageNotifyTypeReturnInstance>,
+  >();
+  for (const result of results) {
+    if (!result) {
+      continue;
+    }
+    let resultsForThread = returnMap.get(result.messageInfo.threadID);
+    if (!resultsForThread) {
+      resultsForThread = [];
+      returnMap.set(result.messageInfo.threadID, resultsForThread);
+    }
+    resultsForThread.push(result);
+  }
+  return returnMap;
+}
+
+type GenerateNotifUserInfoInputData = {
+  +messageNotifyType: MessageNotifyType,
+  +messages: $ReadOnlyMap<
+    string,
+    $ReadOnlyArray<FetchMessageNotifyTypeReturnInstance>,
+  >,
+  +devices: $ReadOnlyArray<Device>,
+  +threadIDs: $ReadOnlyArray<string>,
+};
+function generateNotifUserInfo(inputData: GenerateNotifUserInfoInputData): ?{
+  devices: $ReadOnlyArray<Device>,
+  messageInfos: Array<RawMessageInfo>,
+  messageDatas: Array<MessageData>,
+} {
+  const { messageNotifyType, messages, devices, threadIDs } = inputData;
+
+  const messageInfos: Array<RawMessageInfo> = [];
+  const messageDatas: Array<MessageData> = [];
+
+  for (const threadID of threadIDs) {
+    const threadMessages = messages.get(threadID);
+    if (!threadMessages) {
+      continue;
+    }
+    for (const message of threadMessages) {
+      if (message.messageNotifyType !== messageNotifyType) {
+        continue;
+      }
+      messageInfos.push(message.messageInfo);
+      messageDatas.push(message.messageData);
+    }
+  }
 
-  if (filteredMessagesToNotify.length === 0) {
+  if (messageInfos.length === 0) {
     return undefined;
   }
 
-  return {
-    devices,
-    messageInfos: filteredMessagesToNotify.map(
-      ({ messageInfo }) => messageInfo,
-    ),
-    messageDatas: filteredMessagesToNotify.map(
-      ({ messageData }) => messageData,
-    ),
-  };
+  return { devices, messageInfos, messageDatas };
 }
 
 export type UserAllowsNotifInputData = {
@@ -238,6 +270,7 @@
   stringToVersionKey,
   versionKeyToString,
   getDevicesByPlatform,
-  generateNotifUserInfoPromise,
+  fetchMessageNotifyType,
+  generateNotifUserInfo,
   userAllowsNotif,
 };