Merge pull request #2559 from Bilb/fix-attachments-failure-mark-as-error

fix: mark attachment as failure if we get a 404
pull/2560/head
Audric Ackermann 3 years ago committed by GitHub
commit acd2178779
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -77,10 +77,13 @@ export const downloadFileFromFileServer = async (
if (window.sessionFeatureFlags?.debug.debugFileServerRequests) { if (window.sessionFeatureFlags?.debug.debugFileServerRequests) {
window.log.info(`about to try to download fsv2: "${urlToGet}"`); window.log.info(`about to try to download fsv2: "${urlToGet}"`);
} }
// this throws if we get a 404 from the file server
const result = await OnionSending.getBinaryViaOnionV4FromFileServer({ const result = await OnionSending.getBinaryViaOnionV4FromFileServer({
abortSignal: new AbortController().signal, abortSignal: new AbortController().signal,
endpoint: urlToGet, endpoint: urlToGet,
method: 'GET', method: 'GET',
throwError: true,
}); });
if (window.sessionFeatureFlags?.debug.debugFileServerRequests) { if (window.sessionFeatureFlags?.debug.debugFileServerRequests) {
window.log.info(`download fsv2: "${urlToGet} got result:`, JSON.stringify(result)); window.log.info(`download fsv2: "${urlToGet} got result:`, JSON.stringify(result));

@ -20,6 +20,7 @@ export async function fetchBinaryFromSogsWithOnionV4(sendOptions: {
headers: Record<string, any> | null; headers: Record<string, any> | null;
roomId: string; roomId: string;
fileId: string; fileId: string;
throwError: boolean;
}): Promise<Uint8Array | null> { }): Promise<Uint8Array | null> {
const { const {
serverUrl, serverUrl,
@ -30,6 +31,7 @@ export async function fetchBinaryFromSogsWithOnionV4(sendOptions: {
doNotIncludeOurSogsHeaders, doNotIncludeOurSogsHeaders,
roomId, roomId,
fileId, fileId,
throwError,
} = sendOptions; } = sendOptions;
const stringifiedBody = null; const stringifiedBody = null;
@ -62,12 +64,12 @@ export async function fetchBinaryFromSogsWithOnionV4(sendOptions: {
body: stringifiedBody, body: stringifiedBody,
useV4: true, useV4: true,
}, },
false, throwError,
abortSignal abortSignal
); );
if (!res?.bodyBinary) { if (!res?.bodyBinary) {
window.log.info('fetchBinaryFromSogsWithOnionV4 no binary content'); window.log.info('fetchBinaryFromSogsWithOnionV4 no binary content with code', res?.status_code);
return null; return null;
} }
return res.bodyBinary; return res.bodyBinary;
@ -169,6 +171,7 @@ const sogsV3FetchPreview = async (
doNotIncludeOurSogsHeaders: true, doNotIncludeOurSogsHeaders: true,
roomId: roomInfos.roomId, roomId: roomInfos.roomId,
fileId: roomInfos.imageID, fileId: roomInfos.imageID,
throwError: false,
}); });
if (fetched && fetched.byteLength) { if (fetched && fetched.byteLength) {
return fetched; return fetched;
@ -198,6 +201,7 @@ export const sogsV3FetchFileByFileID = async (
doNotIncludeOurSogsHeaders: true, doNotIncludeOurSogsHeaders: true,
roomId: roomInfos.roomId, roomId: roomInfos.roomId,
fileId, fileId,
throwError: true,
}); });
return fetched && fetched.byteLength ? fetched : null; return fetched && fetched.byteLength ? fetched : null;
}; };

@ -27,6 +27,14 @@ export const resetSnodeFailureCount = () => {
const snodeFailureThreshold = 3; const snodeFailureThreshold = 3;
export const OXEN_SERVER_ERROR = 'Oxen Server error'; export const OXEN_SERVER_ERROR = 'Oxen Server error';
// Not ideal, but a pRetry.AbortError only lets us customize the message, and not the code
const errorContent404 = ': 404 ';
export const was404Error = (error: Error) => error.message.includes(errorContent404);
export const buildErrorMessageWithFailedCode = (prefix: string, code: number, suffix: string) =>
`${prefix}: ${code} ${suffix}`;
/** /**
* When sending a request over onion, we might get two status. * When sending a request over onion, we might get two status.
* The first one, on the request itself, the other one in the json returned. * The first one, on the request itself, the other one in the json returned.

@ -2,6 +2,7 @@
import { OnionPaths } from '.'; import { OnionPaths } from '.';
import { import {
buildErrorMessageWithFailedCode,
FinalDestNonSnodeOptions, FinalDestNonSnodeOptions,
FinalRelayOptions, FinalRelayOptions,
Onions, Onions,
@ -212,10 +213,17 @@ const sendViaOnionV4ToNonSnodeWithRetries = async (
}; };
} }
if (foundStatusCode === 404) { if (foundStatusCode === 404) {
// this is most likely that a 404 won't fix itself. So just stop right here retries by throwing a non retryable error window.log.warn(
throw new pRetry.AbortError(
`Got 404 while sendViaOnionV4ToNonSnodeWithRetries with url:${url}. Stopping retries` `Got 404 while sendViaOnionV4ToNonSnodeWithRetries with url:${url}. Stopping retries`
); );
// most likely, a 404 won't fix itself. So just stop right here retries by throwing a non retryable error
throw new pRetry.AbortError(
buildErrorMessageWithFailedCode(
'sendViaOnionV4ToNonSnodeWithRetries',
404,
`with url:${url}. Stopping retries`
)
);
} }
// we consider those cases as an error, and trigger a retry (if possible), by throwing a non-abortable error // we consider those cases as an error, and trigger a retry (if possible), by throwing a non-abortable error
throw new Error( throw new Error(
@ -239,7 +247,7 @@ const sendViaOnionV4ToNonSnodeWithRetries = async (
} }
); );
} catch (e) { } catch (e) {
window?.log?.warn('sendViaOnionV4ToNonSnodeRetryable failed ', e.message); window?.log?.warn('sendViaOnionV4ToNonSnodeRetryable failed ', e.message, throwErrors);
if (throwErrors) { if (throwErrors) {
throw e; throw e;
} }
@ -455,8 +463,9 @@ async function getBinaryViaOnionV4FromFileServer(sendOptions: {
endpoint: string; endpoint: string;
method: string; method: string;
abortSignal: AbortSignal; abortSignal: AbortSignal;
throwError: boolean;
}): Promise<OnionV4BinarySnodeResponse | null> { }): Promise<OnionV4BinarySnodeResponse | null> {
const { endpoint, method, abortSignal } = sendOptions; const { endpoint, method, abortSignal, throwError } = sendOptions;
if (!endpoint.startsWith('/')) { if (!endpoint.startsWith('/')) {
throw new Error('endpoint needs a leading /'); throw new Error('endpoint needs a leading /');
} }
@ -465,6 +474,9 @@ async function getBinaryViaOnionV4FromFileServer(sendOptions: {
if (window.sessionFeatureFlags?.debug.debugFileServerRequests) { if (window.sessionFeatureFlags?.debug.debugFileServerRequests) {
window.log.info(`getBinaryViaOnionV4FromFileServer fsv2: "${builtUrl} `); window.log.info(`getBinaryViaOnionV4FromFileServer fsv2: "${builtUrl} `);
} }
// this throws for a bunch of reasons.
// One of them, is if we get a 404 (i.e. the file server was reached but reported no such attachments exists)
const res = await OnionSending.sendViaOnionV4ToNonSnodeWithRetries( const res = await OnionSending.sendViaOnionV4ToNonSnodeWithRetries(
fileServerPubKey, fileServerPubKey,
builtUrl, builtUrl,
@ -474,7 +486,7 @@ async function getBinaryViaOnionV4FromFileServer(sendOptions: {
body: null, body: null,
useV4: true, useV4: true,
}, },
false, throwError,
abortSignal abortSignal
); );

@ -8,6 +8,7 @@ import { MessageModel } from '../../models/message';
import { downloadAttachment, downloadAttachmentSogsV3 } from '../../receiver/attachments'; import { downloadAttachment, downloadAttachmentSogsV3 } from '../../receiver/attachments';
import { initializeAttachmentLogic, processNewAttachment } from '../../types/MessageAttachment'; import { initializeAttachmentLogic, processNewAttachment } from '../../types/MessageAttachment';
import { getAttachmentMetadata } from '../../types/message/initializeAttachmentMetadata'; import { getAttachmentMetadata } from '../../types/message/initializeAttachmentMetadata';
import { was404Error } from '../apis/snode_api/onions';
// this may cause issues if we increment that value to > 1, but only having one job will block the whole queue while one attachment is downloading // this may cause issues if we increment that value to > 1, but only having one job will block the whole queue while one attachment is downloading
const MAX_ATTACHMENT_JOB_PARALLELISM = 3; const MAX_ATTACHMENT_JOB_PARALLELISM = 3;
@ -175,6 +176,7 @@ async function _runJob(job: any) {
let downloaded; let downloaded;
try { try {
// those two functions throw if they get a 404
if (isOpenGroupV2) { if (isOpenGroupV2) {
downloaded = await downloadAttachmentSogsV3(attachment, openGroupV2Details); downloaded = await downloadAttachmentSogsV3(attachment, openGroupV2Details);
} else { } else {
@ -189,14 +191,12 @@ async function _runJob(job: any) {
} from message ${found.idForLogging()} as permanent error` } from message ${found.idForLogging()} as permanent error`
); );
await _finishJob(found, id);
// Make sure to fetch the message from DB here right before writing it. // Make sure to fetch the message from DB here right before writing it.
// This is to avoid race condition where multiple attachments in a single message get downloaded at the same time, // This is to avoid race condition where multiple attachments in a single message get downloaded at the same time,
// and tries to update the same message. // and tries to update the same message.
found = await Data.getMessageById(messageId); found = await Data.getMessageById(messageId);
_addAttachmentToMessage(found, _markAttachmentAsError(attachment), { type, index }); _addAttachmentToMessage(found, _markAttachmentAsError(attachment), { type, index });
await _finishJob(found, id);
return; return;
} }
@ -232,7 +232,9 @@ async function _runJob(job: any) {
// tslint:disable: restrict-plus-operands // tslint:disable: restrict-plus-operands
const currentAttempt: 1 | 2 | 3 = (attempts || 0) + 1; const currentAttempt: 1 | 2 | 3 = (attempts || 0) + 1;
if (currentAttempt >= 3) { // if we get a 404 error for attachment downloaded, we can safely assume that the attachment expired server-side.
// so there is no need to continue trying to download it.
if (currentAttempt >= 3 || was404Error(error)) {
logger.error( logger.error(
`_runJob: ${currentAttempt} failed attempts, marking attachment ${id} from message ${found?.idForLogging()} as permament error:`, `_runJob: ${currentAttempt} failed attempts, marking attachment ${id} from message ${found?.idForLogging()} as permament error:`,
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
@ -321,40 +323,29 @@ function _addAttachmentToMessage(
return; return;
} }
if (type === 'preview') { // for quote and previews, if the attachment cannot be downloaded we just erase it from the message itself, so just the title or body is rendered
if (type === 'preview' || type === 'quote') {
if (type === 'quote') {
const quote = message.get('quote');
if (!quote) {
throw new Error("_addAttachmentToMessage: quote didn't exist");
}
delete message.attributes.quote.attachments;
return;
}
const preview = message.get('preview'); const preview = message.get('preview');
if (!preview || preview.length <= index) { if (!preview || preview.length <= index) {
throw new Error(`_addAttachmentToMessage: preview didn't exist or ${index} was too large`); throw new Error(`_addAttachmentToMessage: preview didn't exist or ${index} was too large`);
} }
const item = preview[index];
if (!item) {
throw new Error(`_addAttachmentToMessage: preview ${index} was falsey`);
}
_replaceAttachment(item, 'image', attachment, logPrefix);
return;
}
if (type === 'quote') {
const quote = message.get('quote');
if (!quote) {
throw new Error("_addAttachmentToMessage: quote didn't exist");
}
const { attachments } = quote;
if (!attachments || attachments.length <= index) {
throw new Error(
`_addAttachmentToMessage: quote attachments didn't exist or ${index} was too large`
);
}
const item = attachments[index];
if (!item) {
throw new Error(`_addAttachmentToMessage: attachment ${index} was falsey`);
}
_replaceAttachment(item, 'thumbnail', attachment, logPrefix);
delete message.attributes.preview[0].image;
return; return;
} }
// for quote and previews, if the attachment cannot be downloaded we just erase it from the message itself, so just the title or body is rendered
throw new Error( throw new Error(
`_addAttachmentToMessage: Unknown job type ${type} for message ${message.idForLogging()}` `_addAttachmentToMessage: Unknown job type ${type} for message ${message.idForLogging()}`
); );

Loading…
Cancel
Save