Zpět na domů

Design validace QR-lístků pro kina

Článek rozebírá design systému validace QR-lístků kin. Od offline podpisu po idempotentní API s transakcemi PostgreSQL. Diskutují se problémy timeoutů, HoL blokování a kompromisy.

Jak navrhnout spolehlivou validaci lístků v kině
Advertisement 728x90

Systémový návrh validace QR vstupenek: od offline režimu k idempotentnosti

Kontrolor naskenuje QR kód vstupenky, systém ji ověří v databázi a označí jako použitou. Hlavní požadavek: jedna vstupenka – jeden vstup. Na první pohled jednoduchý úkol se komplikuje síťovými výpadky, frontami a více kontrolory. Prozkoumáme vývoj řešení od offline validace k spolehlivému serverovému zpracování.

Offline režim využívá digitální podpis (ECC nebo RSA). QR obsahuje data vstupenky a podpis, kontrolor ověří veřejným klíčem bez sítě. Problém: chybí synchronizace mezi kontrolory. Snímek obrazovky s vstupenkou projde každou bránou nezávisle.

Serverový stav a atomické aktualizace

Jednotné úložiště řeší problém synchronizace. Každé skenování je požadavek na server s atomickou aktualizací stavu.

Google AdInline article slot

Příklad v PostgreSQL:

UPDATE tickets 
SET status = 'wasted', used_at = NOW()
WHERE id = :ticket_id AND status = 'active'
RETURNING *;

Pokud je vrácen řádek – vstupenka je zneplatněna, vstup povolen. Žádný řádek – vstupenka již byla použita nebo neexistuje.

Klasický problém: ztráta odpovědi od serveru. Kontrolor v oblasti slabého signálu odešle požadavek, server aktualizuje databázi, ale odpověď nedorazí. Časový limit, opětovné skenování – server odmítne, klient neprojde navzdory zaplacené vstupence.

Google AdInline article slot

Idempotentnost na úrovni API

Řešení – idempotentní požadavky s unikátním klíčem (UUID), generovaným při otevření sezení skenování. Klíč zaznamenává pokus, umožňuje opakování bez duplikace.

Logika v transakci (read committed):

begin ;

-- 1. Zaznamenáme pokus
INSERT INTO processed_requests (ticket_id, idempotency_key)
VALUES (:ticket_id, :idempotency_key)
ON CONFLICT (ticket_id, idempotency_key) 
DO NOTHING RETURNING *;

-- 2. Pokud rows_affected == 0: rollback, vrátit SUCCESS

-- 3. Zneplatníme vstupenku
UPDATE tickets 
SET status = 'wasted', used_at = NOW()
WHERE id = :ticket_id AND status = 'active'
RETURNING *;

-- 4. Pokud rows_affected == 0: rollback, vrátit ERROR

commit;
-- 5. Vrátit SUCCESS

Transakce zajišťuje atomičnost: buď je klíč + stav aktualizován, nebo nic. Unikátní index na (ticket_id, idempotency_key) zabraňuje závodům – druhý požadavek čeká nebo jde do DO NOTHING.

Google AdInline article slot

Opakovaný pokus ve stejném sezení vrátí úspěch, i když první odpověď byla ztracena.

Head-of-Line Blocking a ztráta kontextu

Skutečný problém: kontrolor pod tlakem fronty uzavře sezení bez odpovědi. Nové skenování vygeneruje nový idempotency_key. Server vidí použitou vstupenku s cizím klíčem – odmítne.

Kontrolor nerozliší legitimního klienta od podvodníka se snímkem obrazovky. HoL-blokování vyžaduje konečnou odpověď před další vstupenkou.

Kompromis: vracet časové razítko posledního použití. Kontrolor vyhodnotí: „před 2 minutami – stejná osoba“. Riziko: skupina se společným QR projde postupně.

Klíčové výzvy:

  • Síťové časové limity bez ztráty stavu
  • Synchronizace více kontrolorů
  • Lidský faktor ve frontách
  • Rovnováha spolehlivosti a UX

Co je důležité

  • Atomický UPDATE s podmínkou status='active' zabraňuje dvojímu zneplatnění
  • Idempotentnost přes UUID + unikátní index zaručuje opakování bez duplikátů
  • Transakce (read committed) zajišťují sekvenční viditelnost změn
  • HoL-blokování je nevyhnutelné pro 100% spolehlivost bez biometrie
  • Kompromis s časovým razítkem přenáší rozhodnutí na operátora

Perspektivy vylepšení

Statické QR zastarávají. Dynamické tokeny nebo challenge-response s jednorázovými OTP řeší problém. Kontrolor požádá o nonce od serveru, klient podepíše – server ověří a atomicky označí.

Biometrie (face ID) nebo NFC eliminují snímky obrazovky, ale vyžadují hardware. QUIC minimalizuje ztrátu paketů, ale neřeší ztrátu kontextu.

Škálování: Redis pro mezipaměť idempotency_keys, sharding podle ticket_id. Monitorování: metriky failed_scans podle časového razítka, A/B testy kompromisů.

— Editorial Team

Advertisement 728x90

Číst dál