Update retry behavior for backed-up attachments

This commit is contained in:
trevor-signal 2025-08-18 13:53:04 -04:00 committed by GitHub
commit a7e22b14b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 50 additions and 2 deletions

View file

@ -96,8 +96,14 @@ const DEFAULT_RETRY_CONFIG = {
}, },
}; };
const BACKUP_RETRY_CONFIG = { const BACKUP_RETRY_CONFIG = {
...DEFAULT_RETRY_CONFIG, // Always retry if we think the item may end up being backed up
maxAttempts: Infinity, maxAttempts: Infinity,
backoffConfig: {
// 30 seconds, 5 minutes, 50 minutes, 500 minutes (~8.3hrs), (max) 3 days
multiplier: 10,
firstBackoffs: [30 * durations.SECOND],
maxBackoffTime: 3 * durations.DAY,
},
}; };
type RunDownloadAttachmentJobOptions = { type RunDownloadAttachmentJobOptions = {
@ -224,7 +230,10 @@ export class AttachmentDownloadManager extends JobManager<CoreAttachmentDownload
limit, limit,
prioritizeMessageIds: [...this.#visibleTimelineMessages], prioritizeMessageIds: [...this.#visibleTimelineMessages],
sources: window.storage.get('backupMediaDownloadPaused') sources: window.storage.get('backupMediaDownloadPaused')
? [AttachmentDownloadSource.STANDARD] ? [
AttachmentDownloadSource.STANDARD,
AttachmentDownloadSource.BACKFILL,
]
: undefined, : undefined,
timestamp: Date.now(), timestamp: Date.now(),
}); });

View file

@ -1100,6 +1100,7 @@ type WritableInterface = {
saveAttachmentDownloadJob: (job: AttachmentDownloadJobType) => void; saveAttachmentDownloadJob: (job: AttachmentDownloadJobType) => void;
saveAttachmentDownloadJobs: (jobs: Array<AttachmentDownloadJobType>) => void; saveAttachmentDownloadJobs: (jobs: Array<AttachmentDownloadJobType>) => void;
resetAttachmentDownloadActive: () => void; resetAttachmentDownloadActive: () => void;
resetBackupAttachmentDownloadJobsRetryAfter: () => void;
removeAttachmentDownloadJob: (job: AttachmentDownloadJobType) => void; removeAttachmentDownloadJob: (job: AttachmentDownloadJobType) => void;
removeAttachmentDownloadJobsForMessage: (messageId: string) => void; removeAttachmentDownloadJobsForMessage: (messageId: string) => void;
removeAllBackupAttachmentDownloadJobs: () => void; removeAllBackupAttachmentDownloadJobs: () => void;

View file

@ -599,6 +599,7 @@ export const DataWriter: ServerWritableInterface = {
saveAttachmentDownloadJob, saveAttachmentDownloadJob,
saveAttachmentDownloadJobs, saveAttachmentDownloadJobs,
resetAttachmentDownloadActive, resetAttachmentDownloadActive,
resetBackupAttachmentDownloadJobsRetryAfter,
removeAttachmentDownloadJob, removeAttachmentDownloadJob,
removeAttachmentDownloadJobsForMessage, removeAttachmentDownloadJobsForMessage,
removeAllBackupAttachmentDownloadJobs, removeAllBackupAttachmentDownloadJobs,
@ -5746,6 +5747,16 @@ function resetAttachmentDownloadActive(db: WritableDB): void {
).run(); ).run();
} }
function resetBackupAttachmentDownloadJobsRetryAfter(db: WritableDB): void {
db.prepare(
`
UPDATE attachment_downloads
SET retryAfter = NULL
WHERE originalSource = 'backup_import'
`
).run();
}
function removeAttachmentDownloadJob( function removeAttachmentDownloadJob(
db: WritableDB, db: WritableDB,
job: Pick< job: Pick<

View file

@ -502,6 +502,31 @@ describe('AttachmentDownloadManager/JobManager', () => {
jobs[2], jobs[2],
]); ]);
}); });
it('retries backup job immediately if retryAfters are reset', async () => {
strictAssert(downloadManager, 'must exist');
const jobs = await addJobs(1, {
source: AttachmentDownloadSource.BACKUP_IMPORT,
});
const jobAttempts = getPromisesForAttempts(jobs[0], 2);
runJob.callsFake(async () => {
return new Promise<{ status: 'finished' | 'retry' }>(resolve => {
Promise.resolve().then(() => {
resolve({ status: 'retry' });
});
});
});
await downloadManager?.start();
await jobAttempts[0].completed;
assertRunJobCalledWith([jobs[0]]);
await DataWriter.resetBackupAttachmentDownloadJobsRetryAfter();
await downloadManager.start();
await jobAttempts[1].completed;
});
describe('will drop jobs from non-media backup imports that are old', () => { describe('will drop jobs from non-media backup imports that are old', () => {
it('will not queue attachments older than 90 days (2 * message queue time)', async () => { it('will not queue attachments older than 90 days (2 * message queue time)', async () => {
hasMediaBackups.returns(false); hasMediaBackups.returns(false);

View file

@ -20,6 +20,8 @@ export async function pauseBackupMediaDownload(): Promise<void> {
export async function resumeBackupMediaDownload(): Promise<void> { export async function resumeBackupMediaDownload(): Promise<void> {
log.info('Resuming media download'); log.info('Resuming media download');
// Reset the retry-afters so that all jobs will be immediately retried
await DataWriter.resetBackupAttachmentDownloadJobsRetryAfter();
return startBackupMediaDownload(); return startBackupMediaDownload();
} }