feat: automatické sledování doručení objednávky přes Bolt Food
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
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>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
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);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user