За даними 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), проте оскільки транзакція обробляє лише один індексований рядок, тривалість блокування вимірюється мікросекундами.