Простий QR-квиток у кіно ховає за собою серйозну інженерну проблему

Uncategorized

Стоячи у черзі до кінотеатру, програміст Лайтсоул помітив деталь, яка більшості людей і на думку не спала б: контролер сканує QR-код, і людина проходить. Просто. Але у своєму технічному матеріалі він пояснює, що за цим непомітним жестом ховається цілий клубок проблем з теорії розподілених систем: що стане, якщо мережа впаде? Що, якщо хтось зробить скріншот квитка? Що, якщо відповідь від сервера загубиться на шляху назад? Правильна відповідь на ці питання — не банальна, і вона визначає, чи потрапить оплатившая людина всередину.

Що відомо коротко

  • Золоте правило системи: один квиток — один вхід. Виконати його надійно в умовах реального світу набагато складніше, ніж здається.
  • Перша проблема — офлайн-валідація: можна перевірити підпис QR-коду без інтернету, але тоді кілька входів можуть пропустити один і той самий квиток.
  • Друга проблема — «Задача двох генералів»: сервер позначив квиток як використаний, але відповідь загубилася, і контролер бачить помилку.
  • Третя проблема — втрата контексту ідемпотентності: після того як людина відійшла з черги, сканування з новим ключем виглядає для системи як спроба повторного використання чужого квитка.
  • Автор доходить висновку, що з QR-кодами проблему принципово не вирішити — потрібна зміна протоколу.

Що таке розподілені системи і чому вони важливі

Розподілені системи — це програмні архітектури, де кілька незалежних компонентів (серверів, пристроїв, сервісів) взаємодіють між собою через мережу, щоб виглядати для користувача як єдине ціле. Банківський переказ, онлайн-бронювання, мобільний додаток для квитків — усе це розподілені системи.

Їхня складність у тому, що мережа є ненадійним середовищем: пакети даних губляться, відповіді затримуються, сервери перезавантажуються. Задача інженера — зробити систему коректною навіть у цих умовах. Перевірка квитка в кіно виглядає тривіальною, але в ній сконцентровані всі класичні виклики цього напряму інженерії.

Деталі: чотири шари проблем

Шар 1: Офлайн-валідація. Найпростіший підхід — зашити в QR-код цифровий підпис (наприклад, ECC або RSA). Контролер може перевірити підпис, навіть без інтернету — достатньо знати публічний ключ системи. Мінус очевидний: якщо на виході кілька контролерів і вони не об’єднані в мережу, той самий QR-код можна провести крізь кожен вхід. Офлайн-підхід працює лише з одним єдиним входом.

Шар 2: Спільний стан і задача двох генералів. Щоб усі контролери знали, які квитки вже використані, потрібна єдина база даних. Атомарний запит у PostgreSQL вирішує проблему одночасного доступу:

sql

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

Якщо запит повертає рядок — людина проходить. Якщо ні — квиток вже використаний або не існує. Але тут з’являється класична «Задача двох генералів»: уявіть, що сигнал у підвалі кінотеатру слабкий. Телефон відправляє запит — сервер отримує, позначає квиток як використаний і надсилає відповідь «ОК». Але відповідь губиться на шляху назад. Контролер бачить таймаут, намагається ще раз. Сервер відповідає: «Квиток вже використаний». Людина з оплаченим квитком залишається надворі.

Шар 3: Ідемпотентність. Щоб повторні спроби сканування не призводили до помилок, систему роблять ідемпотентною. При відкритті вікна сканування конкретного квитка застосунок генерує унікальний ключ (UUID). Скільки б разів той самий контролер не надсилав той самий запит у межах тієї ж сесії сканування — сервер обробить його рівно один раз.

Це вирішується через унікальний індекс у базі:

sql

INSERT INTO processed_requests (ticket_id, idempotency_key)
VALUES (:ticket_id, :idempotency_key)
ON CONFLICT (ticket_id, idempotency_key) DO NOTHING RETURNING *;

Якщо запис вже є — повертається попередній результат. Якщо нема — обробляємо вперше.

Шар 4: Втрата контексту. Ось де система ламається остаточно. Уявіть: мережа зависла, контролер відправив людину вбік черги чекати. Той закрив вікно сканування. Повертається — і контролер сканує знову. Але новий запит має вже інший idempotency key. З точки зору сервера: квиток уже позначений як використаний, новий ключ не збігається — схоже на шахрайство. Контролер бачить помилку «Квиток уже використаний». Людина безпідставно відмовлена.

Що показали ці спостереження

Автор описує патч-рішення, яке іноді застосовують: повертати разом з помилкою мітку часу останнього сканування. «Квиток використаний 2 хвилини тому». Контролер сам вирішує, пускати людину чи ні. Але це переносить технічну проблему на людський фактор — і відкриває діру в безпеці: два чоловіки зі скріншотом одного QR-коду можуть просто зайти один за одним.

Справжнє рішення полягає в усуненні блокування голови черги (head-of-line blocking): контролер не має переходити до наступного квитка, поки не отримає однозначну відповідь (успіх або помилка) за поточним. Саме для таких сценаріїв Google розробив протокол QUIC — надійний транспортний протокол, що краще справляється з нестабільним з’єднанням і втраченими пакетами.

Але фундаментально автор вважає, що статичні QR-коди не можуть вирішити цю задачу повністю. Потрібна зміна протоколу — наприклад, динамічні токени або схема «запит-відповідь» з одноразовими підтвердженнями.

Про те, як квантові комп’ютери вирішують задачі, що недоступні звичайним машинам, — у нашому окремому матеріалі.

Чому це важливо поза кінотеатром

Ті самі проблеми, що описані для кінотеатру, стоять перед будь-якою системою з обмеженим доступом: посадка в літак, вхід на концерт, контроль на виборчій дільниці. Скрізь є QR-код, мережа, база даних і натовп нетерплячих людей. Різниця лише в ціні помилки.

Більш глобально — ця стаття є яскравою ілюстрацією того, що найскладніші проблеми в програмуванні часто не пов’язані з математикою або алгоритмами. Вони виникають на межі між програмним забезпеченням і реальним фізичним світом: ненадійними мережами, стомленими контролерами і черговими, яким кортить потрапити на сеанс.

Про масштаб інформаційних систем сучасного інтернету, де такі задачі вирішуються мільярди разів на добу, — у нашому відеоматеріалі.

Цікаві факти

  1. «Задача двох генералів» — одна з фундаментальних проблем теорії розподілених систем, відомих з 1975 року. Вона доводить, що в умовах ненадійного зв’язку неможливо досягти абсолютної гарантії консенсусу. Це теоретичне обмеження, а не технічний недолік конкретного продукту.
  2. Протокол QUIC, розроблений Google і стандартизований IETF у 2021 році, вирішує багато проблем надійності передачі даних на транспортному рівні. Він лежить в основі HTTP/3 — сучасної версії протоколу, за яким передається переважна більшість веб-трафіку.
  3. Властивість ідемпотентності в програмуванні означає, що повторне застосування операції дає той самий результат, що й перше. Наприклад, клавіша «стерти» — ідемпотентна (натискай скільки завгодно, якщо текст вже стертий), а «додати рядок» — ні. Правильне проектування ідемпотентних API — базова вимога до будь-якого надійного веб-сервісу.
  4. За даними Statista, глобальний ринок електронних квитків (e-ticketing) у 2024 році перевищив $90 млрд і продовжує зростати. Всі ці мільярди транзакцій стикаються з тими самими технічними проблемами, що й система кінотеатру, — але в набагато більшому масштабі і з набагато вищими ставками.

FAQ

Чому не можна просто зберігати всі використані квитки офлайн і синхронізуватися пізніше? Можна, але тоді між синхронізаціями один квиток може бути використаний двічі на різних пристроях. Це підходить там, де ризик низький (наприклад, доступ до парковки), але не там, де квитки мають реальну грошову цінність і відсутність дублювання критична.

Чому динамічні токени краще за статичні QR-коди? Статичний QR-код — незмінний і може бути скопійований. Динамічний токен змінюється при кожному запиті або після кожного сканування, і не може бути повторно використаний. Але він потребує постійного зв’язку між пристроєм і сервером — що й створює всі описані проблеми з мережею.

Чи є реальні системи, що вирішили цю задачу? Частково — так. Системи посадки в літаки (наприклад, у великих авіакомпаній) використовують комбінацію локального кешу, синхронізації станів і детектора конфліктів. Але навіть вони іноді дають збій при масовій посадці або збоях мережі — і тоді потрібне ручне втручання співробітників.


WOW-факт. Проблема, яку описує автор — «Задача двох генералів» — математично доведена як нерозв’язна в умовах ненадійного каналу зв’язку. Це означає, що не існує і ніколи не буде існувати алгоритму, що гарантує 100% надійну синхронізацію між двома вузлами через ненадійну мережу. Кожна реальна система — авіакомпанії, банки, квиткові платформи — живе з цим фундаментальним обмеженням і будує обхідні шляхи, а не справжнє рішення.

#Простий #QRквиток #кіно #ховає #за #собою #серйозну #інженерну #проблему

Source link

Оцініть статтю