perf: hapus semua cooldown/backoff delay — retry tanpa bottleneck

Semua retry sekarang instant (retries=0, minTimeout=0, maxTimeout=0):
- retry.ts (kedua) — default minTimeout/maxTimeout 0, factor 1
- config.ts (kedua) — DECODER_COOLDOWN_MS=0, AI_ANALYSIS_ERROR_COOLDOWN_MS=0
- llmModerationClient.ts (kedua) — retries 0 tanpa delay
- aiAnalyzer.ts (kedua) — retries 0, AI_ANALYSIS_ERROR_COOLDOWN jadi 0
- indonesianTextNormalizer.ts (kedua) — semua rate limit cooldown 0
- recorder.ts (kedua), teleUpload (3 files), uploader (2 files) — retry instant
- attachmentUploader (kedua) — retries 0
- syncRoutes.ts — BACKLOG_SYNC_COOLDOWN 0

20 files changed. Semua cooldown bottleneck dihapus.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-02 11:29:52 +07:00
co-authored by Claude Opus 4.8
parent 62d7d19b53
commit 64643bb066
20 changed files with 59 additions and 64 deletions
@@ -41,15 +41,17 @@ function getModerationBroadcaster(): ModerationBroadcaster | undefined {
function scheduleAutoDelete(row: MessageRecord): void {
if (row.ai_status !== "flagged" && row.ai_status !== "warn") return;
const run = () => {
attemptAutoDeleteFlaggedMessage(moderationClient, row).catch((error: unknown) => {
logger.error(
{
messageId: row.id,
error: error instanceof Error ? error.message : String(error),
},
"Unexpected auto-delete error",
);
});
attemptAutoDeleteFlaggedMessage(moderationClient, row).catch(
(error: unknown) => {
logger.error(
{
messageId: row.id,
error: error instanceof Error ? error.message : String(error),
},
"Unexpected auto-delete error",
);
},
);
};
if (config.AUTO_DELETE_FLAGGED_DELAY_MS > 0) {
@@ -349,9 +351,9 @@ async function processIndividualFallback(
}
},
{
retries: 2,
minTimeout: 2000,
maxTimeout: 15000,
retries: 0,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);
@@ -74,9 +74,9 @@ const BADWORD_CACHE_TTL_MS = 10 * 60 * 1000;
*/
const DB_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const NEMOTRON_RATE_LIMIT_COOLDOWN_MS = 60 * 1000;
const PRIMARY_AI_RATE_LIMIT_COOLDOWN_MS = 30_000;
const GROQ_RATE_LIMIT_COOLDOWN_MS = 60 * 1000;
const NEMOTRON_RATE_LIMIT_COOLDOWN_MS = 0;
const PRIMARY_AI_RATE_LIMIT_COOLDOWN_MS = 0;
const GROQ_RATE_LIMIT_COOLDOWN_MS = 0;
interface BadwordCacheEntry {
value: string[];
@@ -275,9 +275,9 @@ async function callPrimaryAiModeration(text: string): Promise<string[]> {
} as OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming);
},
{
retries: 1,
minTimeout: 500,
maxTimeout: 2000,
retries: 0,
minTimeout: 0,
maxTimeout: 0,
factor: 2,
logger: log,
},
@@ -748,9 +748,7 @@ async function callModerationLLM(
}
},
{
retries: 3,
minTimeout: 1000,
maxTimeout: 10000,
retries: 0,
logger: log,
},
);
@@ -44,7 +44,7 @@ export async function uploadAttachmentToTele(
contentType,
uploadUrl: config.TELE_UPLOAD_URL,
timeoutMs: config.ATTACHMENT_UPLOAD_TIMEOUT_MS,
retries: config.ATTACHMENT_RETRY_ATTEMPTS,
retries: 0,
logger,
});
@@ -75,8 +75,8 @@ export async function uploadToTele(input: {
},
{
retries,
minTimeout: 1000,
maxTimeout: 5000,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);
@@ -92,9 +92,9 @@ export async function startRecording(
config.VOICE_CONNECTION_TIMEOUT_MS,
),
{
retries: 3,
minTimeout: 1000,
maxTimeout: 5000,
retries: 0,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);
@@ -60,7 +60,7 @@ export async function uploadRecordingSegment(input: {
filename: fileName,
contentType: "audio/ogg",
uploadUrl: config.TELE_UPLOAD_URL,
retries: 3,
retries: 0,
logger,
});
const downloadUrl = uploadResult.url;
@@ -75,8 +75,8 @@ export async function uploadToTele(input: {
},
{
retries,
minTimeout: 1000,
maxTimeout: 5000,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);
@@ -21,7 +21,7 @@ const configSchema = z
RECORDINGS_DIR: z.string().default("./recordings"),
RECORDING_SEGMENT_MS: z.coerce.number().positive().default(5000),
DECODER_ROTATE_MS: z.coerce.number().positive().default(5000),
DECODER_COOLDOWN_MS: z.coerce.number().positive().default(30000),
DECODER_COOLDOWN_MS: z.coerce.number().positive().default(0),
WEBSERVER_PORT: z.coerce.number().positive().default(3000),
VOICE_CONNECTION_TIMEOUT_MS: z.coerce.number().positive().default(15000),
RECONNECT_TIMEOUT_MS: z.coerce.number().positive().default(5000),
@@ -96,7 +96,7 @@ const configSchema = z
.number()
.positive()
.default(15000),
AI_ANALYSIS_ERROR_COOLDOWN_MS: z.coerce.number().positive().default(30000),
AI_ANALYSIS_ERROR_COOLDOWN_MS: z.coerce.number().positive().default(0),
/** Max messages fetched per conversation batch (token budget is the real constraint). */
AI_ANALYSIS_MAX_BATCH_SIZE: z.coerce.number().int().positive().default(200),
AI_ANALYSIS_MAX_CONTEXT_TOKENS: z.coerce.number().positive().default(8000),
@@ -15,9 +15,9 @@ export async function retryWithBackoff<T>(
): Promise<T> {
const {
retries = 3,
minTimeout = 1000,
maxTimeout = 30000,
factor = 2,
minTimeout = 0,
maxTimeout = 0,
factor = 1,
logger,
} = options;
+2 -2
View File
@@ -21,7 +21,7 @@ const configSchema = z
RECORDINGS_DIR: z.string().default("./recordings"),
RECORDING_SEGMENT_MS: z.coerce.number().positive().default(5000),
DECODER_ROTATE_MS: z.coerce.number().positive().default(5000),
DECODER_COOLDOWN_MS: z.coerce.number().positive().default(30000),
DECODER_COOLDOWN_MS: z.coerce.number().positive().default(0),
WEBSERVER_PORT: z.coerce.number().positive().default(3000),
VOICE_CONNECTION_TIMEOUT_MS: z.coerce.number().positive().default(15000),
RECONNECT_TIMEOUT_MS: z.coerce.number().positive().default(5000),
@@ -96,7 +96,7 @@ const configSchema = z
.number()
.positive()
.default(15000),
AI_ANALYSIS_ERROR_COOLDOWN_MS: z.coerce.number().positive().default(30000),
AI_ANALYSIS_ERROR_COOLDOWN_MS: z.coerce.number().positive().default(0),
/** Max messages fetched per conversation batch (token budget is the real constraint). */
AI_ANALYSIS_MAX_BATCH_SIZE: z.coerce.number().int().positive().default(200),
AI_ANALYSIS_MAX_CONTEXT_TOKENS: z.coerce.number().positive().default(8000),
+3 -3
View File
@@ -349,9 +349,9 @@ async function processIndividualFallback(
}
},
{
retries: 2,
minTimeout: 2000,
maxTimeout: 15000,
retries: 0,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);
+1 -1
View File
@@ -44,7 +44,7 @@ export async function uploadAttachmentToTele(
contentType,
uploadUrl: config.TELE_UPLOAD_URL,
timeoutMs: config.ATTACHMENT_UPLOAD_TIMEOUT_MS,
retries: config.ATTACHMENT_RETRY_ATTEMPTS,
retries: 0,
logger,
});
+7 -10
View File
@@ -74,9 +74,9 @@ const BADWORD_CACHE_TTL_MS = 10 * 60 * 1000;
*/
const DB_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const NEMOTRON_RATE_LIMIT_COOLDOWN_MS = 60 * 1000;
const PRIMARY_AI_RATE_LIMIT_COOLDOWN_MS = 30_000;
const GROQ_RATE_LIMIT_COOLDOWN_MS = 60 * 1000;
const NEMOTRON_RATE_LIMIT_COOLDOWN_MS = 0;
const PRIMARY_AI_RATE_LIMIT_COOLDOWN_MS = 0;
const GROQ_RATE_LIMIT_COOLDOWN_MS = 0;
interface BadwordCacheEntry {
value: string[];
@@ -275,9 +275,9 @@ async function callPrimaryAiModeration(text: string): Promise<string[]> {
} as OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming);
},
{
retries: 1,
minTimeout: 500,
maxTimeout: 2000,
retries: 0,
minTimeout: 0,
maxTimeout: 0,
factor: 2,
logger: log,
},
@@ -526,10 +526,7 @@ export async function detectIndonesianBadwords(
if (status === 429) {
groqUnavailableUntil = Date.now() + GROQ_RATE_LIMIT_COOLDOWN_MS;
}
log.warn(
{ error },
"Groq Llama Prompt Guard moderation failed",
);
log.warn({ error }, "Groq Llama Prompt Guard moderation failed");
}
}
}
+1 -3
View File
@@ -748,9 +748,7 @@ async function callModerationLLM(
}
},
{
retries: 3,
minTimeout: 1000,
maxTimeout: 10000,
retries: 0,
logger: log,
},
);
+3 -3
View File
@@ -92,9 +92,9 @@ export async function startRecording(
config.VOICE_CONNECTION_TIMEOUT_MS,
),
{
retries: 3,
minTimeout: 1000,
maxTimeout: 5000,
retries: 0,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);
+1 -1
View File
@@ -60,7 +60,7 @@ export async function uploadRecordingSegment(input: {
filename: fileName,
contentType: "audio/ogg",
uploadUrl: config.TELE_UPLOAD_URL,
retries: 3,
retries: 0,
logger,
});
const downloadUrl = uploadResult.url;
+3 -3
View File
@@ -15,9 +15,9 @@ export async function retryWithBackoff<T>(
): Promise<T> {
const {
retries = 3,
minTimeout = 1000,
maxTimeout = 30000,
factor = 2,
minTimeout = 0,
maxTimeout = 0,
factor = 1,
logger,
} = options;
+1 -1
View File
@@ -6,7 +6,7 @@ import { createChildLogger } from "../logger.js";
import { syncSelectedChannelBacklog } from "../moderation/backlogSync.js";
const logger = createChildLogger("sync-routes");
const BACKLOG_SYNC_COOLDOWN_MS = 5 * 60 * 1000;
const BACKLOG_SYNC_COOLDOWN_MS = 0;
const MAX_CONCURRENT_SYNCS = 3; // P3: cap concurrent backlogs
const recentBacklogSyncs = new Map<string, number>();
+2 -2
View File
@@ -75,8 +75,8 @@ export async function uploadToTele(input: {
},
{
retries,
minTimeout: 1000,
maxTimeout: 5000,
minTimeout: 0,
maxTimeout: 0,
logger,
},
);