Files
Luncher/server/src/leaderLease.ts
T
batmanisko 491ec25b52
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 21s
CI / Build server (push) Successful in 24s
CI / Build client (push) Successful in 36s
CI / Playwright E2E tests (push) Successful in 1m25s
CI / Build and push Docker image (push) Successful in 43s
CI / Notify (push) Successful in 2s
feat: automatické sledování doručení objednávky přes Bolt Food
Zakladatel skupiny může na stránce objednání vložit sdílecí odkaz
Bolt Food. Server pak každou minutu dotazuje veřejné Bolt API
a automaticky aktualizuje čas doručení skupiny (deliveryAt).
Sledování se samo ukončí po doručení, zrušení objednávky nebo
opakovaných chybách. Leader lease vytažena do znovupoužitelného
modulu leaderLease.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 12:22:07 +02:00

57 lines
2.2 KiB
TypeScript

import { getRedisClient } from './storage/redis';
const POD_ID = process.env.POD_ID ?? `local-${process.pid}`;
export interface LeaderLease {
tryAcquireOrRenew(): Promise<boolean>;
release(): Promise<void>;
}
/**
* Vytvoří leader lease pro daný klíč — zajišťuje, že periodickou úlohu
* spouští v multi-replica nasazení pouze jedna instance.
* Při non-Redis storage vždy vrací true (single-process, leader election není potřeba).
*/
export function createLeaderLease(leaseKey: string, ttlSeconds = 90): LeaderLease {
return {
async tryAcquireOrRenew(): Promise<boolean> {
if (process.env.STORAGE?.toLowerCase() !== 'redis') return true;
try {
const c = getRedisClient();
if (!c) return true;
// Zkusíme získat lease atomicky (SET NX EX)
const acquired = await c.set(leaseKey, POD_ID, { NX: true, EX: ttlSeconds });
if (acquired !== null) return true; // lease čerstvě získána
// Pokud jsme ji nedostali, ověříme zda ji držíme my
const currentHolder = await c.get(leaseKey);
if (currentHolder === POD_ID) {
// Naše lease — obnovíme TTL
await c.set(leaseKey, POD_ID, { EX: ttlSeconds });
return true;
}
return false; // lease drží jiná instance
} catch (e) {
console.error(`Leader lease (${leaseKey}): chyba při získávání, úloha bude spuštěna`, e);
return true; // při chybě raději spustíme, než vynecháme
}
},
async release(): Promise<void> {
if (process.env.STORAGE?.toLowerCase() !== 'redis') return;
try {
const c = getRedisClient();
if (!c) return;
const currentHolder = await c.get(leaseKey);
if (currentHolder === POD_ID) {
await c.del(leaseKey);
console.log(`Leader lease (${leaseKey}): uvolněna`);
}
} catch (e) {
console.error(`Leader lease (${leaseKey}): chyba při uvolňování`, e);
}
},
};
}