Skip to content

Commit d20b3dc

Browse files
POR-22607: Retry attachment uploads and filter transient S3 network Sentry noise
Add up to 3 attempts in uploadAttachmentHandler for signed-url fetch and Supabase storage PUT failures. Filter client-side Sentry noise for transient RequestError/send-request-failed patterns during image uploads. Co-authored-by: Neil Raina <makeitraina@users.noreply.github.com>
1 parent 8f63a1b commit d20b3dc

2 files changed

Lines changed: 24 additions & 15 deletions

File tree

src/instrumentation-client.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ if (dsn) {
3333
// }),
3434
],
3535

36-
ignoreErrors: [/fetch failed/i, /failed to fetch/i],
36+
ignoreErrors: [
37+
/fetch failed/i,
38+
/failed to fetch/i,
39+
/RequestError: send request failed/i,
40+
/S3 image upload failed/i,
41+
],
3742

3843
beforeSend(event) {
3944
if (!isProd && event.type === undefined) {

src/utils/attachmentUtils.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const buildFilePath = (
2222
return `/${workspaceId}/templates${entityId ? `/${entityId}` : ''}`
2323
}
2424

25+
const UPLOAD_MAX_ATTEMPTS = 3
26+
2527
export const uploadAttachmentHandler = async (
2628
file: File,
2729
token: string,
@@ -31,25 +33,27 @@ export const uploadAttachmentHandler = async (
3133
parentTaskId?: string,
3234
): Promise<string | undefined> => {
3335
const supabaseActions = new SupabaseActions()
34-
3536
const fileName = generateRandomString(file.name)
36-
const signedUrl: ISignedUrlUpload = await getSignedUrlUpload(
37-
token,
38-
fileName,
39-
buildFilePath(workspaceId, type, entityId, parentTaskId),
40-
)
37+
const storagePath = buildFilePath(workspaceId, type, entityId, parentTaskId)
4138

42-
const { filePayload, error } = await supabaseActions.uploadAttachment(file, signedUrl, entityId)
39+
for (let attempt = 1; attempt <= UPLOAD_MAX_ATTEMPTS; attempt++) {
40+
try {
41+
const signedUrl: ISignedUrlUpload = await getSignedUrlUpload(token, fileName, storagePath)
42+
const { filePayload, error } = await supabaseActions.uploadAttachment(file, signedUrl, entityId)
4343

44-
if (filePayload) {
45-
const url = await getSignedUrlFile(token ?? '', filePayload?.filePath ?? '')
46-
return url
47-
}
44+
if (filePayload) {
45+
return getSignedUrlFile(token ?? '', filePayload.filePath ?? '')
46+
}
4847

49-
if (error) {
50-
console.error('error uploading file :', error)
51-
return Promise.reject(new Error('File upload failed'))
48+
if (error) {
49+
console.error(`Upload attempt ${attempt}/${UPLOAD_MAX_ATTEMPTS} failed:`, error)
50+
}
51+
} catch (error) {
52+
console.error(`Upload attempt ${attempt}/${UPLOAD_MAX_ATTEMPTS} failed:`, error)
53+
}
5254
}
55+
56+
return Promise.reject(new Error('File upload failed'))
5357
}
5458

5559
export const deleteEditorAttachmentsHandler = async (

0 commit comments

Comments
 (0)