fix(message): bound batched message response requests by size (#2252)

This commit is contained in:
Hampus
2026-08-31 16:13:36 +02:00
committed by GitHub
parent 5036ac3efa
commit 8c85cce75c
2 changed files with 169 additions and 43 deletions
@@ -15,6 +15,7 @@ import {isJsonRecord, parseJsonRecord, parseJsonWithGuard} from '../../../utils/
const MESSAGE_RESPONSE_SERVICE_SUBJECT = 'svc.messages';
const MESSAGE_RESPONSE_SERVICE_TIMEOUT_MS = 6000;
export const MESSAGE_BUILD_BATCH_MAX_BYTES = 262_144;
let messageResponseDataService: MessageResponseDataService | undefined;
let injectedMessageResponseDataService: MessageResponseDataService | undefined;
@@ -230,23 +231,27 @@ export class MessageResponseDataService {
includeReactions?: boolean;
}): Promise<Array<MessageResponse>> {
if (params.messages.length === 0) return [];
const response = await this.request({
op: 'BuildResponses',
messages: params.messages.map(serializeMessageForService),
viewer_user_id: params.userId.toString(),
source_guild_id: params.access.sourceGuildId?.toString(),
message_history_cutoff_ms: params.access.messageHistoryCutoff
? new Date(params.access.messageHistoryCutoff).getTime()
: null,
can_read_message_history: params.access.canReadMessageHistory,
media_endpoint: Config.endpoints.media,
media_proxy_secret_key: Config.mediaProxy.secretKey,
include_reactions: params.includeReactions ?? true,
});
if (typeof response === 'object' && 'FoundApiMany' in response) {
return response.FoundApiMany;
const responses: Array<MessageResponse> = [];
for (const batch of chunkMessagesForService(params.messages)) {
const response = await this.request({
op: 'BuildResponses',
messages: batch,
viewer_user_id: params.userId.toString(),
source_guild_id: params.access.sourceGuildId?.toString(),
message_history_cutoff_ms: params.access.messageHistoryCutoff
? new Date(params.access.messageHistoryCutoff).getTime()
: null,
can_read_message_history: params.access.canReadMessageHistory,
media_endpoint: Config.endpoints.media,
media_proxy_secret_key: Config.mediaProxy.secretKey,
include_reactions: params.includeReactions ?? true,
});
if (typeof response !== 'object' || !('FoundApiMany' in response)) {
throw new Error(`[message-response-service] unexpected BuildResponses response: ${JSON.stringify(response)}`);
}
responses.push(...response.FoundApiMany);
}
throw new Error(`[message-response-service] unexpected BuildResponses response: ${JSON.stringify(response)}`);
return responses;
}
async buildMessagesForChannels(params: {
@@ -340,6 +345,27 @@ function serializeMessageForService(message: Message): Record<string, unknown> {
return row;
}
function chunkMessagesForService(messages: Array<Message>): Array<Array<Record<string, unknown>>> {
const batches: Array<Array<Record<string, unknown>>> = [];
let batch: Array<Record<string, unknown>> = [];
let batchBytes = 0;
for (const message of messages) {
const serialized = serializeMessageForService(message);
const bytes = Buffer.byteLength(JSON.stringify(serialized));
if (batch.length > 0 && batchBytes + bytes > MESSAGE_BUILD_BATCH_MAX_BYTES) {
batches.push(batch);
batch = [];
batchBytes = 0;
}
batch.push(serialized);
batchBytes += bytes;
}
if (batch.length > 0) {
batches.push(batch);
}
return batches;
}
function serializeValue(value: unknown): unknown {
if (value == null) return value;
if (typeof value === 'bigint') return value.toString();