Как удар беспилотников по «Яндексу» едва не погубил мою инди-игру
Как я поднимала онлайн-игру после недели простоя, почему четыре резервные копии не уберегли данные игроков и какой урок преподнесла мне первая серьёзная авария собственного проекта.
Воскресенье пошло наперекосяк
Утром 11 октября я по привычке заглянула проверить, как дела у моей игры. День у меня традиционно начинается с административной панели: сколько людей играло накануне, сколько новичков зарегистрировалось, были ли внутриигровые покупки и не оставил ли кто-нибудь ночью сообщение в поддержке. Для небольшого инди-проекта эта статистика — что-то вроде утреннего ритуала: она подтверждает, что всё работает и что старания не пропадают впустую.
Но в то воскресенье ритуал сорвался. Админка просто не загружалась.
Вообще такое случается, особенно когда активно развиваешь продукт и часто выпускаешь обновления: можно сломать зависимость, допустить ошибку в коде или упустить из виду очередную правку. Поэтому первой мыслью было: «Опять я что-то натворила». Но последнее обновление вышло около недели назад, сервер всё это время работал без сбоев, и причин для внезапного отказа вроде бы не нашлось.
Я попробовала открыть саму игру — она тоже оказалась недоступна. Тогда я зашла в личный кабинет хостинг-провайдера Amvera и увидела сообщение: из-за атаки БПЛА на инфраструктуру Яндекса сервисы провайдера временно не работают.
Стало ясно: откатом последнего коммита здесь не отделаешься.
Дело в том, что я разрабатываю многопользовательскую карточную стратегию PanzerShrek о танковых сражениях Второй мировой. Игроки собирают собственные колоды боевой техники, соревнуются друг с другом, проходят исторические кампании, прокачивают аккаунты и пополняют коллекции. Игра доступна и в браузере, и на Android (кстати, публикация игры в Play Market из России превратилась в отдельный квест). Поскольку значительная часть игровой логики завязана на сервер, его недоступность равносильна остановке всего проекта.
К тому же 11 октября выпало на воскресенье, а по выходным посещаемость у меня обычно хорошая. Аудитория PanzerShrek довольно взрослая, в основном это люди старше 30, которым удобнее играть в свободное от работы время. Серверы отключились в один из самых неудачных моментов.
Была и ещё одна неприятная деталь. О проблемах с дата-центрами Яндекса я слышала за несколько дней до этого, но и представить не могла, что они как-то заденут мою игру.
Почему я выбрала Amvera и при чём тут Яндекс
Когда я только начинала работу над PanzerShrek, вопрос с размещением серверной части пришлось решать самой. Отдельного системного администратора у меня не было, как и большого бюджета на инфраструктуру, да и превращать разработку карточной игры в бесконечное обслуживание собственного сервера совсем не хотелось.
Поэтому я стала искать подходящий российский хостинг и в итоге остановилась на Amvera.
Выбор оказался удачным. Во-первых, это российская площадка, которую можно оплатить обычной банковской картой — по нынешним временам это уже немалое преимущество. Во-вторых, меня устраивала цена, особенно учитывая, что игра только набирала обороты и серьёзного дохода не приносила. К тому же размещение данных на территории России было для меня важно с точки зрения требований о локализации персональных данных.
Но больше всего порадовал процесс развёртывания.
Я настроила автоматический деплой из GitHub: чтобы выпустить новую версию, достаточно отправить изменения в основную ветку репозитория, а Amvera сама соберёт и запустит приложение. Когда ты одновременно разработчица, тестировщица, геймдизайнер и администратор, такая автоматизация — настоящее спасение.
За всё время серьёзных претензий к хостингу у меня почти не возникало. Всё работало стабильно, интерфейс был понятным, а вмешиваться в серверную инфраструктуру вручную приходилось крайне редко.
Я так привыкла к этой стабильности, что перестала задумываться, где физически стоит мой сервер и от каких внешних сервисов он зависит.
В начале октября появились сообщения об атаках БПЛА на дата-центры Яндекса. Первой пострадала инфраструктура в Сасове, затем пришли новости о проблемах на других объектах. Моя игра при этом продолжала работать, и я даже мысленно похвалила себя за выбор хостинга, который, как мне казалось, никак не связан с Яндексом.
Позже выяснилось, что зависимость всё же была — просто я о ней не подозревала.
В этом, пожалуй, и состоит особенность современной облачной инфраструктуры. Разработчик видит удобный сервис с понятными тарифами, контейнерами и автоматическим деплоем, но далеко не всегда представляет, какая цепочка технологических зависимостей скрывается за этим интерфейсом.
Пока всё работает, думать об этом не хочется. Зато после серьёзной аварии вдруг обнаруживается, что независимость твоего сервиса была сильно преувеличена.
Когда остаётся только ждать
Убедившись, что проблема на стороне инфраструктуры, я поняла: сама я почти ничего сделать не могу. Доступа к приложению не было, личный кабинет провайдера не открывался, а повлиять на восстановление я никак не могла.
Мне оставалось следить за сообщениями Amvera и ждать, пока инженеры вернут сервисы к жизни.
Надо отдать должное: сотрудники провайдера старались оперативно информировать пользователей и принимали меры для восстановления. Ситуация была явно нештатной, и я прекрасно понимала, что устранять последствия физического повреждения дата-центров куда сложнее, чем перезапускать упавший контейнер.
Но меня всё сильнее беспокоило другое — база данных моей игры.
Исходный код хранился и на GitHub, и на моём компьютере, а вот актуальная база SQLite лежала прямо на сервере. В ней находились сведения об игроках, их коллекциях, истории сражений, игровом прогрессе — всё то, что нельзя просто восстановить из исходников.
Я, конечно, знала, что резервные копии существуют. На моём тарифе в Amvera предусмотрено автоматическое еженедельное резервное копирование с хранением четырёх последних версий.
Поэтому отдельной системой резервирования я особо не занималась. Мне казалось логичным доверить эту задачу провайдеру, который предлагает такую услугу.
И, признаюсь, до определённого момента такая логика меня полностью устраивала.
Спустя какое-то время Amvera сообщила, что пользователям станут доступны резервные копии приложений, в том числе от 7 октября. Это была отличная новость: потеря нескольких дней игрового прогресса казалась гораздо меньшим злом, чем исчезновение всех данных.
Я уже начала мысленно планировать восстановление проекта и возвращение к обычной работе, но самое интересное было впереди.
База есть, но пользоваться ею нельзя
Примерно 14 октября доступ к личному кабинету Amvera начал возвращаться, и я наконец смогла заняться развёртыванием приложения.
Сервер к тому времени простаивал уже несколько дней, и мне хотелось как можно скорее запустить игру. Однако приложение стартовать не желало, а логи быстро показали, что дело в базе данных.
SQLite сообщала:
database disk image is malformed
Для тех, кто не работал с этой СУБД, поясню: SQLite хранит базу в одном файле и не требует отдельного серверного процесса. Для небольшой многопользовательской игры это удобное решение, особенно на раннем этапе, когда хочется сократить расходы и не усложнять администрирование.
Но если файл базы повреждён, восстановление данных превращается в весьма увлекательное занятие.
Сначала я скачала резервную копию на локальный компьютер и проверила её штатными средствами SQLite — запустила проверку целостности:
PRAGMA integrity_check;
Результат был предсказуемо неутешительным: проверка нашла множество ошибок, подтверждающих, что база повреждена.
Дальше я перебирала разные способы восстановления. Ситуация была нестандартной, поэтому я подключила нейросеть: с её помощью разбиралась в структуре испорченной базы, запускала инструменты SQLite и пыталась вытащить всё, что ещё поддавалось чтению.
Часть данных спасти удалось, но итог заметно поубавил мой оптимизм.
На момент аварии в игре было около 500 зарегистрированных пользователей и примерно 2000 гостевых аккаунтов. Гостевые аккаунты — особенность моей игры: многие предпочитают сначала познакомиться с ней без полноценной регистрации, поэтому их немало.
Из повреждённой базы поначалу получилось восстановить сведения лишь о 145 зарегистрированных игроках.
Такой результат, разумеется, меня не устраивал. Я обратилась в техподдержку Amvera, описала ситуацию и попросила попробовать восстановить данные на стороне провайдера. Сотрудники согласились помочь, и примерно через сутки я получила обработанную копию.
Увы, лучше не стало. После восстановления стандартными средствами SQLite в ней уцелело около 114 пользовательских аккаунтов — несопоставимо мало по сравнению с реальным числом игроков.
Выходило, что сервер постепенно оживает, исходный код никуда не делся, но самая важная часть проекта — данные о пользователях — серьёзно пострадала.
Нельзя было и просто создать новую пустую базу и работать дальше, будто ничего не случилось. За каждым аккаунтом стоит живой человек, который тратил время на игру, собирал коллекцию, развивал профиль и участвовал в боях. Некоторые игроки совершали внутриигровые покупки, так что вопрос сохранности данных был уже не только техническим, но и финансовым.
И тут меня неожиданно выручил файл, о котором я почти забыла.
Как старый файл из «Загрузок» спас аккаунты
Ещё 22 августа мне из любопытства захотелось посмотреть, как устроено резервное копирование в Amvera. Я скачала одну из копий базы на компьютер, убедилась, что файл на месте и открывается, — и благополучно о нём забыла.
С тех пор он так и лежал среди прочего содержимого папки «Загрузки».
Когда стало ясно, что октябрьскую базу восстановить очень непросто, я вспомнила об этом файле и решила использовать его как вспомогательный материал.
Разумеется, августовская база не могла хранить данные об игроках, пришедших в сентябре и октябре. Зато в ней сохранились целые структуры таблиц, связи и другие элементы, которые пригодились при анализе более свежей, повреждённой копии.
После новых попыток восстановления с помощью нейросети мне всё же удалось собрать информацию почти обо всех зарегистрированных пользователях по состоянию на 8 октября.
Это был огромный шаг вперёд по сравнению с первыми 114–145 аккаунтами, которые получалось извлечь из повреждённых копий.
К сожалению, полностью вернуть игровой прогресс так и не вышло. Профили сохранились, но значительная часть данных о развитии аккаунтов, коллекциях и истории сражений была утрачена.
Зато с одной категорией данных мне очень повезло: история транзакций по внутриигровым покупкам уцелела полностью. Благодаря этому я смогла разобраться с прошлыми платежами и восстановить игрокам оплаченные приобретения.
Вот тогда я по-настоящему оценила значение случайно скачанного августовского файла.
Получается, что четыре автоматические копии, на которые я полагалась, не позволили вернуть игру в прежнее состояние, а забытый файл двухмесячной давности стал одним из самых полезных инструментов спасения проекта.
Более наглядного довода в пользу независимого резервного копирования, пожалуй, не придумать.
Решение, которое разработчику принимать не хочется
Когда стало понятно, что прогресс полностью восстановить не получится, передо мной встал неприятный вопрос: что делать дальше?
Можно было и дальше собирать данные по крупицам, вручную возвращать достижения и надеяться когда-нибудь приблизиться к состоянию игры до аварии. Но чем дольше я об этом думала, тем яснее понимала: такой путь отнимет уйму времени и всё равно не гарантирует справедливого результата для всех игроков.
Поэтому я выбрала другой путь — глобальный вайп, то есть обнуление игрового прогресса.
Для многопользовательских игр это не такая уж редкость. Многие проекты периодически запускают новые сезоны с частичным или полным сбросом достижений, особенно при серьёзном пересмотре баланса или игровой экономики.
В PanzerShrek такое решение отчасти даже назрело. Игра открыта для всех с августа, и за это время я заметно переработала баланс некоторых карт, изменила способности и механики. У игроков уже сложились определённые стратегии и связки, некоторые из которых оказались слишком сильными.
С этой точки зрения новый старт мог пойти на пользу. Но одно дело — заранее объявить новый сезон и подготовить к нему аудиторию, и совсем другое — поставить людей перед фактом: их прогресс пропал из-за аварии.
Поэтому я решила компенсировать потери внутриигровой валютой, чтобы игроки могли быстро вернуться на прежний уровень развития. Отдельные вопросы пришлось решать вручную через поддержку.
Тут нужно пояснить: PanzerShrek я делаю практически в одиночку, поэтому и служба поддержки у меня состоит из одного человека. Нетрудно догадаться, что этот человек — я.
После восстановления серверов мне предстояло ещё несколько дней активно общаться с игроками, разбираться с их аккаунтами, проверять покупки и помогать вернуться в игру.
Но прежде нужно было окончательно привести в порядок серверную инфраструктуру.
Сервер заработал. Теперь надо починить всё остальное
Восстановление оказалось не просто запуском нового контейнера со старым кодом.
IP-адрес сервера изменился, поэтому пришлось обновлять DNS-записи у регистратора домена и проверять, что клиентские приложения обращаются по верному адресу. Затем требовалось убедиться, что корректно работают авторизация, игровые соединения, административная панель и остальные серверные механизмы.
Когда основные функции заработали, я наконец смогла выдохнуть. Игроки потянулись обратно, в статистике снова появились активные пользователи, а поддержка постепенно переключилась с вопросов о недоступности игры на восстановление аккаунтов.
Но вскоре выяснилось, что я упустила ещё один важный элемент проекта — платёжную систему.
Для внутриигровых покупок я использую ЮKassa. После восстановления инфраструктуры адрес серверной части изменился, и настройки интеграции в личном кабинете платёжного сервиса понадобилось обновить.
Самое забавное, что проблему я заметила уже после запуска, когда игроки вернулись и начали пытаться совершать покупки.
Пришлось снова настраивать интеграции, проверять взаимодействие сервисов и убеждаться, что платежи обрабатываются корректно.
В итоге восстановление превратилось в последовательное устранение целой цепочки неполадок. Сначала нужно было вернуть базу данных, затем поднять приложение, перенастроить адреса, наладить платежи — и только после этого можно было с уверенностью сказать, что игра снова работает в штатном режиме.
На всё ушло несколько дней, причём львиную долю времени я потратила не на написание кода, а на возвращение к жизни того, что неделей раньше спокойно работало.
Я боялась, что игроки не вернутся
Пожалуй, самым тяжёлым в этой истории была даже не потеря данных, а неопределённость: что будет с проектом дальше.
PanzerShrek — небольшая инди-игра, которую я развиваю собственными силами. У меня нет огромного рекламного бюджета, команды маркетологов и возможности постоянно привлекать новых пользователей масштабными кампаниями. Каждый игрок, который заинтересовался проектом, зарегистрировался и продолжает возвращаться, для меня по-настоящему важен.
А теперь взгляните на ситуацию глазами обычного пользователя. Вы нашли интересную игру, начали развивать аккаунт, собрали несколько колод, возможно, даже что-то купили. И вдруг игра на несколько дней перестаёт работать, а после восстановления выясняется, что часть прогресса пропала.
Я понимала: многие в такой ситуации вполне могут просто удалить приложение и никогда к нему не вернуться. Тем более что других мобильных игр сейчас хоть отбавляй.
Поэтому, пока я возилась с повреждёнными базами и восстанавливала инфраструктуру, меня не покидала мысль: сколько игроков останется после всего этого?
Но реакция аудитории оказалась совершенно неожиданной.
Ещё во время простоя мне начали писать на почту и в личные сообщения. Люди спрашивали, когда игра заработает, что произошло, и рассказывали, что ждут возвращения серверов. Некоторые искренне переживали за судьбу проекта — не столько из-за потерянного прогресса, сколько из страха, что игра вообще больше не запустится.
Это меня удивило и невероятно поддержало.
Когда разрабатываешь что-то в одиночку, легко начать воспринимать аудиторию лишь через цифры. В админке видишь число регистраций, ежедневную активность, удержание, покупки и другие показатели, по которым пытаешься понять, насколько успешно развивается продукт.
Но за всей этой статистикой стоят живые люди, для которых твоя игра уже стала частью привычного досуга.
А самое удивительное случилось после восстановления серверов.
Буквально за два дня число активных игроков почти вернулось к прежнему уровню.
Более того, люди снова начали совершать внутриигровые покупки — то есть поддерживали проект не только присутствием, но и деньгами.

Для меня это стало главным открытием всей истории. Я готовилась заново привлекать аудиторию, а оказалось, что вокруг PanzerShrek уже сложилось настоящее сообщество, которому небезразлично развитие игры.
Конечно, несколько дней простоя обошлись проекту недёшево. За это время я могла бы работать над новыми механиками, выпускать обновления, тестировать режимы и привлекать новых игроков. Вместо этого пришлось восстанавливать данные и расхлёбывать последствия аварии.
Но эмоционально всё сложилось неожиданно наоборот. Вместо разочарования я почувствовала ещё большее желание продолжать: теперь я точно знала, что игра нужна не только мне.
Как я решила сделать аварию частью истории игры
Одно из любимых моих направлений в PanzerShrek — исторические кампании. Помимо обычных боёв с другими игроками или компьютером, я время от времени добавляю тематические режимы, посвящённые событиям Второй мировой войны.

Размышляя о том, как поддержать игроков после случившегося, я вспомнила историю восстановления Горьковского автомобильного завода после немецких бомбардировок летом 1943 года.
ГАЗ тогда пережил серию разрушительных авиаударов: серьёзно пострадали производственные корпуса, оборудование и инфраструктура предприятия. Тем не менее завод удалось восстановить, и производство продолжилось.
Разумеется, я ни в коем случае не собираюсь сравнивать трагедии военного времени с аварией небольшого игрового сервера — это совершенно несопоставимые по масштабу и последствиям вещи. Но сама тема возрождения производства после разрушений показалась мне интересной, к тому же она отлично вписывается в исторический период игры.
Поэтому следующую историческую кампанию PanzerShrek я решила посвятить именно Горьковскому автозаводу и его восстановлению после бомбардировок.
Кроме того, мне хотелось как-то отметить игроков, которые остались с проектом в трудное время.
Так появился специальный памятный набор, который получат только те, кто был зарегистрирован в игре на момент аварии. В него войдут уникальная рубашка для карт и три эксклюзивные карты: американский ударный беспилотник, немецкий разведывательный БПЛА и советская зенитная установка на базе грузовика ГАЗ-АА.

Позже получить эти предметы будет невозможно. Пусть они станут своеобразным знаком принадлежности к тем самым первым игрокам, которые вместе с проектом пережили его первую серьёзную аварию.
Выходит любопытная картина: событие, которое сперва казалось настоящей катастрофой для маленького инди-проекта, неожиданно подарило повод для нового игрового контента и более тесного общения с сообществом.
Но куда важнее было разобраться с техническими причинами случившегося и сделать так, чтобы подобное не повторилось.
Чему меня научила эта история
До PanzerShrek у меня уже был немалый опыт создания программного обеспечения, но в основном я работала с системами, рассчитанными на ограниченный круг пользователей. Часто это были решения для корпоративной инфраструктуры, локальных сетей и собственных серверов организаций.
Публичный онлайн-продукт оказался совсем другим опытом.
Здесь нельзя просто предупредить пару знакомых пользователей, что система временно недоступна, а потом спокойно её чинить. Любая серьёзная авария напрямую бьёт по людям, которые рассчитывают на работу сервиса, тратят на него своё время и порой деньги.
Поэтому после восстановления игры я решила пересмотреть отношение к инфраструктуре, и прежде всего к хранению пользовательских данных.
Главный вывод оказался довольно банальным: наличие резервной копии вовсе не гарантирует, что из неё получится восстановить работающую систему.
Раньше я исходила из того, что если хостинг автоматически создаёт бэкапы, то вопрос резервирования для меня закрыт. Теперь понимаю: этого мало. Нужно не только следить за наличием копий, но и периодически проверять их целостность, а в идеале — разворачивать тестовую копию приложения и убеждаться, что из сохранённых данных действительно можно поднять рабочий сервис.
У SQLite есть ещё один важный нюанс. Раз база — это файл, возникает естественное желание просто регулярно копировать его в другое место. Однако если делать это во время работы приложения, особенно в режиме WAL, можно получить несогласованную копию. Поэтому резервирование стоит организовывать штатными механизмами SQLite, например через Online Backup API, а затем проверять результат.
Второй вывод касается места хранения копий. Теперь я точно не хочу полагаться только на инфраструктуру хостинг-провайдера. Даже если провайдер предлагает удобную систему
Владелец физического издания игры для Switch 2 столкнулся с проблемой, и Nintendo направила его к издателю
Княжества Юга в Total War: Warhammer 3
Создатели The Last of Us раньше высмеивали Resident Evil, и теперь об этом вспомнили
Создатель God of War резко раскритиковал коллекционное издание PlayStation и заявил, что раньше такое не допустили бы
Слух: разработкой Star Wars: Battlefront 3 может заняться Bungie, а не DICE
Видео: серьёзная проблема Ведьмака 4
Менеджер Xbox заявил об использовании ИИ в компании и удалил аккаунт после волны критики
Новая вакансия раскрыла детали сиквела Lies of P: для работы нужны только фанаты соулслайков