chore(server): promise safety net and sweep tooling for the async migration

This commit is contained in:
jubnl
2026-09-21 19:01:38 +02:00
parent ef184ec7c2
commit 27d044f9e8
18 changed files with 711 additions and 23 deletions
+4 -1
View File
@@ -91,7 +91,10 @@ let admin1Building: Promise<Map<string, string>> | null = null;
function getAdmin1Store(): Promise<Map<string, string>> {
if (admin1Store) return Promise.resolve(admin1Store);
if (!admin1Building) {
// `=== null`, not `!admin1Building`: a Promise in a boolean condition is what
// no-misused-promises/checksConditionals exists to catch, and the distinction
// matters once every DB-touching method returns one.
if (admin1Building === null) {
admin1Building = buildAdmin1Store().then((s) => {
admin1Store = s;
admin1Building = null;
+3 -1
View File
@@ -292,7 +292,9 @@ export async function createBackup(storage: StorageService, prefix: 'backup' | '
}
}
archive.finalize();
// finalize() is async: without this catch a failure that never reached the
// 'error' listener above would hang this promise and reject unobserved.
archive.finalize().catch(reject);
});
// The commit — and, under a mirror backend, the replica fan-out point.
@@ -75,7 +75,9 @@ export class ExchangeRatesService {
// Coalesce concurrent fetches for the same base.
let p = inflight.get(key);
if (!p) {
// `=== undefined`, not `!p`: a Promise is always truthy, so the truthiness
// test reads as a live check but never is one (no-misused-promises).
if (p === undefined) {
p = fetchRates(key)
.then(rates => {
if (rates) cache.set(key, { rates, ts: Date.now() });
@@ -91,7 +91,10 @@ export class TrekPhotoCacheService {
setInFlight(key: string, promise: Promise<Buffer | null>): void {
inFlight.set(key, promise);
promise.finally(() => inFlight.delete(key));
// Book-keeping only: the caller awaits `promise` itself and observes its
// rejection, so the derived chain swallows the same one rather than raising
// a second, unhandled rejection.
void promise.finally(() => inFlight.delete(key)).catch(() => undefined);
}
async sweepExpired(): Promise<void> {
+3 -1
View File
@@ -64,7 +64,9 @@ export function scheduleJobs(
export function stopJobs(tasks: CronJob[] | undefined): void {
for (const t of tasks ?? []) {
try {
t.stop();
// cron's stop() is async; the rejection is swallowed for the same reason
// the synchronous throw below is — teardown must never throw.
void t.stop().catch(() => undefined);
} catch {
/* ignore — teardown must never throw */
}