diff --git a/frontend/src/features/admin-form/responses/ResponsesPage/storage/useDecryptionWorkers.ts b/frontend/src/features/admin-form/responses/ResponsesPage/storage/useDecryptionWorkers.ts index 8335d7edf3..13b745a086 100644 --- a/frontend/src/features/admin-form/responses/ResponsesPage/storage/useDecryptionWorkers.ts +++ b/frontend/src/features/admin-form/responses/ResponsesPage/storage/useDecryptionWorkers.ts @@ -2,6 +2,8 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { useMutation, UseMutationOptions } from 'react-query' import { datadogLogs } from '@datadog/browser-logs' +import { waitForMs } from '~utils/waitForMs' + import { useAdminForm } from '~features/admin-form/common/queries' import { trackDownloadNetworkFailure, @@ -27,6 +29,12 @@ import { const NUM_OF_METADATA_ROWS = 5 +// We will download attachments in convoys of 5 +// This is to prevent the script from downloading too many attachments at once +// which could cause it to block downloads. +const ATTACHMENT_DOWNLOAD_CONVOY_SIZE = 5 +const ATTACHMENT_DOWNLOAD_CONVOY_MINIMUM_SEPARATION_TIME = 1000 + const killWorkers = (workers: CleanableDecryptionWorkerApi[]): void => { return workers.forEach((worker) => worker.cleanup()) } @@ -141,6 +149,7 @@ const useDecryptionWorkers = ({ const downloadStartTime = performance.now() let progress = 0 + let timeSinceLastXAttachmentDownload = 0 return new Promise((resolve, reject) => { reader @@ -181,7 +190,24 @@ const useDecryptionWorkers = ({ errorCount++ console.error('Error in getResponseInstance', e) } + if (downloadAttachments && decryptResult.downloadBlob) { + // Ensure attachments downloads are spaced out to avoid browser blocking downloads + if (progress % ATTACHMENT_DOWNLOAD_CONVOY_SIZE === 0) { + const now = new Date().getTime() + const elapsedSinceXDownloads = + now - timeSinceLastXAttachmentDownload + + const waitTime = Math.max( + 0, + ATTACHMENT_DOWNLOAD_CONVOY_MINIMUM_SEPARATION_TIME - + elapsedSinceXDownloads, + ) + if (waitTime > 0) { + await waitForMs(waitTime) + } + timeSinceLastXAttachmentDownload = now + } await downloadResponseAttachment( decryptResult.downloadBlob, decryptResult.id, diff --git a/frontend/src/utils/waitForMs.ts b/frontend/src/utils/waitForMs.ts new file mode 100644 index 0000000000..f04e7a6df5 --- /dev/null +++ b/frontend/src/utils/waitForMs.ts @@ -0,0 +1,5 @@ +export const waitForMs = (ms: number): Promise => { + return new Promise((resolve) => { + setTimeout(resolve, ms) + }) +}