From 19e02501af8752c936bc2c4426b5a1ca483134f6 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sun, 23 Jul 2023 10:07:41 +0200 Subject: [PATCH] [Perf] API: Batch updates into single `postMessage` --- src/api/gramjs/worker/provider.ts | 22 ++++++++++++++++++---- src/api/gramjs/worker/types.ts | 4 ++-- src/api/gramjs/worker/worker.ts | 23 +++++++++++++++++------ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/api/gramjs/worker/provider.ts b/src/api/gramjs/worker/provider.ts index 0ec2ee11e..23ef43385 100644 --- a/src/api/gramjs/worker/provider.ts +++ b/src/api/gramjs/worker/provider.ts @@ -1,7 +1,7 @@ import type { Api } from '../../../lib/gramjs'; import type { ApiInitialArgs, ApiOnProgress, OnApiUpdate } from '../../types'; -import type { Methods, MethodArgs, MethodResponse } from '../methods/types'; -import type { WorkerMessageEvent, OriginRequest, ThenArg } from './types'; +import type { MethodArgs, MethodResponse, Methods } from '../methods/types'; +import type { OriginRequest, ThenArg, WorkerMessageEvent } from './types'; import type { LocalDb } from '../localDb'; import type { TypedBroadcastChannel } from '../../../util/multitab'; @@ -254,8 +254,22 @@ export function cancelApiProgressMaster(messageId: string) { function subscribeToWorker(onUpdate: OnApiUpdate) { worker?.addEventListener('message', ({ data }: WorkerMessageEvent) => { if (!data) return; - if (data.type === 'update') { - onUpdate(data.update); + if (data.type === 'updates') { + // eslint-disable-next-line @typescript-eslint/naming-convention + let DEBUG_startAt: number | undefined; + if (DEBUG) { + DEBUG_startAt = performance.now(); + } + + data.updates.forEach(onUpdate); + + if (DEBUG) { + const duration = performance.now() - DEBUG_startAt!; + if (duration > 5) { + // eslint-disable-next-line no-console + console.warn(`[API] Slow updates processing: ${data.updates.length} updates in ${duration} ms`); + } + } } else if (data.type === 'methodResponse') { handleMethodResponse(data); } else if (data.type === 'methodCallback') { diff --git a/src/api/gramjs/worker/types.ts b/src/api/gramjs/worker/types.ts index cf2fbea80..8b54f0028 100644 --- a/src/api/gramjs/worker/types.ts +++ b/src/api/gramjs/worker/types.ts @@ -6,8 +6,8 @@ import type { DebugLevel } from '../../../util/debugConsole'; export type ThenArg = T extends Promise ? U : T; export type WorkerMessageData = { - type: 'update'; - update: ApiUpdate; + type: 'updates'; + updates: ApiUpdate[]; } | { type: 'methodResponse'; messageId: string; diff --git a/src/api/gramjs/worker/worker.ts b/src/api/gramjs/worker/worker.ts index 1f8d3b4f8..e87abfce9 100644 --- a/src/api/gramjs/worker/worker.ts +++ b/src/api/gramjs/worker/worker.ts @@ -4,10 +4,11 @@ import type { ApiOnProgress, ApiUpdate } from '../../types'; import type { OriginMessageEvent, WorkerMessageData } from './types'; import { DEBUG } from '../../../config'; -import { initApi, callApi, cancelApiProgress } from '../provider'; +import { callApi, cancelApiProgress, initApi } from '../provider'; import { log } from '../helpers'; import type { DebugLevel } from '../../../util/debugConsole'; import { DEBUG_LEVELS } from '../../../util/debugConsole'; +import { throttleWithTickEnd } from '../../../util/schedulers'; declare const self: WorkerGlobalScope; @@ -154,15 +155,25 @@ function handleErrors() { }); } -function onUpdate(update: ApiUpdate) { - sendToOrigin({ - type: 'update', - update, - }); +let pendingUpdates: ApiUpdate[] = []; +const sendUpdatesOnTickEnd = throttleWithTickEnd(() => { + const currentUpdates = pendingUpdates; + pendingUpdates = []; + + sendToOrigin({ + type: 'updates', + updates: currentUpdates, + }); +}); + +function onUpdate(update: ApiUpdate) { if (DEBUG && update['@type'] !== 'updateUserStatus' && update['@type'] !== 'updateServerTimeOffset') { log('UPDATE', update['@type'], update); } + + pendingUpdates.push(update); + sendUpdatesOnTickEnd(); } export function sendToOrigin(data: WorkerMessageData, arrayBuffer?: ArrayBuffer) {