Reduce timeout of some long running tasks

This commit is contained in:
Fedor Indutny 2022-10-25 17:03:05 -07:00 committed by GitHub
parent 08f2a966a1
commit 3beccbfd31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 67 deletions

View file

@ -17,6 +17,7 @@ import { HTTPError } from './textsecure/Errors';
import createTaskWithTimeout, { import createTaskWithTimeout, {
suspendTasksWithTimeout, suspendTasksWithTimeout,
resumeTasksWithTimeout, resumeTasksWithTimeout,
reportLongRunningTasks,
} from './textsecure/TaskWithTimeout'; } from './textsecure/TaskWithTimeout';
import type { import type {
MessageAttributesType, MessageAttributesType,
@ -995,6 +996,10 @@ export async function startApp(): Promise<void> {
} }
}, FIVE_MINUTES); }, FIVE_MINUTES);
setInterval(() => {
reportLongRunningTasks();
}, FIVE_MINUTES);
let mainWindowStats = { let mainWindowStats = {
isMaximized: false, isMaximized: false,
isFullScreen: false, isFullScreen: false,

View file

@ -174,6 +174,10 @@ export type MessageReceiverOptions = {
serverTrustRoot: string; serverTrustRoot: string;
}; };
const TASK_WITH_TIMEOUT_OPTIONS = {
timeout: 2 * durations.MINUTE,
};
const LOG_UNEXPECTED_URGENT_VALUES = false; const LOG_UNEXPECTED_URGENT_VALUES = false;
const MUST_BE_URGENT_TYPES: Array<SendTypesType> = [ const MUST_BE_URGENT_TYPES: Array<SendTypesType> = [
'message', 'message',
@ -331,9 +335,13 @@ export default class MessageReceiver
if (request.verb === 'PUT' && request.path === '/api/v1/queue/empty') { if (request.verb === 'PUT' && request.path === '/api/v1/queue/empty') {
this.incomingQueue.add( this.incomingQueue.add(
createTaskWithTimeout(async () => { createTaskWithTimeout(
this.onEmpty(); async () => {
}, 'incomingQueue/onEmpty') this.onEmpty();
},
'incomingQueue/onEmpty',
TASK_WITH_TIMEOUT_OPTIONS
)
); );
} }
return; return;
@ -407,12 +415,16 @@ export default class MessageReceiver
} catch (e) { } catch (e) {
request.respond(500, 'Bad encrypted websocket message'); request.respond(500, 'Bad encrypted websocket message');
log.error('Error handling incoming message:', Errors.toLogFormat(e)); log.error('Error handling incoming message:', Errors.toLogFormat(e));
await this.dispatchAndWait(new ErrorEvent(e)); await this.dispatchAndWait('websocket request', new ErrorEvent(e));
} }
}; };
this.incomingQueue.add( this.incomingQueue.add(
createTaskWithTimeout(job, 'incomingQueue/websocket') createTaskWithTimeout(
job,
'incomingQueue/websocket',
TASK_WITH_TIMEOUT_OPTIONS
)
); );
} }
@ -421,7 +433,8 @@ export default class MessageReceiver
this.incomingQueue.add( this.incomingQueue.add(
createTaskWithTimeout( createTaskWithTimeout(
async () => this.queueAllCached(), async () => this.queueAllCached(),
'incomingQueue/queueAllCached' 'incomingQueue/queueAllCached',
TASK_WITH_TIMEOUT_OPTIONS
) )
); );
@ -457,7 +470,11 @@ export default class MessageReceiver
); );
return this.incomingQueue.add( return this.incomingQueue.add(
createTaskWithTimeout(waitForIncomingQueue, 'drain/waitForIncoming') createTaskWithTimeout(
waitForIncomingQueue,
'drain/waitForIncoming',
TASK_WITH_TIMEOUT_OPTIONS
)
); );
} }
@ -605,11 +622,12 @@ export default class MessageReceiver
// Private // Private
// //
private async dispatchAndWait(event: Event): Promise<void> { private async dispatchAndWait(id: string, event: Event): Promise<void> {
this.appQueue.add( this.appQueue.add(
createTaskWithTimeout( createTaskWithTimeout(
async () => Promise.all(this.dispatchEvent(event)), async () => Promise.all(this.dispatchEvent(event)),
'dispatchEvent' `dispatchEvent(${event.type}, ${id})`,
TASK_WITH_TIMEOUT_OPTIONS
) )
); );
} }
@ -658,7 +676,9 @@ export default class MessageReceiver
: this.decryptedQueue; : this.decryptedQueue;
try { try {
return await queue.add(createTaskWithTimeout(task, id)); return await queue.add(
createTaskWithTimeout(task, id, TASK_WITH_TIMEOUT_OPTIONS)
);
} finally { } finally {
this.updateProgress(this.count); this.updateProgress(this.count);
} }
@ -684,7 +704,9 @@ export default class MessageReceiver
); );
// We don't await here because we don't want this to gate future message processing // We don't await here because we don't want this to gate future message processing
this.appQueue.add(createTaskWithTimeout(emitEmpty, 'emitEmpty')); this.appQueue.add(
createTaskWithTimeout(emitEmpty, 'emitEmpty', TASK_WITH_TIMEOUT_OPTIONS)
);
}; };
const waitForEncryptedQueue = async () => { const waitForEncryptedQueue = async () => {
@ -710,7 +732,11 @@ export default class MessageReceiver
const waitForCacheAddBatcher = async () => { const waitForCacheAddBatcher = async () => {
await this.decryptAndCacheBatcher.onIdle(); await this.decryptAndCacheBatcher.onIdle();
this.incomingQueue.add( this.incomingQueue.add(
createTaskWithTimeout(waitForIncomingQueue, 'onEmpty/waitForIncoming') createTaskWithTimeout(
waitForIncomingQueue,
'onEmpty/waitForIncoming',
TASK_WITH_TIMEOUT_OPTIONS
)
); );
}; };
@ -810,7 +836,7 @@ export default class MessageReceiver
async () => { async () => {
this.queueDecryptedEnvelope(decryptedEnvelope, payloadPlaintext); this.queueDecryptedEnvelope(decryptedEnvelope, payloadPlaintext);
}, },
'queueDecryptedEnvelope', `queueDecryptedEnvelope(${getEnvelopeId(decryptedEnvelope)})`,
TaskType.Encrypted TaskType.Encrypted
); );
} else { } else {
@ -850,7 +876,8 @@ export default class MessageReceiver
this.incomingQueue.add( this.incomingQueue.add(
createTaskWithTimeout( createTaskWithTimeout(
async () => this.queueAllCached(), async () => this.queueAllCached(),
'queueAllCached' 'queueAllCached',
TASK_WITH_TIMEOUT_OPTIONS
) )
); );
}, RETRY_TIMEOUT); }, RETRY_TIMEOUT);
@ -1074,13 +1101,14 @@ export default class MessageReceiver
const task = this.handleDecryptedEnvelope.bind(this, envelope, plaintext); const task = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
const taskWithTimeout = createTaskWithTimeout( const taskWithTimeout = createTaskWithTimeout(
task, task,
`queueDecryptedEnvelope ${id}` `queueDecryptedEnvelope ${id}`,
TASK_WITH_TIMEOUT_OPTIONS
); );
try { try {
await this.addToQueue( await this.addToQueue(
taskWithTimeout, taskWithTimeout,
'dispatchEvent', `handleDecryptedEnvelope(${id})`,
TaskType.Decrypted TaskType.Decrypted
); );
} catch (error) { } catch (error) {
@ -1125,7 +1153,7 @@ export default class MessageReceiver
this.addToQueue( this.addToQueue(
async () => this.dispatchEvent(new EnvelopeEvent(unsealedEnvelope)), async () => this.dispatchEvent(new EnvelopeEvent(unsealedEnvelope)),
'dispatchEvent', `dispatchEvent(EnvelopeEvent(${logId}))`,
TaskType.Decrypted TaskType.Decrypted
); );
@ -1398,12 +1426,14 @@ export default class MessageReceiver
if (syncMessage?.pniIdentity) { if (syncMessage?.pniIdentity) {
inProgressMessageType = 'pni identity'; inProgressMessageType = 'pni identity';
await this.handlePNIIdentity(envelope, syncMessage.pniIdentity); await this.handlePNIIdentity(envelope, syncMessage.pniIdentity);
this.removeFromCache(envelope);
return { plaintext: undefined, envelope }; return { plaintext: undefined, envelope };
} }
if (syncMessage?.pniChangeNumber) { if (syncMessage?.pniChangeNumber) {
inProgressMessageType = 'pni change number'; inProgressMessageType = 'pni change number';
await this.handlePNIChangeNumber(envelope, syncMessage.pniChangeNumber); await this.handlePNIChangeNumber(envelope, syncMessage.pniChangeNumber);
this.removeFromCache(envelope);
return { plaintext: undefined, envelope }; return { plaintext: undefined, envelope };
} }
@ -1422,6 +1452,7 @@ export default class MessageReceiver
(envelope.sourceUuid && this.isUuidBlocked(envelope.sourceUuid))) (envelope.sourceUuid && this.isUuidBlocked(envelope.sourceUuid)))
) { ) {
log.info(`${logId}: Dropping non-GV2 message from blocked sender`); log.info(`${logId}: Dropping non-GV2 message from blocked sender`);
this.removeFromCache(envelope);
return { plaintext: undefined, envelope }; return { plaintext: undefined, envelope };
} }
@ -1492,6 +1523,7 @@ export default class MessageReceiver
logUnexpectedUrgentValue(envelope, 'deliveryReceipt'); logUnexpectedUrgentValue(envelope, 'deliveryReceipt');
await this.dispatchAndWait( await this.dispatchAndWait(
getEnvelopeId(envelope),
new DeliveryEvent( new DeliveryEvent(
{ {
timestamp: envelope.timestamp, timestamp: envelope.timestamp,
@ -1822,6 +1854,8 @@ export default class MessageReceiver
throw error; throw error;
} }
const envelopeId = getEnvelopeId(envelope);
if (uuid && deviceId) { if (uuid && deviceId) {
const { cipherTextBytes, cipherTextType } = envelope; const { cipherTextBytes, cipherTextType } = envelope;
const event = new DecryptionErrorEvent( const event = new DecryptionErrorEvent(
@ -1842,11 +1876,10 @@ export default class MessageReceiver
// Avoid deadlocks by scheduling processing on decrypted queue // Avoid deadlocks by scheduling processing on decrypted queue
this.addToQueue( this.addToQueue(
async () => this.dispatchEvent(event), async () => this.dispatchEvent(event),
'decrypted/dispatchEvent', `decrypted/dispatchEvent/DecryptionErrorEvent(${envelopeId})`,
TaskType.Decrypted TaskType.Decrypted
); );
} else { } else {
const envelopeId = getEnvelopeId(envelope);
this.removeFromCache(envelope); this.removeFromCache(envelope);
log.error( log.error(
`MessageReceiver.decrypt: Envelope ${envelopeId} missing uuid or deviceId` `MessageReceiver.decrypt: Envelope ${envelopeId} missing uuid or deviceId`
@ -1939,7 +1972,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(getEnvelopeId(envelope), ev);
} }
private async handleStoryMessage( private async handleStoryMessage(
@ -2036,7 +2069,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
this.dispatchAndWait(ev); this.dispatchAndWait(logId, ev);
return; return;
} }
@ -2094,7 +2127,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
this.dispatchAndWait(ev); this.dispatchAndWait(logId, ev);
}); });
return; return;
} }
@ -2117,7 +2150,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleDataMessage( private async handleDataMessage(
@ -2148,7 +2181,7 @@ export default class MessageReceiver
return undefined; return undefined;
} }
await this.checkGroupV1Data(msg); this.checkGroupV1Data(msg);
if (msg.flags && msg.flags & Proto.DataMessage.Flags.END_SESSION) { if (msg.flags && msg.flags & Proto.DataMessage.Flags.END_SESSION) {
p = this.handleEndSession(envelope, new UUID(destination)); p = this.handleEndSession(envelope, new UUID(destination));
@ -2170,7 +2203,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
await p; await p;
@ -2236,7 +2269,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async maybeUpdateTimestamp( private async maybeUpdateTimestamp(
@ -2305,10 +2338,7 @@ export default class MessageReceiver
content.decryptionErrorMessage && content.decryptionErrorMessage &&
Bytes.isNotEmpty(content.decryptionErrorMessage) Bytes.isNotEmpty(content.decryptionErrorMessage)
) { ) {
await this.handleDecryptionError( this.handleDecryptionError(envelope, content.decryptionErrorMessage);
envelope,
content.decryptionErrorMessage
);
return; return;
} }
if (content.syncMessage) { if (content.syncMessage) {
@ -2323,7 +2353,7 @@ export default class MessageReceiver
return; return;
} }
if (content.nullMessage) { if (content.nullMessage) {
await this.handleNullMessage(envelope); this.handleNullMessage(envelope);
return; return;
} }
if (content.callingMessage) { if (content.callingMessage) {
@ -2335,7 +2365,7 @@ export default class MessageReceiver
return; return;
} }
if (content.typingMessage) { if (content.typingMessage) {
await this.handleTypingMessage(envelope, content.typingMessage); this.handleTypingMessage(envelope, content.typingMessage);
return; return;
} }
@ -2351,10 +2381,10 @@ export default class MessageReceiver
} }
} }
private async handleDecryptionError( private handleDecryptionError(
envelope: UnsealedEnvelope, envelope: UnsealedEnvelope,
decryptionError: Uint8Array decryptionError: Uint8Array
) { ): void {
const logId = getEnvelopeId(envelope); const logId = getEnvelopeId(envelope);
log.info(`handleDecryptionError: ${logId}`); log.info(`handleDecryptionError: ${logId}`);
@ -2381,7 +2411,7 @@ export default class MessageReceiver
}, },
() => this.removeFromCache(envelope) () => this.removeFromCache(envelope)
); );
await this.dispatchEvent(event); this.dispatchEvent(event);
} }
private async handleSenderKeyDistributionMessage( private async handleSenderKeyDistributionMessage(
@ -2506,6 +2536,8 @@ export default class MessageReceiver
logUnexpectedUrgentValue(envelope, type); logUnexpectedUrgentValue(envelope, type);
const logId = getEnvelopeId(envelope);
await Promise.all( await Promise.all(
receiptMessage.timestamp.map(async rawTimestamp => { receiptMessage.timestamp.map(async rawTimestamp => {
const ev = new EventClass( const ev = new EventClass(
@ -2519,15 +2551,15 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
await this.dispatchAndWait(ev); await this.dispatchAndWait(logId, ev);
}) })
); );
} }
private async handleTypingMessage( private handleTypingMessage(
envelope: UnsealedEnvelope, envelope: UnsealedEnvelope,
typingMessage: Proto.ITypingMessage typingMessage: Proto.ITypingMessage
): Promise<void> { ): void {
this.removeFromCache(envelope); this.removeFromCache(envelope);
logUnexpectedUrgentValue(envelope, 'typing'); logUnexpectedUrgentValue(envelope, 'typing');
@ -2564,7 +2596,7 @@ export default class MessageReceiver
} }
} }
await this.dispatchEvent( this.dispatchEvent(
new TypingEvent({ new TypingEvent({
sender: envelope.source, sender: envelope.source,
senderUuid: envelope.sourceUuid, senderUuid: envelope.sourceUuid,
@ -2640,9 +2672,7 @@ export default class MessageReceiver
return Bytes.toBase64(data.id); return Bytes.toBase64(data.id);
} }
private async checkGroupV1Data( private checkGroupV1Data(message: Readonly<Proto.IDataMessage>): void {
message: Readonly<Proto.IDataMessage>
): Promise<void> {
const { group } = message; const { group } = message;
if (!group) { if (!group) {
@ -2744,7 +2774,8 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); const logId = getEnvelopeId(envelope);
return this.dispatchAndWait(logId, ev);
} }
if (sentMessage.storyMessage) { if (sentMessage.storyMessage) {
@ -2767,7 +2798,7 @@ export default class MessageReceiver
return; return;
} }
await this.checkGroupV1Data(sentMessage.message); this.checkGroupV1Data(sentMessage.message);
strictAssert(sentMessage.timestamp, 'sent message without timestamp'); strictAssert(sentMessage.timestamp, 'sent message without timestamp');
@ -2847,7 +2878,8 @@ export default class MessageReceiver
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
configuration: Proto.SyncMessage.IConfiguration configuration: Proto.SyncMessage.IConfiguration
): Promise<void> { ): Promise<void> {
log.info('got configuration sync message'); const logId = getEnvelopeId(envelope);
log.info('got configuration sync message', logId);
logUnexpectedUrgentValue(envelope, 'configurationSync'); logUnexpectedUrgentValue(envelope, 'configurationSync');
@ -2855,14 +2887,15 @@ export default class MessageReceiver
configuration, configuration,
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleViewOnceOpen( private async handleViewOnceOpen(
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
sync: Proto.SyncMessage.IViewOnceOpen sync: Proto.SyncMessage.IViewOnceOpen
): Promise<void> { ): Promise<void> {
log.info('got view once open sync message'); const logId = getEnvelopeId(envelope);
log.info('got view once open sync message', logId);
logUnexpectedUrgentValue(envelope, 'viewOnceSync'); logUnexpectedUrgentValue(envelope, 'viewOnceSync');
@ -2877,14 +2910,15 @@ export default class MessageReceiver
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleMessageRequestResponse( private async handleMessageRequestResponse(
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
sync: Proto.SyncMessage.IMessageRequestResponse sync: Proto.SyncMessage.IMessageRequestResponse
): Promise<void> { ): Promise<void> {
log.info('got message request response sync message'); const logId = getEnvelopeId(envelope);
log.info('got message request response sync message', logId);
logUnexpectedUrgentValue(envelope, 'messageRequestSync'); logUnexpectedUrgentValue(envelope, 'messageRequestSync');
@ -2921,14 +2955,15 @@ export default class MessageReceiver
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleFetchLatest( private async handleFetchLatest(
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
sync: Proto.SyncMessage.IFetchLatest sync: Proto.SyncMessage.IFetchLatest
): Promise<void> { ): Promise<void> {
log.info('got fetch latest sync message'); const logId = getEnvelopeId(envelope);
log.info('got fetch latest sync message', logId);
logUnexpectedUrgentValue(envelope, 'fetchLatestManifestSync'); logUnexpectedUrgentValue(envelope, 'fetchLatestManifestSync');
@ -2937,14 +2972,15 @@ export default class MessageReceiver
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleKeys( private async handleKeys(
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
sync: Proto.SyncMessage.IKeys sync: Proto.SyncMessage.IKeys
): Promise<void> { ): Promise<void> {
log.info('got keys sync message'); const logId = getEnvelopeId(envelope);
log.info('got keys sync message', logId);
logUnexpectedUrgentValue(envelope, 'keySync'); logUnexpectedUrgentValue(envelope, 'keySync');
@ -2957,7 +2993,7 @@ export default class MessageReceiver
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
// Runs on TaskType.Encrypted queue // Runs on TaskType.Encrypted queue
@ -3019,7 +3055,8 @@ export default class MessageReceiver
operations: Array<Proto.SyncMessage.IStickerPackOperation> operations: Array<Proto.SyncMessage.IStickerPackOperation>
): Promise<void> { ): Promise<void> {
const ENUM = Proto.SyncMessage.StickerPackOperation.Type; const ENUM = Proto.SyncMessage.StickerPackOperation.Type;
log.info('got sticker pack operation sync message'); const logId = getEnvelopeId(envelope);
log.info('got sticker pack operation sync message', logId);
logUnexpectedUrgentValue(envelope, 'stickerPackSync'); logUnexpectedUrgentValue(envelope, 'stickerPackSync');
const stickerPacks = operations.map(operation => ({ const stickerPacks = operations.map(operation => ({
@ -3034,14 +3071,15 @@ export default class MessageReceiver
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleRead( private async handleRead(
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
read: Array<Proto.SyncMessage.IRead> read: Array<Proto.SyncMessage.IRead>
): Promise<void> { ): Promise<void> {
log.info('MessageReceiver.handleRead', getEnvelopeId(envelope)); const logId = getEnvelopeId(envelope);
log.info('MessageReceiver.handleRead', logId);
logUnexpectedUrgentValue(envelope, 'readSync'); logUnexpectedUrgentValue(envelope, 'readSync');
@ -3058,7 +3096,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
results.push(this.dispatchAndWait(ev)); results.push(this.dispatchAndWait(logId, ev));
} }
await Promise.all(results); await Promise.all(results);
} }
@ -3067,7 +3105,8 @@ export default class MessageReceiver
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
viewed: ReadonlyArray<Proto.SyncMessage.IViewed> viewed: ReadonlyArray<Proto.SyncMessage.IViewed>
): Promise<void> { ): Promise<void> {
log.info('MessageReceiver.handleViewed', getEnvelopeId(envelope)); const logId = getEnvelopeId(envelope);
log.info('MessageReceiver.handleViewed', logId);
logUnexpectedUrgentValue(envelope, 'viewSync'); logUnexpectedUrgentValue(envelope, 'viewSync');
@ -3084,7 +3123,7 @@ export default class MessageReceiver
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
await this.dispatchAndWait(ev); await this.dispatchAndWait(logId, ev);
}) })
); );
} }
@ -3093,7 +3132,8 @@ export default class MessageReceiver
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
contacts: Proto.SyncMessage.IContacts contacts: Proto.SyncMessage.IContacts
): Promise<void> { ): Promise<void> {
log.info(`MessageReceiver: handleContacts ${getEnvelopeId(envelope)}`); const logId = getEnvelopeId(envelope);
log.info(`MessageReceiver: handleContacts ${logId}`);
const { blob } = contacts; const { blob } = contacts;
if (!blob) { if (!blob) {
throw new Error('MessageReceiver.handleContacts: blob field was missing'); throw new Error('MessageReceiver.handleContacts: blob field was missing');
@ -3112,7 +3152,7 @@ export default class MessageReceiver
envelope.receivedAtCounter, envelope.receivedAtCounter,
envelope.timestamp envelope.timestamp
); );
await this.dispatchAndWait(contactSync); await this.dispatchAndWait(logId, contactSync);
log.info('handleContacts: finished'); log.info('handleContacts: finished');
} }
@ -3121,8 +3161,9 @@ export default class MessageReceiver
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
groups: Proto.SyncMessage.IGroups groups: Proto.SyncMessage.IGroups
): Promise<void> { ): Promise<void> {
const logId = getEnvelopeId(envelope);
log.info('group sync'); log.info('group sync');
log.info(`MessageReceiver: handleGroups ${getEnvelopeId(envelope)}`); log.info(`MessageReceiver: handleGroups ${logId}`);
const { blob } = groups; const { blob } = groups;
this.removeFromCache(envelope); this.removeFromCache(envelope);
@ -3157,7 +3198,7 @@ export default class MessageReceiver
}, },
envelope.receivedAtCounter envelope.receivedAtCounter
); );
const promise = this.dispatchAndWait(ev).catch(e => { const promise = this.dispatchAndWait(logId, ev).catch(e => {
log.error('error processing group', e); log.error('error processing group', e);
}); });
groupDetails = groupBuffer.next(); groupDetails = groupBuffer.next();
@ -3167,7 +3208,7 @@ export default class MessageReceiver
await Promise.all(promises); await Promise.all(promises);
const ev = new GroupSyncEvent(); const ev = new GroupSyncEvent();
return this.dispatchAndWait(ev); return this.dispatchAndWait(logId, ev);
} }
private async handleBlocked( private async handleBlocked(

View file

@ -1,13 +1,15 @@
// Copyright 2020-2022 Signal Messenger, LLC // Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import * as durations from '../util/durations'; import { MINUTE } from '../util/durations';
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
import { explodePromise } from '../util/explodePromise'; import { explodePromise } from '../util/explodePromise';
import { toLogFormat } from '../types/errors'; import { toLogFormat } from '../types/errors';
import * as log from '../logging/log'; import * as log from '../logging/log';
type TaskType = { type TaskType = {
id: string;
startedAt: number | undefined;
suspend(): void; suspend(): void;
resume(): void; resume(): void;
}; };
@ -31,12 +33,28 @@ export function resumeTasksWithTimeout(): void {
} }
} }
export function reportLongRunningTasks(): void {
const now = Date.now();
for (const task of tasks) {
if (task.startedAt === undefined) {
continue;
}
const duration = Math.max(0, now - task.startedAt);
if (duration > MINUTE) {
log.warn(
`TaskWithTimeout: ${task.id} has been running for ${duration}ms`
);
}
}
}
export default function createTaskWithTimeout<T, Args extends Array<unknown>>( export default function createTaskWithTimeout<T, Args extends Array<unknown>>(
task: (...args: Args) => Promise<T>, task: (...args: Args) => Promise<T>,
id: string, id: string,
options: { timeout?: number } = {} options: { timeout?: number } = {}
): (...args: Args) => Promise<T> { ): (...args: Args) => Promise<T> {
const timeout = options.timeout || 30 * durations.MINUTE; const timeout = options.timeout || 30 * MINUTE;
const timeoutError = new Error(`${id || ''} task did not complete in time.`); const timeoutError = new Error(`${id || ''} task did not complete in time.`);
@ -54,6 +72,7 @@ export default function createTaskWithTimeout<T, Args extends Array<unknown>>(
return; return;
} }
entry.startedAt = Date.now();
timer = setTimeout(() => { timer = setTimeout(() => {
if (complete) { if (complete) {
return; return;
@ -72,6 +91,8 @@ export default function createTaskWithTimeout<T, Args extends Array<unknown>>(
}; };
const entry: TaskType = { const entry: TaskType = {
id,
startedAt: undefined,
suspend: stopTimer, suspend: stopTimer,
resume: startTimer, resume: startTimer,
}; };