Avoid race condition in AttachmentDownloadQueue
This commit is contained in:
parent
eb2573187d
commit
e72a5c3320
1 changed files with 53 additions and 33 deletions
|
@ -1,16 +1,16 @@
|
||||||
// Copyright 2023 Signal Messenger, LLC
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { MessageAttributesType } from '../model-types.d';
|
|
||||||
import type { MessageModel } from '../models/messages';
|
import type { MessageModel } from '../models/messages';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import { isMoreRecentThan } from './timestamp';
|
import { isMoreRecentThan } from './timestamp';
|
||||||
|
import { isNotNil } from './isNotNil';
|
||||||
|
|
||||||
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
||||||
const MAX_ATTACHMENT_MSGS_TO_DOWNLOAD = 250;
|
const MAX_ATTACHMENT_MSGS_TO_DOWNLOAD = 250;
|
||||||
|
|
||||||
let isEnabled = true;
|
let isEnabled = true;
|
||||||
let attachmentDownloadQueue: Array<MessageModel> | undefined = [];
|
let attachmentDownloadQueue: Array<string> | undefined = [];
|
||||||
const queueEmptyCallbacks: Set<() => void> = new Set();
|
const queueEmptyCallbacks: Set<() => void> = new Set();
|
||||||
|
|
||||||
export function shouldUseAttachmentDownloadQueue(): boolean {
|
export function shouldUseAttachmentDownloadQueue(): boolean {
|
||||||
|
@ -25,6 +25,11 @@ export function registerQueueEmptyCallback(callback: () => void): void {
|
||||||
queueEmptyCallbacks.add(callback);
|
queueEmptyCallbacks.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onQueueEmpty() {
|
||||||
|
queueEmptyCallbacks.forEach(callback => callback());
|
||||||
|
queueEmptyCallbacks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
export function addToAttachmentDownloadQueue(
|
export function addToAttachmentDownloadQueue(
|
||||||
idLog: string,
|
idLog: string,
|
||||||
message: MessageModel
|
message: MessageModel
|
||||||
|
@ -33,7 +38,7 @@ export function addToAttachmentDownloadQueue(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentDownloadQueue.unshift(message);
|
attachmentDownloadQueue.unshift(message.id);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`${idLog}: Adding to attachmentDownloadQueue`,
|
`${idLog}: Adding to attachmentDownloadQueue`,
|
||||||
|
@ -42,48 +47,63 @@ export function addToAttachmentDownloadQueue(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function flushAttachmentDownloadQueue(): Promise<void> {
|
export async function flushAttachmentDownloadQueue(): Promise<void> {
|
||||||
if (!attachmentDownloadQueue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: ts/models/messages.ts expects this global to become undefined
|
// NOTE: ts/models/messages.ts expects this global to become undefined
|
||||||
// once we stop processing the queue.
|
// once we stop processing the queue.
|
||||||
isEnabled = false;
|
isEnabled = false;
|
||||||
|
|
||||||
const attachmentsToDownload = attachmentDownloadQueue.filter(
|
if (!attachmentDownloadQueue?.length) {
|
||||||
(message, index) =>
|
onQueueEmpty();
|
||||||
index <= MAX_ATTACHMENT_MSGS_TO_DOWNLOAD ||
|
return;
|
||||||
isMoreRecentThan(message.getReceivedAt(), MAX_ATTACHMENT_DOWNLOAD_AGE) ||
|
}
|
||||||
// Stickers and long text attachments has to be downloaded for UI
|
|
||||||
// to display the message properly.
|
const messageIdsToDownload = attachmentDownloadQueue.slice(
|
||||||
message.hasRequiredAttachmentDownloads()
|
0,
|
||||||
|
MAX_ATTACHMENT_MSGS_TO_DOWNLOAD
|
||||||
|
);
|
||||||
|
|
||||||
|
const messageIdsToSave: Array<string> = [];
|
||||||
|
let numMessagesQueued = 0;
|
||||||
|
await Promise.all(
|
||||||
|
messageIdsToDownload.map(async messageId => {
|
||||||
|
const message = window.MessageCache.__DEPRECATED$getById(messageId);
|
||||||
|
if (!message) {
|
||||||
|
log.warn(
|
||||||
|
'attachmentDownloadQueue: message not found in messageCache, maybe it was deleted?'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isMoreRecentThan(
|
||||||
|
message.getReceivedAt(),
|
||||||
|
MAX_ATTACHMENT_DOWNLOAD_AGE
|
||||||
|
) ||
|
||||||
|
// Stickers and long text attachments has to be downloaded for UI
|
||||||
|
// to display the message properly.
|
||||||
|
message.hasRequiredAttachmentDownloads()
|
||||||
|
) {
|
||||||
|
const shouldSave = await message.queueAttachmentDownloads();
|
||||||
|
if (shouldSave) {
|
||||||
|
messageIdsToSave.push(messageId);
|
||||||
|
}
|
||||||
|
numMessagesQueued += 1;
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
'Downloading recent attachments of total attachments',
|
`Downloading recent attachments for ${numMessagesQueued} ` +
|
||||||
attachmentsToDownload.length,
|
`of ${attachmentDownloadQueue.length} total messages`
|
||||||
attachmentDownloadQueue.length
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const messagesWithDownloads = await Promise.all(
|
const messagesToSave = messageIdsToSave
|
||||||
attachmentsToDownload.map(message => {
|
.map(messageId => window.MessageCache.accessAttributes(messageId))
|
||||||
const updatedMessage =
|
.filter(isNotNil);
|
||||||
window.MessageCache.__DEPRECATED$getById(message.id) ?? message;
|
|
||||||
return updatedMessage.queueAttachmentDownloads();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const messagesToSave: Array<MessageAttributesType> = [];
|
|
||||||
messagesWithDownloads.forEach((shouldSave, messageKey) => {
|
|
||||||
if (shouldSave) {
|
|
||||||
const message = attachmentsToDownload[messageKey];
|
|
||||||
messagesToSave.push(message.attributes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await window.Signal.Data.saveMessages(messagesToSave, {
|
await window.Signal.Data.saveMessages(messagesToSave, {
|
||||||
ourAci: window.storage.user.getCheckedAci(),
|
ourAci: window.storage.user.getCheckedAci(),
|
||||||
});
|
});
|
||||||
|
|
||||||
attachmentDownloadQueue = undefined;
|
attachmentDownloadQueue = undefined;
|
||||||
queueEmptyCallbacks.forEach(callback => callback());
|
onQueueEmpty();
|
||||||
queueEmptyCallbacks.clear();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue