LiveOps-ивенты — релизы без App Review, которые тестируют слабее всего
Релиз игры протестирован, выкатан, все выдохнули. А дальше начинается настоящая жизнь: каждую неделю — новый ивент. Батл-пасс, турнир выходного дня, распродажа, сезон. И всё это выкатывается без релиза: серверным конфигом, без App Review, без бинарника — часто в пятницу к вечеру, потому что ивент стартует в выходные. Ивент — это полноценный мини-релиз, который попадает к юзерам быстрее и проще любого билда. А тестируется, по моему опыту, в разы слабее. Хотя должен бы наоборот: у него нет страховки в виде ревью стора и staged rollout по умолчанию.
Разберу, что именно ломается в ивентах и как я их тестирую.
Ивент — это конфиг, время, экономика и прод. Одновременно
Четыре свойства делают ивенты особым объектом тестирования. Контент приезжает серверным конфигом — то есть минует всю привычную релизную защиту. Он приезжает поверх любой версии клиента — включая ту, что вышла полгода назад. У него есть окно во времени — а время у юзеров идёт в 38 таймзонах, и некоторые умеют его переводить. И он раздаёт награды — то есть трогает экономику игры. Каждое из четырёх свойств — отдельный класс багов.
Границы окна: старт, конец и юзер из UTC+13
Первый вопрос к любому ивенту: старт и конец — по какому времени? UTC, серверное, локальное юзера? Что бы ни ответила команда, проверяй краевых юзеров: Окленд (UTC+13) и Гонолулу (UTC-11) видят ивент в разное календарное время, и «ивент выходных» у кого-то из них начинается в пятницу днём, а у кого-то заканчивается в понедельник.
Дальше сами границы. Что видит юзер до старта — таймер, тизер, ничего? Что происходит с юзером, который вошёл в ивент за минуту до конца — его выкидывает посреди матча? И главное: что с наградами, которые заработаны, но не заклеймлены к моменту конца? Хорошие команды делают grace period на клейм или auto-grant; плохие — молча сжигают, и саппорт неделю разгребает «у меня пропали награды». Какое поведение у вас — должно быть решено и проверено до старта, а не после жалоб.
Перевод часов: главный чит и главный источник багов
Классика жанра: юзер переводит часы девайса вперёд — и энергия восстановилась, кулдаун прошёл, ежедневная награда выдалась ещё раз. Переводит назад — ивент «ещё не начался», хотя уже идёт. Правило простое: источник истины — серверное время, клиентское — только для отображения таймеров. Но это правило должно быть проверено на каждом ивент-флоу отдельно: перевод вперёд, назад, смена таймзоны, полёт через океан (легитимный кейс, который выглядит как чит!). Если игра оффлайновая или синхронизируется редко — решение о том, чему верить, становится продуктовым, и его тоже принимают до старта.
Конфиг поверх старого клиента
Про матрицу версий я подробно писал в посте про обновления, для ивентов это критично вдвойне: конфиг уходит всем версиям сразу. Новый ивент ссылается на предмет/механику/экран, которых в клиенте полугодовой давности не существует. Правильное поведение — минимальная версия в конфиге ивента и graceful скрытие на старых клиентах. Неправильное, но частое — краш при парсинге или, тоньше, ивент показался, но кнопка ведёт в никуда. Тест обязателен на самой старой поддерживаемой версии — не только на текущей.
Награды: ровно один раз, даже при обрыве сети
Клейм награды — это транзакция, и она обязана быть идемпотентной: тап по «Забрать», обрыв сети, ретрай — награда одна, не ноль и не две. Это ровно тот же класс проверок, что в посте про idempotency и ретраи: двойной тап, kill приложения между запросом и ответом, повтор клейма с другого девайса.
И отдельно — цены и номиналы. Ивент-конфиг набивается руками в таблицу или JSON, и «запятая не там» превращается в топ-меч за 1 монету. Такие истории кончаются откатом экономики и волной возвратов. Дешёвая страховка — валидация конфига по схеме плюс sanity-чеки диапазонов («цена > 0», «награда < X») в пайплайне конфига. Если валидации нет — её стоит выпросить раньше, чем случится первый инцидент.
Конфиг — это код. Со всеми последствиями
Урок разбора CrowdStrike применим к ивентам буквально: контент, который минует релизный процесс, роняет прод так же, как код — только быстрее. Поэтому к ивент-конфигу я предъявляю те же требования, что к коду: он проходит стейджинг (окружение, где конфиг можно выкатить и прогнать до прода), у него есть ревью (кто может править прод-конфиг? двое глаз?), он выкатывается постепенно (хотя бы на процент юзеров, если платформа умеет), и у него есть kill switch — способ выключить ивент за минуту, не дожидаясь никого. Если чего-то из этого нет — это находка тестировщика уровня «важнее любого бага в самом ивенте».
Старт ивента — это нагрузочный тест на проде
В момент старта все ломятся одновременно: открыть ивент-экран, купить в ивент-магазине, попасть в лидерборд. В момент конца — массовый клейм. Это готовый спайк-профиль из поста про нагрузочное: если лидерборд и магазин не гоняли под нагрузкой старта — первый большой ивент прогонит за вас, на живых юзерах.
Ивент №2 и грязное состояние
Ивенты повторяются, и второй запуск по тому же шаблону ломается о хвосты первого: незакрытые сущности, застрявший прогресс, юзер с активным стейтом прошлого ивента входит в новый. Регресс «прошлый ивент корректно завершился и подчистил за собой» — обязательная часть чек-листа каждого следующего запуска. И тестировать нужно не только на свежем аккаунте: основная масса юзеров приходит в новый ивент со шлейфом старых.
Чем тестировать: time travel или никак
Честно тестировать ивенты можно только если на стейдже умеют двигать серверное время (time travel API или хотя бы правка даты старта конфига). Без этого «тестирование ивента» превращается в «посмотрели за день до старта, дальше как пойдёт» — то есть в мониторинг вместо тестирования. Если у вашего бэкенда этой ручки нет — это первое, что стоит попросить у команды, важнее любого тулинга. Остальной набор: стейджинг-конфиг, подмена конфига на клиенте через Proxyman/Charles (Map Local) для быстрых проверок краевых значений, и девайс с ручным переводом часов.
Чек-лист запуска ивента
- Старт/конец: заданы явно, проверены в крайних таймзонах (UTC+13 / UTC-11)
- До старта / после конца: осознанное поведение, grace period для клейма решён
- Перевод часов вперёд/назад и перелёт: серверное время — истина
- Самая старая поддерживаемая версия клиента: ивент скрыт или работает, не крашит
- Клейм идемпотентен: обрыв сети, двойной тап, второй девайс — награда одна
- Цены/номиналы: конфиг валидируется схемой, sanity-чеки диапазонов
- Конфиг: стейджинг, ревью прод-правок, staged rollout, kill switch — есть и проверены
- Старт и конец под нагрузкой: лидерборд, магазин, массовый клейм
- Хвосты прошлого ивента подчищены, тест на «пожившем» аккаунте
- Дежурный на старте ивента назначен (особенно если старт в выходные)
Ирония LiveOps в том, что «маленький ивент» трогает всё самое опасное сразу — прод, экономику, время и старые клиенты, — но проходит мимо всех процессов, которые защищают «большой релиз». Верни ему эти процессы — и ивенты перестанут быть еженедельной рулеткой.