Система інвентарю: що ламається під час паралельних операцій
Класична система інвентарю працює за схемою «прочитав — змінив — записав»: запит бере поточний стан предмета, змінює його в пам'яті та зберігає назад. Поки звернення йдуть по одному, це непомітно.
Під час одночасних обмінів або крафту обидва запити читають одну й ту саму версію даних, і другий мовчки перезаписує результат першого.
Під час спільного крафту це дає від'ємні залишки: два клієнти списують останній екземпляр ресурсу, сервер підтверджує обидві операції. Обміни дають дзеркальний ефект — один предмет іде двом отримувачам, коли списання та видача не виконуються як атомарні транзакції предметів. Перевірка володіння проходить успішно в обох, бо між перевіркою та записом стан ще не змінився.
Дублікати предметів з'являються й через повторні запити.
Гравець натискає купівлю, відповідь не доходить через обрив зв'язку, клієнт повторює запит, а сервер обробляє його як новий: без ідентифікатора операції нарахування проходить двічі, і в інвентарі з'являється другий екземпляр.
Втрата покупок під час обриву зв'язку — зворотний бік тієї самої механіки: платіж підтверджено платформою, а запис в інвентар відкотився або не дійшов.
Клієнт показує старий стан із локального кешу, розсинхронізація клієнта й сервера накопичується та розходиться під час кожного входу, доки серверний стан не стане для гравця несподіваним.
Далі вмикається ручний розбір: потік звернень до підтримки зростає, правки вносять прямо в живу базу без трасування, хто і коли змінював запис. Розбираємо, як це закривається.
Що дає атомарність операцій: цілісність, швидкість, відмови
Атомарність — це коли обмін, крафт або передача предметів фіксуються повністю або не фіксуються взагалі. Стану, у якому предмет зник в одного гравця і не з'явився в іншого, не залишається навіть під час обриву зв'язку в момент запису. Для проєкту це різниця між поодиноким зверненням до підтримки та хвилею скарг після масової події.
Цілісність під час масових обмінів, крафту та передачі предметів
Кожна операція проходить серверну перевірку й лягає в журнал змін одним записом: списання та нарахування йдуть усередині спільної транзакції з блокуванням задіяних комірок.
Крафт, що витрачає кілька стеків, і обмін між гравцями підкоряються одному правилу, тому «половина» рецепта застосуватися не може.
Ідемпотентність запитів закриває повторне надсилання: якщо гравець не дочекався відповіді й натиснув ще раз, друге списання не відбудеться — повтор розпізнається за ключем операції, і в інвентарі залишається рівно одна зміна.
Відгук під піковим навантаженням і зникнення класу відмов
На піковому навантаженні — старт сезону, івент, роздача нагород — вузьким місцем стає не сама логіка, а конкуренція за ті самі комірки. Ми розносимо блокування за мінімальним набором об'єктів і тримаємо часті операції в пам'яті, а важкі — у черзі з послідовною обробкою. Час відгуку на звичайні дії з інвентарем при цьому не зростає разом із кількістю гравців.
Клас відмов змінюється якісно: замість «предмет зник» або «предмет задублювався» залишаються тільки відмови виду «операцію відхилено, стан не змінився». Такі випадки гравець бачить як зрозуміле повідомлення й може повторити дію, а не як втрату прогресу.
На прийманні це перевіряється просто: розбіжності після масових обмінів, частка дублікатів і час відгуку на піку — три метрики, за якими видно, що система інвентарю витримує навантаження живої гри.
Як влаштована система інвентарю: режими зберігання та синхронізації
Вибір між журналом подій і поточним станом
Журнал подій зберігає кожну дію гравця окремим записом і дає змогу відновити інвентар на будь-який момент часу. Поточний стан тримає готовий знімок — читається дешевше, але історія змін втрачається, якщо не писати її окремо.
Частіше беруть гібрид: знімок як джерело для віддачі клієнту плюс журнал останніх операцій для розбору спірних ситуацій. Версіонування запису — номер версії в кожної зміни — дає відкат до попереднього стану й захищає від повторного застосування тієї самої операції.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Журнал подій | Потрібна повна історія, розбір скарг, відкат | Зростає обсяг, читання потребує повторного збирання |
| Поточний стан | Часте читання, простий інвентар | Немає історії, відкат лише з резервної копії |
| Знімок + журнал | Баланс швидкості читання й розбираності | Складніша схема та перенесення даних |
Де проходить межа між клієнтом і сервером
Клієнт малює інвентар і показує результат одразу, але не є джерелом істини: будь-яка зміна підтверджується сервером. Сервер валідує операцію, ставить блокування операцій гравця на час транзакції й лише потім фіксує зміну — так два паралельні запити не спишуть один предмет двічі. Якщо підтвердження не надійшло, інтерфейс відкочує показаний результат і перечитує стан із сервера.
Етапи впровадження: від аудиту операцій до передачі регламентів
Впровадження системи інвентарю починається не з коду, а з розбору того, як предмети зараз потрапляють до гравця і що з ними відбувається далі.
Ми описуємо кожну операцію — видача за нагороду, купівля, обмін, витрата, повернення під час скасування — і позначаємо місця, де баланс може розійтися: обрив зв'язку, повторний запит, одночасні дії з одного акаунта.
Аудит поточних операцій і моделі даних
- Збираємо перелік операцій і правил: ліміти на предмети, терміни життя, прив'язка до події або сезону, хто ініціює зміну — клієнт, ігровий сервер або зовнішній сервіс.
- Розбираємо поточну модель даних: де зберігається стан, як пов'язані предмети та стеки, чи є журнал змін і чи можна за ним відновити інвентар на будь-який момент.
- Проєктуємо цільову схему: журнал операцій з унікальним ключем запиту, щоб повторне надсилання не видавало предмет двічі, і правила блокувань під час паралельних покупок.
Перевірка на збоях і повторних запитах
- Проганяємо сценарії відмов: обрив зв'язку в момент списання, дублювання запиту, одночасна купівля останнього предмета, падіння сервісу між списанням і видачею — стан має збігатися.
- Переносимо реєстр на нову схему в тіньовому режимі: старий і новий розрахунок ідуть паралельно, розбіжності звіряються построково, відкат доступний до моменту перемикання.
- Передаємо регламент експлуатації: метрики розбіжностей, оповіщення про аномалії, порядок ручних правок з обов'язковим записом у журнал аудиту та зрозумілий відкат на попередню версію.
Комплект передачі: схеми даних, політики доступу, регламенти
Комплект передачі збираємо так, щоб команда замовника розвивала систему інвентарю без нас: новий інженер за цим комплектом за день розуміє структуру предметів, а черговий — що робити при розбіжності балансів. Усе описане перевірено на робочому контурі, а не відновлено з пам'яті на виході з проєкту.
- Схема даних предметів: сутності, зв'язки, правила стекування, ідентифікатори екземплярів і заборони, які не можна порушувати.
- Політики доступу та ролі адміністраторів: матриця прав на видачу, вилучення й правку балансів.
- Набір тестів на цілісність: подвійне списання, паралельна видача, відкат незавершеної транзакції.
- Документація з відновлення та звірки: точки відновлення, порядок дій під час збою, ознаки збіжності.
- Регламенти експлуатації: ротація ключів, видача прав, ескалація інцидентів.
- Журнал змін схеми: версії, міграції, сумісність раніше збережених інвентарів.
Політики доступу та ролі адміністраторів
Права розводимо за мінімальною достатністю: рядовий адміністратор видає предмети за заявкою, старший править баланси, зміна схеми доступна вузькому колу. Кожна дія потрапляє в незмінний журнал з автором — спір «хто видав» закривається за хвилину.
Документація з відновлення та звірки
Описуємо два сценарії: відкат до цілісного стану та звірку після часткового збою. Зазначено порядок дій, відповідальні та ознаки, за якими черговий розуміє, що інвентар знову збігається.
Кейс: прибрали дублі предметів і втрати покупок під навантаженням
Проєкт — мобільна гра з внутрішньоігровою економікою: предмети, набори, покупки за внутрішню валюту та за платежі. До нас прийшли після серії інцидентів на сезонних подіях, коли зрослий онлайн оголив слабке місце системи інвентарю.
Що було до: ручна звірка та потік звернень
Облік предметів вівся у двох місцях: частина стану трималася в профілі гравця, частина — у черзі операцій. Під час обриву зв'язку між списанням і записом гравець міг отримати предмет двічі або втратити оплачену покупку.
Підтримка щотижня збирала скарги, а інженери вручну звіряли реєстр із журналом операцій — на звірку йшов робочий день, і до половини розбіжностей знаходили вже за зверненнями. У перші хвилини події кількість таких скарг зростала кратно.
Що стало після: метрики та зняті ризики
Видачу предметів перевели на атомарні транзакції з ключем операції: повторний запит не створює другий запис, а повертає результат першого. Проєкт працює на Unity, але серверну частину інвентарю ми тримаємо незалежною від клієнта — оновлення гри не ламає облік.
За підсумками контрольного періоду: дублі предметів у реєстрі — нуль, звернення щодо втрачених покупок скоротилися приблизно в шість разів, автоматична звірка реєстру займає хвилини замість робочого дня.
Навантажувальний пік пройшов без розбіжностей між реєстром і журналом операцій. Зняті ризики — подвійна видача на повторних запитах, втрата оплаченої покупки під час обриву зв'язку, розбіжність балансів між сервісами.
Підтвердні джерела — панелі моніторингу, журнал транзакцій і вивантаження звернень підтримки за зіставні періоди до та після.
Чим атомарна транзакція відрізняється від блокування запису?
Блокування запису розводить операції в часі: перший потік захоплює запис, решта чекають на звільнення. Атомарна транзакція відповідає за інше — набір змін застосовується повністю або відкочується, не залишаючи половину стану. Тому вибір не між ними, а в тому, які блокування живуть усередині транзакції та як довго.
Під час конфлікту операцій песимістичний підхід змушує другого учасника чекати, і на масових операціях відгук зростає разом із довжиною черги — аж до відмов за часом.
Оптимістичний підхід не чекає: він читає стан із версією, під час запису звіряє її і, якщо дані вже змінили, повторює операцію заново. Швидкий шлях — один прохід, дорогий — повтор.
Вибір залежить від частоти конфліктів на один рядок. Поки за один предмет конкурують рідко — нагороди надходять у різні комірки, — оптимістичний підхід дає нульові очікування й майже не витрачає ресурс на повтори.
Якщо ж кілька потоків постійно пишуть в один і той самий стек, повтори йдуть лавиною, і песимістичний підхід із коротким утриманням блокування передбачуваніший.
Для інвентарю важливо, щоб обмін, збирання предмета з ресурсів і передача між гравцями йшли однією атомарною транзакцією: частково застосований обмін гірший за відмову.
Ми обмежуємо кількість повторів, ведемо ключ ідемпотентності проти дублів під час повторного надсилання запиту й стежимо за часткою конфліктів — вона підказує, коли схему час змінювати.
Обмеження та підводні камені: часті технічні питання
Перед стартом технічні сумніви стосуються не логіки предметів, а того, як новий шар уживається з працюючим сервером. Нижче — те, що найчастіше виринає на оцінці: увімкнення на живому проєкті, накопичені дублі, обриви зв'язку, міграція та права доступу.
Чи можна впроваджувати без зупинки гри?
Так, зупинка не потрібна. Новий шар виноситься окремим сервісом зі своєю схемою даних, стара логіка продовжує обслуговувати інвентар. Увімкнення йде за перемикачем і спочатку на частину гравців — якщо перевірки розходяться з очікуваним, перемикач повертається назад.
Що робити з уже накопиченими дублями?
Їх розбирають до ввімкнення. Звірка йде за журналом операцій: для кожної позиції відновлюється ланцюжок нарахувань і визначається один коректний залишок. Зайве списується окремою операцією із записом в аудит, щоб історію можна було пояснити гравцю та підтримці.
Що станеться під час обриву зв'язку або повторного надсилання запиту?
Кожна операція несе унікальний ідентифікатор: повторний запит не створює другий предмет — сервер розпізнає його й повертає результат першого. Транзакція або підтверджується повністю, або відкочується, тому стану «списано, але не нараховано» не виникає.
На кому міграція і як розмежовуються права?
Міграцію ведемо ми: переносимо залишки, звіряємо збіжність до та після, тримаємо шлях відкату. Від замовника потрібні доступ до сховища й людина, що підтверджує правила щодо спірних предметів.
Права ділимо за ролями: підтримка бачить історію, але не змінює залишки; нарахування в межах ліміту — в економістів; довідник предметів — в адміністраторів.
Обговоримо задачу: що надіслати для оцінки та плану робіт
Щоб повернутися з оцінкою робіт, а не з вилкою «від і до», потрібні вихідні дані щодо системи інвентарю: правила видачі та списання предметів, обмеження на конкурентні операції, цільові платформи. Чим конкретніші ввідні, тим точніший план впровадження та склад команди.
- Правила інвентарю: категорії предметів, стеки, ліміти слотів, прив'язка до персонажа або акаунта.
- Схема даних і поточне сховище: структура таблиць, де зберігається стан, чи є журнал операцій.
- Вимоги до атомарності: які операції мають проходити повністю — продаж, обмін, створення предмета, нагорода за бій — і що відбувається під час обриву зв'язку.
- Сценарії навантаження: пікова кількість одночасних гравців, частота операцій, сезонні сплески.
- Цільові платформи та клієнтські гілки: де застосунок уже випущено, які обмеження є в магазинів.
- Доступи: репозиторій, тестове оточення, вивантаження знеособлених даних для перевірки гіпотез.
- Обмеження проєкту: терміни, етапність випуску, хто на боці замовника ухвалює рішення щодо правил гри.
Далі ми збираємо це в порядок робіт: спочатку контур даних і транзакційна модель, потім серверна логіка, потім клієнтська частина та перевірки на конкурентних сценаріях. На виході — оцінка робіт, план впровадження за етапами та склад команди під ваш обсяг. Опишіть задачу — повернемося з оцінкою та планом.







