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
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>
57 lines
2.2 KiB
TypeScript
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);
|
|
}
|
|
},
|
|
};
|
|
}
|