Код Читати оригінал на Dev 2 хв читання 2

Як усунути race conditions при продажу квитків на Go та PostgreSQL

Розробник розкрив архітектурне рішення для усунення race conditions у системі продажу квитків з високим рівнем конкурентності. Використовуючи поєднання Go та PostgreSQL, автор реалізував механізм блокування рядків на рівні бази даних разом із серіалізацією черг у пам'яті. Такий підхід гарантує цілісність даних і запобігає надпродажу квитків навіть під час пікових навантажень, коли тисячі користувачів одночасно намагаються змінити стан одного й того самого запису.

Дві фігури людей у стилі кіберпанку біжать одна на одну до великого неонового замка в цифровому місті з моніторами та серверами.
Дві фігури людей у стилі кіберпанку біжать одна на одну до великого неонового замка в цифровому місті з моніторами та серверами. · Джерело зображення: Dev

За даними Dev, розробник описав свій досвід вирішення критичної проблеми синхронізації даних у системах з обмеженим інвентарем, таких як платформи для продажу квитків або флеш-сейли. Головний виклик тут полягає не лише в масштабуванні серверів, а в забезпеченні абсолютної цілісності даних, коли тисячі запитів намагаються змінити один і той самий рядок бази даних у межах однієї мікросекунди.

Вибір технологічного стека та архітектурні помилки

Для розв'язання задачі було обрано мову Go завдяки її моделі конкурентності. Goroutines дозволяють API мультиплексувати тисячі вхідних HTTP-запитів на невелику кількість потоків, що забезпечує високий RPS без значного споживання оперативної пам'яті. У поєднанні з фреймворком Gin це створює швидкий рівень маршрутизації.

Автор підкреслює, що найпоширеніша помилка — спроба реалізувати перевірку стану на рівні додатка (application-level check). Наприклад, стандартний алгоритм «знайти квиток — перевірити статус — оновити» є вразливим: до моменту виконання команди оновлення інша горутина може вже змінити стан запису. Це призводить до подвійного бронювання та помилок у даних.

Використання блокувань на рівні рядків PostgreSQL

Ключовим рішенням стало делегування цілісності транзакцій інструментам, спеціально для цього створеним — базі даних PostgreSQL. Замість того щоб намагатися запобігти паралельним запитам у коді додатка, розробник впровадив серіалізацію саме в точці вузького горла з використанням конструкції FOR UPDATE.

Механізм роботи включає такі кроки:

  • Початок транзакції в PostgreSQL.
  • Виконання запиту SELECT з додаванням умови FOR UPDATE для конкретного рядка.
  • Накладання ексклюзивного блокування на отриманий рядок до завершення транзакції.
  • Перевірка статусу після отримання блокування та виконання оновлення.
  • Фіксація результату (Commit) або відкат (Rollback).

Якщо 1000 запитів приходять одночасно, PostgreSQL ставить їх у чергу. Перший запит блокує рядок, оновлює його та завершує транзакцію. Наступні запити отримують доступ до рядка вже після оновлення, бачать статус «продано» і коректно відкочуються.

Результати та компроміси

Така архітектура забезпечує 100% цілісність даних і відсутність подвійних бронювань навіть під екстремальним навантаженням. Головним компромісом є конкуренція за ресурси бази даних (database contention), проте оскільки транзакція обробляє лише один індексований рядок, тривалість блокування вимірюється мікросекундами.

Контекст для України

Для українських розробників та системних архітекторів цей підхід є критично релевантним при створенні високонавантажених маркетплейсів або систем бронювання, де конкуренція за обмежений ресурс є нормою. На прикладі таких сервісів, як Concert.ua чи Karbaz, можна побачити, як помилки в синхронізації призводять до фінансових втрат та репутаційних ризиків через надпродаж квитків. Використання стандартних інструментів PostgreSQL замість складних розподілених блокувань дозволяє українським командам швидше виводити продукти на ринок, забезпечуючи цілісність даних без перевантаження архітектури зайвими рішеннями. Таке розділення відповідальності між Go як швидким фасилітатором та базою даних як гарантом транзакційної цілісності є оптимальним для масштабування локальних IT-продуктів.

Часті запитання

Чому не можна перевіряти статус квитка лише на рівні додатка?
Стандартний алгоритм «знайти квиток — перевірити статус — оновити» є вразливим. До моменту виконання команди оновлення інша горутина може змінити стан запису, що призводить до подвійного бронювання та помилок у даних.
Як саме працює блокування рядків у PostgreSQL для продажу квитків?
Система використовує транзакції з командою SELECT FOR UPDATE. Коли приходить запит, PostgreSQL ставить його в чергу і накладає ексклюзивне блокування на конкретний рядок. Перший запит оновлює статус, а наступні отримують доступ до рядка вже після завершення транзакції та коректно відкочуються.
Telegram

Свіжі новини у нашому Telegram

Отримуйте миттєві сповіщення про нові публікації в рубриці «Код»

@procodeandevenmore