fix(worker): prune TDLib's unbounded local file cache after each run
continuous-integration/drone/push Build is failing

TDLib keeps a permanent local copy of every file it downloads or
uploads (via inputFileLocal) with no automatic cleanup. Across the two
worker accounts this had grown to ~270GB, filling the host disk to 91%
and triggering a cascading disk-full failure in the bot's TDLib
session on 2026-08-05. Call optimizeStorage after every ingestion run
to clear it; a 5-minute immunity_delay protects files an in-flight
operation might still reference.
This commit is contained in:
2026-08-10 08:17:51 +02:00
parent 148d688d43
commit 7bc57ec227
2 changed files with 33 additions and 1 deletions
+31
View File
@@ -142,3 +142,34 @@ export async function closeTdlibClient(client: Client): Promise<void> {
log.warn({ err }, "Error closing TDLib client"); log.warn({ err }, "Error closing TDLib client");
} }
} }
/**
* Prune TDLib's local file cache (filesDirectory). TDLib keeps a permanent
* copy of every file it has ever downloaded or uploaded — via inputFileLocal
* uploads in particular — with no automatic cleanup. That cache is redundant
* (the content already lives in the source and destination Telegram chats)
* and grows unbounded, so it's cleared after every ingestion run. A short
* immunity_delay protects files from an in-flight operation that might still
* reference them.
*/
export async function optimizeTdlibStorage(
client: Client,
accountId: string
): Promise<void> {
try {
const result = (await client.invoke({
_: "optimizeStorage",
size: 0,
ttl: 0,
count: 0,
immunity_delay: 300,
return_deleted_file_statistics: true,
})) as { size?: number; count?: number };
log.info(
{ accountId, freedBytes: result.size, freedCount: result.count },
"TDLib local file cache pruned"
);
} catch (err) {
log.warn({ err, accountId }, "TDLib storage optimization failed");
}
}
+2 -1
View File
@@ -42,7 +42,7 @@ import {
isTopicFetchEnabled, isTopicFetchEnabled,
} from "./db/queries.js"; } from "./db/queries.js";
import type { ActivityUpdate } from "./db/queries.js"; import type { ActivityUpdate } from "./db/queries.js";
import { createTdlibClient, closeTdlibClient } from "./tdlib/client.js"; import { createTdlibClient, closeTdlibClient, optimizeTdlibStorage } from "./tdlib/client.js";
import { import {
getAccountChats, getAccountChats,
joinChatByInviteLink, joinChatByInviteLink,
@@ -1203,6 +1203,7 @@ export async function runWorkerForAccount(
accountLog.info({ counters }, "Ingestion run completed"); accountLog.info({ counters }, "Ingestion run completed");
} finally { } finally {
await throttled.flush(); await throttled.flush();
await optimizeTdlibStorage(client, account.id);
await closeTdlibClient(client); await closeTdlibClient(client);
} }
} catch (err) { } catch (err) {