Avoid race when downloading group avatars post-import

This commit is contained in:
trevor-signal
2026-02-11 08:55:26 -05:00
committed by GitHub
parent ff9d247cb2
commit 291000f297
2 changed files with 46 additions and 17 deletions

View File

@@ -11,6 +11,8 @@ import type { JOB_STATUS } from './JobQueue.std.js';
import { JobQueue } from './JobQueue.std.js';
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore.preload.js';
import { parseUnknown } from '../util/schemas.std.js';
import { waitForOnline } from '../util/waitForOnline.dom.js';
import { isOnline } from '../textsecure/WebAPI.preload.js';
const groupAvatarJobDataSchema = z.object({
conversationId: z.string(),
@@ -29,6 +31,8 @@ export class GroupAvatarJobQueue extends JobQueue<GroupAvatarJobData> {
{ attempt, log }: Readonly<{ attempt: number; log: LoggerType }>
): Promise<typeof JOB_STATUS.NEEDS_RETRY | undefined> {
const { conversationId, newAvatarUrl } = data;
await waitForOnline({ server: { isOnline } });
const logId = `groupAvatarJobQueue(${conversationId}, attempt=${attempt})`;
const convo = window.ConversationController.get(conversationId);
@@ -43,15 +47,21 @@ export class GroupAvatarJobQueue extends JobQueue<GroupAvatarJobData> {
return undefined;
}
// Generate correct attributes patch
const patch = await applyNewAvatar({
newAvatarUrl,
attributes,
logId,
});
await convo.queueJob('GroupAvatarJobQueue', async () => {
if (convo.attributes.remoteAvatarUrl !== newAvatarUrl) {
return;
}
convo.set(patch);
await DataWriter.updateConversation(convo.attributes);
// Generate correct attributes patch
const patch = await applyNewAvatar({
newAvatarUrl,
attributes,
logId,
});
convo.set(patch);
await DataWriter.updateConversation(convo.attributes);
});
return undefined;
}
@@ -60,5 +70,5 @@ export class GroupAvatarJobQueue extends JobQueue<GroupAvatarJobData> {
export const groupAvatarJobQueue = new GroupAvatarJobQueue({
store: jobQueueDatabaseStore,
queueType: 'groupAvatar',
maxAttempts: 25,
maxAttempts: 5,
});

View File

@@ -279,7 +279,6 @@ export class BackupImportStream extends Writable {
#customColorById = new Map<number, CustomColorDataType>();
#releaseNotesRecipientId: Long | undefined;
#releaseNotesChatId: Long | undefined;
#pendingGroupAvatars = new Map<string, string>();
#pinnedMessages: Array<PinnedMessageParams> = [];
#frameErrorCount: number = 0;
#backupTier: BackupLevel | undefined;
@@ -422,7 +421,19 @@ export class BackupImportStream extends Writable {
// conversation's last message, which uses redux selectors)
await loadAllAndReinitializeRedux();
const allConversations = window.ConversationController.getAll();
const allConversations = window.ConversationController.getAll().sort(
(convoA, convoB) => {
if (convoA.get('isPinned')) {
return -1;
}
if (convoB.get('isPinned')) {
return 1;
}
return (
(convoB.get('active_at') ?? 0) - (convoA.get('active_at') ?? 0)
);
}
);
// Update last message in every active conversation now that we have
// them loaded into memory.
@@ -445,12 +456,21 @@ export class BackupImportStream extends Writable {
// Schedule group avatar download.
await pMap(
[...this.#pendingGroupAvatars.entries()],
async ([conversationId, newAvatarUrl]) => {
allConversations,
async conversation => {
if (this.options.type === 'cross-client-integration-test') {
return;
}
await groupAvatarJobQueue.add({ conversationId, newAvatarUrl });
if (
!isGroup(conversation.attributes) ||
!conversation.get('remoteAvatarUrl')
) {
return;
}
await groupAvatarJobQueue.add({
conversationId: conversation.get('id'),
newAvatarUrl: conversation.get('remoteAvatarUrl'),
});
},
{ concurrency: MAX_CONCURRENCY }
);
@@ -1218,6 +1238,7 @@ export class BackupImportStream extends Writable {
url: avatarUrl,
}
: undefined,
remoteAvatarUrl: dropNull(avatarUrl),
color: fromAvatarColor(group.avatarColor),
colorFromPrimary: dropNull(group.avatarColor),
@@ -1319,9 +1340,7 @@ export class BackupImportStream extends Writable {
: undefined,
announcementsOnly: dropNull(announcementsOnly),
};
if (avatarUrl) {
this.#pendingGroupAvatars.set(attrs.id, avatarUrl);
}
if (group.blocked) {
await itemStorage.blocked.addBlockedGroup(groupId);
}