Все побежали, и я побежал. Недавно я запустил серию онлайн-митапов, куда приглашаю на дискуссию экспертов в области разработки крупных IT-проектов. Нашим первым гостем был Максим Барышников, Head of Platform из Wargaming. Ниже – расшифровка нашего разговора, вернее, её первая часть, посвященная архитектуре.
Из этой части вы узнаете, например:
- сколько людей работает в Wargaming и сколько строк кода в «Танках»
- как, какие и куда едут байты во время боя в «Танках»
- какие подходы используют в Wargaming для обеспечения масштабируемости и отказоустойчивости
- какие архитектурные боли испытывают и на какие компромиссы между геймплеем и инженерными практиками идут
- почему в Python приходится отключать garbage collector, и где используется Erlang
- какие у Wargaming open source policies, и что они открывают в паблик
Разговор получился достаточно длинным, но подробным, если вам интересна тема разработки больших игровых проектов — прошу под кат.
Алексей Рыбак: Всем привет!
Максим Барышников: Привет.
Алексей Рыбак: Сегодня у нас в гостях Максим Барышников из Wargaming. Поговорим о разном, совместим интервью, ваши вопросы, и сделаем еще такую сессию, которую я назвал Lean Bar, но о ней позже. Максим, привет! Расскажи буквально пару слов о себе, пока я зашлю URL трансляции нам в канал и в группу на Facebook.
Максим Барышников: Привет. В Wargaming я с 2013 года, по-моему. До этого у меня была своя небольшая компания. До этого я занимался другими достаточно крупными проектами. А начинал свою айтишную карьеру с работы в собственном университете – это белорусский Политех, 1999 год. Где-то на втором или третьем курсе, когда я сдал свой первый, можно сказать, коммерческий проект, я примерно и отсчитываю свою профессиональную деятельность.
Алексей Рыбак: Сейчас у нас будет несколько сессий. Одна вводная: знакомство, блиц, короткие вопросы, короткие ответы, в первую очередь про компанию, про ваш технический setup. Приблизительно полчаса будем говорить про компанию, про технологии, которые используются, про особенности игровой разработки, про архитектуру. И после этого будет сессия комментариев, вопросов, когда мы на основную панель будем подключать всех желающих, кто находится в комнате Zoom. Поговорим таким образом 15 минут. Может быть, больше, может быть, меньше. Никто в рамки нас особо не ставит. Потом опять полчаса поговорим про управление большим коллективом. У тебя сейчас большой коллектив, несколько сотен человек, да?
Максим Барышников: 200, да. Достаточно большой.
Алексей Рыбак: Да. Поговорим про управление большими коллективами, про какие-то практики, которые используются, про какие-то личные боли – про разное. И после этого опять 15 минут вопросов. И в самом конце мы сделаем так называемый Lean Bar Session, когда все желающие с помощью канбан-доски могут предложить темы для обсуждения. Дальше мы за них проголосуем, что-то выберем. Вы достанете, может быть, какие-то напитки, которые припасли (я припас бутылочку пива). И в свободной форме, у кого будет время, пообщаемся в значительной более неформальной обстановке.
Максим, вопросы сейчас будут достаточно быстрые. Погнали.
Максим Барышников: Давай.
Блиц-опрос
Алексей Рыбак: Сколько у вас в среднем во всех проектах вашей компании пользователей онлайн в пиках?
Максим Барышников: В пиках, если суммарно говорить, то немногим больше миллиона сейчас будет.
Алексей Рыбак: А какие у вас самые пиковые часы, дни?
Максим Барышников: Как правило, это локальный вечер, 8-9 часов локального времени. Если смотреть по российским кластерам – они у нас более-менее регионально адресованы, охватывают Россию и СНГ, – это 8-9 вечера сейчас по Москве.
Алексей Рыбак: Сколько человек обычно в месяц пользуются сервисами?
Максим Барышников: Если брать целиком все игры, то несколько десятков миллионов. Наверное, 50 с небольшим.
Алексей Рыбак: Давай поговорим про стек технологий. Какие языки программирования вы используете?
Максим Барышников: У нас очень много Python, это не секрет. Дальше у нас есть C++ (безусловно, без него никуда). Весь более-менее традиционный веб-стек тоже основан на Python преимущественно. Сейчас появляется потихоньку Rust, он в некоторых областях сейчас очень хорошо себя показывает. Есть Erlang, Elixir, есть немножко Go, есть Java (умеренное количество). И для всяких энтерпрайсных внутренних наших систем есть .NET, и ничего такого больше нет. Достаточно много.
Алексей Рыбак: Какие вы используете системы контроля версий?
Максим Барышников: «Танки» в SVN, но сейчас основная – это Git (Bitbucket конкретно).
Алексей Рыбак: Какое примерно у вас число строк кода?
Максим Барышников: Строк кода без пробелов и всего прочего в танках около 10 с небольшим миллионов. Суммарно сказать затрудняюсь.
Алексей Рыбак: Какие сервера приложения вы используете? По-видимому, там стек достаточно большой. Может быть, назовешь 1-2 основных.
Максим Барышников: Да, стек там действительно достаточно большой, разлапистый. Все Python веб-сервисы, которые есть, они у нас работают достаточно традиционно – через uWSGI. Про игровые сервера я не говорю, там всё достаточно специфично.
Что еще из интересного? Например, есть Apache Ignite немножко. Всякое интересное в обработке данных, которые непосредственно идут для внутренней аналитики, для кучи разных других вещей. Там много всего интересного, и не очень стандартизовано.
Алексей Рыбак: Понятно. Какую базу данных вы используете в первую очередь?
Максим Барышников: На текущий момент я бы сказал, что PostgreSQL. Но у «Танков», «Кораблей», «Самолетов» в качестве игровой базы используется MySQL. Но он используется довольно специфическим образом. Ничего нетрадиционного там нет, но он зачастую просто используется как key value storage.
Алексей Рыбак: Если можно, попробуем быстрее. Можно просто называть какие-то аббревиатуры, потом поговорим подробнее.
В качестве брокера очередей используете какие-то решения?
Максим Барышников: Kafka, RabbitMQ.
Алексей Рыбак: Какой-то distributed job processing?
Максим Барышников: Если питонячий, то довольно часто используется Celery но я упоминал Apache Ignite Data Grid (Прим. МБ: На Apache Ignite у нас построена инфраструктура для запускания лямбд, эдакий function-as-a-service, которые зачастую довольно удобно использовать для построения data processing pipelines. Некоторые проекты используют Alooma).
Алексей Рыбак: Для аналитической платформы есть какие-то стандартные компоненты: DWH, стриминг логов, хранение сырых данных, агрегированных данных для анализа?
Максим Барышников: Аналитика у нас – это достаточно большая дисциплина, совмещенная с Business Intelligence. Коротко в рамках блица я о ней не расскажу. Если захочешь, потом расскажу подробнее в рамках другой сессии. Там очень много всего серьезного, интересного про DWH и прочую аналитику.
Алексей Рыбак: Колоночные базы используете какие-то?
Максим Барышников: ClickHouse есть.
Алексей Рыбак: Хранилища логов типа Hadoop?
Максим Барышников: Hadoop есть.
Алексей Рыбак: Что ни спроси, всё есть!
Максим Барышников: Я бы предпочел, чтобы было не так, но оно, к сожалению, так.
Алексей Рыбак: Я так понимаю, у вас очень большая компания. Сколько человек работает?
Максим Барышников: Чуть меньше 5 тыс.
Алексей Рыбак: Это всё инженеры?
Максим Барышников: Нет. Давай поугадываешь. Как думаешь, какая часть инженеров из этих 5 тыс. человек?
Алексей Рыбак: Я не очень понимаю, кто остальные, но предположу, что саппортеры. Тогда скажу, что половина.
Максим Барышников: Чуть меньше половины, потому что в Wargaming, в отличие большинства компаний, разрабатывающих игры, еще включены функции паблишинга, маркетинга, вся эта красота. И помимо инженеров же еще большое количество художников, разных других артистов.
Алексей Рыбак: Да, под инженерами я имел в виду весь продакшен. То есть пару тысяч, да?
Максим Барышников: Да. Если художников и прочих людей в работающих над продуктом включить, то, думаю, и 3 тыс. точно будет.
Алексей Рыбак: В каких городах в основном вы сосредоточены?
Максим Барышников: Самые крупные центры – это Минск, Питер, Киев, Чикаго, Балтимор. Остальные поменьше.
Алексей Рыбак: Минск самый большой, наверное?
Максим Барышников: Да. В нем суммарно работает 2400 человек.
Алексей Рыбак: Круто! Свое железо, своя инфраструктура или используете какие-то публичные облака?
Максим Барышников: Свое железо. У нас есть технологический партнер, компания, которая занимается всей этой красотой и инфраструктурой – это G-Core Labs. У нас очень много своего железа. Опять же, я бы предпочёл, чтобы у нас было меньше своего железа, но об этом можем, наверное, потом чуть подробнее.
Алексей Рыбак: Интересно. Обычно те, у кого много железа, думают несколько иначе с точки зрения контроля и прочего. Но это долгий разговор.
Есть у вас какая-то основная операционная система?
Максим Барышников: Да, CentOS 7.
Алексей Рыбак: Понятно. Площадки, точки присутствия, дата-центры?
Максим Барышников: Европейские, Москва, несколько в Америке, в Китае (штуки четыре, наверное). Еще разбросаны. На самом деле, их несложно все увидеть. Если у нас просто пойти на нашу Wiki-страничку, там написаны локации серверов.
Алексей Рыбак: Когда ты делал доклад, ты рассказывал, что в различных городах ещё есть: в Новосибирске, Красноярске… Они остались до сих пор?
Максим Барышников: Остались. Добавились еще в Павлодаре. В Узбекистане добавилась одна периферийка. Еще несколько точек присутствия есть. Из некоторых мы переезжали по тем или иным причинам.
Алексей Рыбак: Последний кусочек про тестирование delivery, про всю вторую часть, пайплайны разработки. Что вы используете для раскатки кода, какие компоненты?
Максим Барышников: В разных командах оно может быть по-разному.
Алексей Рыбак: В вашей команде что используется?
Максим Барышников: В нашей команде самописно допиленная Fabric, для раскатывания приложений. А управление OS, раздеплоивание всего остального – Terraform и вся остальная красота. Но у нас отличие от «Танков» состоит в том, что нам надо деплоить не одно приложение, а целую длинную цепочку. В целом мое подразделение – это где-то 150 разных приложений. И мы вынуждены использовать release trains для того, чтобы у нас всё согласовано работало там, где это нужно. «Танкам» этого не требуется, и там они просто спокойно раскатывают билды по серверам, делают это во время downtime. Игровая индустрия может себе позволить сказать: «А теперь у нас мир останавливается на три часа. Мы раскатываем релиз». Это что касается конкретно игры.
Алексей Рыбак: Скажи, пожалуйста, насколько вы заморачиваетесь автоматизированным тестированием, насколько у вас это является основополагающей парадигмой? Пишете ли вы тесты? Если да, то какой code coverage считаете нормальным?
Максим Барышников: Это преимущественная парадигма. У нас практически не осталось исключительно ручных тестировщиков. Те, которые есть, потихонечку становятся автоматизаторами, потому что такой объем всего без автоматизированного тестирования просто невозможно вывезти. Это заняло бы бесконечное количество времени.
Алексей Рыбак: Но coverage, наверное, всё-таки не автоматизаторы делают, это же сценарии больше.
Максим Барышников: Конечно. Я просто говорю про функцию QA. То есть QA у нас преимущественно делают автоматические тесты. Тестов пишем много. Coverage не является у нас какой-то особенной метрикой, мы не гонимся за coverage. Нам не надо говорить: «Чтобы было покрыто 80%». Команды решают отдельно.
Если говорить про мое подразделение – я говорил про 150 сервисов, – в некоторых подразделениях команды решают, что хотят отслеживать coverage и делать его максимальным. Это отдано на откуп команде.
Алексей Рыбак: Соотношение в командах между инженерами, которые пишут код, и инженерами, которые занимаются QA, является ли метрикой, которая универсальна и за которой вы следите?
Максим Барышников: Нет, за соотношение мы не следим, хотя оно примерно 1 к 3. Просто я заметил из практики, что ты там ни делай и как ни организовывай эту работу, она всё равно остается 1 к 3.
Алексей Рыбак: Или 1 к 4?
Максим Барышников: Да.
Алексей Рыбак: Или, если у тебя микрокоманды, то 1 к 2 или 1 к 1, если совсем микро-микрокоманды?
Максим Барышников: На самом деле, есть микрокоманды, у которых вообще нет QA инженеров, где они полностью полагаются на свой coverage, на автоматические тесты, написанные раньше, или на интеграционные тесты, которые они всё равно вынуждены проходить, и релизы у них проходят QA-less в режиме. Такие тоже есть.
Архитектура
Алексей Рыбак: Хорошо. С таким блицем давай закончим. Теперь хочется углубиться непосредственно в архитектуру. У тебя есть совершенно замечательный доклад, который ты делал на конференции HighLoad. Но если мы попытаемся подробно осветить все эти темы, то у нас уйдет время всего нашего митапа. Мы это делать не будем. Попробую сам дать какое-то архитектурное summary, чтобы плясать от него. Но прежде чем я расскажу свою интерпретацию этого доклада, расскажи, пожалуйста, следующее.
Мне как человеку, который больше работал с более традиционными, не real-time архитектурами, кажется, что ключевое отличие игровых проектов в том, что вам нельзя «лагать», вы должны упарываться по сетевому стеку и прочим делам – по всему тому, что влияет на user experience в плане real-time. Потому что все остальные проекты, там есть какой-то элемент интерактивности – вебсокеты, какой-нибудь polling, но всё это чатики или карандашик, когда кто-то пишет друг другу, там «лаг» даже в секунду будет незаметной ерудой. У вас должны быть совершенно другие SLA и совершенно другие особенности. Помимо этой реалтаймности это что?
Максим Барышников: Реалтаймовость – это, безусловно, основное. Но я уже упоминал, что, например, ни один из веб-проектов не может позволить себе выключиться на время релизов, а игры могут. Это такое послабление.
Второе, за чем нам приходится пристально следить – это скорее специфика конкретно наших проектов, в смысле эти цепочки специфичны конкретно для наших проектов: из игры, от сервера, от клиентов начинается очень много разных цепочек обработки данных, событий и всего прочего. Приходится следить за тем, чтобы эти цепочки тоже функционировали правильно и надежно, чтобы у них не скапливались очередь. Там реалтаймовость постепенно начинает немножко терять в своей значимости, но она просто меняется в цифрах. Учитывая количество всех событий, которые приходят, даже минимальная задержка в работе одного сервиса моментально где-нибудь строит здоровенную пробку из данных. Это уже будет, так или иначе, заметно пользователю. За этим тоже пристально следим.
Алексей Рыбак: Вернемся к твоему докладу. Тезисно озвучу, как я понимаю вашу архитектуру на самом базовом уровне каких-то компонент.
Юзеры пришпилены к дата-центру жестко, играют в рамках одного дата-центра. У вас есть два режима взаимодействия юзера с системой. Один режим – это всякие настройки. Наверное, сюда же можно отнести оплату, всё, что не связано с игрой, и второй режим — игра. Всё, что связано с настройками, включая настройки внутри игры… Я так понимаю, что у вас есть режим ангара в игре. Я там выбираю оружие, танк… Я сейчас говорю про танк, хотя, как понимаю, ваша платформа работает не только для «Танков», но и для остальных проектов. Но пока оставим «Танки», и пример, который я хочу осветить, тоже будет про «Танки». За это всё отвечают демоны base-app. Дальше у вас есть понятие игры, есть арена. Я зашел в игру, я в этой арене. Эта арена также присобачена к дата-центру, на этой арене достаточно много танков. Арена – это какая-то карта, которая побита на какие-то кусочки. За каждый кусочек отвечает свой демон cell-app, который обсчитывает логику, какие-то сцены. Там есть довольно интересные проблемы пограничных кусочков карты, но мы этого не коснемся.
В этой модели всё ли правильно? Надо ли к этому что-то добавить?
Максим Барышников: На момент, когда я делал этот доклад, всё было так. Сейчас немного изменилась некоторая вводная в сторону упрощения, но в целом всё так.
Алексей Рыбак: Мне и как пользователю, и с точки зрения архитектуры, всегда хочется понять, как те или иные сценарии задействуют те или иные компоненты, и какие байты в каком случае куда едут.
Давай представим себе ситуацию. Мы с тобой играем в одной игре. Мы на одной сцене на одной ячейке. Ты стреляешь в меня и, например, попадаешь в меня. Две точки во времени. Первая – ты стрельнул, второе – ты попал. Какие байты куда у кого поехали, и какие компоненты были задействованы?
Максим Барышников: Во-первых, сервер «авторитарный». То есть фактически и ты, и я находимся в одном времени, в одном пространстве, и всё это происходит более-менее так же, как это происходило в физическом времени. Сейчас я выстрелил. В рамках этого же процесса – оставим кусочки пока за кадром – сервер уже сам посчитал, куда это полетело, когда это попало, куда попало, и так далее.
Интересное начинается, если рассматривать всю ситуацию с точки зрения меня, сидящего за компом, и тебя, сидящего за компом в соседнем городе. Получается, от тебя идет input, от меня идет input. Я нажал кнопку выстрела, и от меня полетели байтики, вектор input в сервер. Он туда прилетел. Там сработал выстрел.
В это время и к тебе, и ко мне одновременно из одного и того же пространства сейчас идут байтики обратно, которые транслируют состояние дел.
Алексей Рыбак: Что это за байты? Пока не понимаю, что передается. Когда ты стреляешь, передается что?
Максим Барышников: На самом деле, с выстрелом всё менее интересно с инженерной точки зрения, чем с положениями, чем с координатами танков. Про выстрелы тебе рассказать или про координаты, про байтики?
Алексей Рыбак: Про всё. Я не понимаю, что едет. Я так понимаю, что должно быть какое-то представление сцены и действий.
Максим Барышников: Да. Представление сцены передавать по сети не нужно. В смысле ее геометрию и прочее. Она у тебя есть на клиенте, точно такое же, как и на сервере.
Алексей Рыбак: То есть мне нужно обеспечить очень быстрый протокол и обмен сообщениями об изменениях, которые переобсчитываются на моем локальном компьютере.
Максим Барышников: Да.
Алексей Рыбак: Ты говорил, что какие-то сцены обсчитываются непосредственно на сервере. Что там обсчитывается?
Максим Барышников: Геометрия сцены… геометрию карты тебе передавать не надо, она у тебя есть на компьютере, не меняется. Что тебе надо передавать с сервера? Всё считается на сервере. На клиенте ничего не считается. Клиент – это своеобразный плеер.
Алексей Рыбак: То есть вся картинка, вся 3D-отрисовка?..
Максим Барышников: Нет. Вся картина и вся 3D-отрисовка считается на клиенте, безусловно. Но весь геймплей, все положения объектов, геймплейное взаимодействие между ними и так далее, всё на сервере. То есть, грубо говоря, ты не можешь никак подхачивать свой клиент, чтобы у тебя увеличилось здоровье или еще что-нибудь. Но картинка, безусловно, рисуется на твоем клиенте. Есть, на самом деле, танки через GeForce Now (Прим. АР: NVidia cloud gaming service), где ты получаешь видеострим, но это другая история.
Алексей Рыбак: Правильно ли я понимаю, что пока мы играем на сервере, в основном нагружается проц, ну, может быть, еще память? То есть это такая CPU-bound архитектура?
Максим Барышников: Верно.
Алексей Рыбак: И дальше ты говорил в своих докладах, что вы каким-то образом бэкапируетесь. В целом для геймплея какой подход к high availability используете? Есть ли у вас балансеры, как балансируют? И так далее.
Максим Барышников: Вот что я говорил про бэкапирование в рамках того доклада. Поскольку у нас несколько процессов считают один и тот же бой, то теория high availability нам говорит, что любой из этих процессов может помереть, и нам надо решать, что делать в этой ситуации. Первоначальная стратегия, под которую делался движок, было то, что любой из процессов выпадает, и кластер должен суметь восстановиться с данных, какими они были до выпадения этого процесса, включая те данные, которые хранились на этом процессе. Поэтому этот процесс, который потенциально в нашей модельке выпал, он бэкап своих данных делает на соседей.
Алексей Рыбак: Давай представим себе совершенно упрощенный случай. Вся карта – это один cell, фактор репликации – условно говоря, два. У вас будет два процесса на серверной стороне?
Максим Барышников: У нас всегда будет одинаковое количество процессов на серверной стороне, сколько мы сконфигурируем, но просто на каждом процессе будет разное количество объектов, условно.Допустим, для простоты у нас есть два процесса, на всем кластере играет один бой. Один из процессов обсчитывает текущий бой. И по механизмам бэкапирования он все свои данные будет бэкапить на соседние.
Алексей Рыбак: Второй при этом ничего не считает, клиентов не обслуживает?
Максим Барышников: Да. Но если боя становится два, то на втором тоже запустится бой, он будет считать бой, обслуживать клиентов, а бэкапить свои данные на первый процесс.
Алексей Рыбак: Общение между этими двумя процессами… У вас фактор репликации какой?
Максим Барышников: (Прим МБ: Я так и не ответил на вопрос про фактор репликации, в описанном случае он равен единице.) Следующая картина сейчас. Мы пришли к тому, что мы сейчас любой бой можем считать на одном процессе жестко, не заморачиваясь с этими кусочками. Это первое изменение относительно времени того доклада. Процессоры стали существенно быстрее, а карты у нас так и не стали бесконечными. Это геймплейно не получалось, и в этом не было большого смысла. Поэтому мы сейчас на каждом ядре современного серверного можем обслуживать 3-4 боя легко, без проблем.
Второе. В рамках того доклада я говорил, что когда мы считаем, что мы бой безнадежно потеряли, то мы просто его убиваем, и люди начинают следующий бой, как будто ничего и не бывало. В рамках таких допущений то, что происходит конкретно в рамках одного боя, мы по-прежнему можем не бэкапить. Мы это пока еще делаем, но думаем о том, чтобы, если мы уже всё равно жертвуем боем в определенных условиях, то зачем нам его целиком бэкапить?
Алексей Рыбак: Я тогда не понимаю. Я так понял, что это ваше требование, чтобы в случае, если процесс умер, вы возобновили этот бой достаточно быстро. Как улучшение процессоров снизило риск того, что процесс может умереть, и у вас будет страдать пользовательский опыт?
Максим Барышников: Сам бой – особенно сейчас, он стал короче, 5-6 минут, может быть, 7, – пользователь в одну сессию играет 20-30 боев, а некоторые больше. Наши товарищи гейм-дизайнеры и прочие дизайнеры игры решили, что если один бой по каким-то причинам безвозвратно утерян, то лучше его похоронить совсем, чем пытаться восстановить.
Алексей Рыбак: И в этой ситуации вы говорите, что «Нам не нужно бэкапить. Если пользователь не расстроился и не ушел, то он еще раз запустит бой, бой будет на нормальном процессе».
Максим Барышников: Да.
Алексей Рыбак: Я теперь примерно понимаю, как это работает. Между различными компонентами есть ли у вас какая-то балансировка? Если есть, то что используете?
Максим Барышников: Да. Можно, еще к предыдущему пункту? Это как раз та ситуация, когда инженеры говорят: «Но как же? Мы же можем сделать, чтобы всё не потерялось», но гейм-дизайнеры говорят: «Всё и так хорошо. Не надо».
Балансировка нагрузки, безусловно, есть. Когда ты разбирался с этим докладом, там есть два специальных процесса – один BaseApp Manager, второй CellApp Manager – которые менеджерят свои типы процессов. Заодно они выступают load balancers в некотором смысле. То есть base manager знает всегда всю информацию о всех процессах base, их загрузку, количество cущностей, которые там запущены и работают (Прим. АР: танки, игроки и прочее), и они могут выступать в роли балансеров. То есть когда мне нужно подключить нового пользователя, именно BaseAppMgr решит, в каком конкретно процессе поднять эту сущность. Когда мне нужно создать еще какой-нибудь геймплейный объект и так далее, код фреймворка написан там таким образом, что ты просто говоришь: «Создай мне объект и скажи, где он создался», и всё. И балансировщик уже решает, где его создать.
На одной карте, если мы говорим про бой, там сотни объектов, если не больше. И каждый из них – это отдельный instance питонячьего в нашем случае класса с C++ бэкграундом, который создается на каком-либо процессе. И ты прозрачно работаешь с ним по сети – опять же, код так написан, – что тебе не надо знать, где он, а всё за тебя этот фреймворк обработает.
Алексей Рыбак: Всё самописное, правильно понимаю? Я так понимаю, что поскольку у вас сетевой стек свой, то всё остальное, что касается балансировки, там тоже свое?
Максим Барышников: Да. Безусловно, там много достаточно традиционных алгоритмов и очень традиционной и хорошо описанной математики, но реализация своя.
Алексей Рыбак: В жизни любого проекта есть определенные архитектурные боли, которые нельзя исправить очень быстро, они живут и потихонечку уменьшаются или, наоборот, увеличиваются. Можешь рассказать 1-2 кейса, что у вас было такого, и как вы порешали?
Максим Барышников: В случае конкретно «Танков», наверное, самая большая архитектурная боль – что кластер целостный. Он вот такой, и всё. От этого он достаточно хрупкий. Может случаться… Сейчас поясню почему.
В прошлом вопросе я рассказывал, что фреймворк сам тебе даст объект, и тебе неважно, где он находится и как обсчитывается. Безусловно, это провоцирует программистов начинать об этом забывать. В итоге, когда ты точно знаешь, что тебе надо объект не где-нибудь на кластере, а здесь рядом, потому что это условно сервисная штука и так далее, ты всё равно пишешь код: «Создай мне объект где-нибудь». Он для тебя выглядит как локальный, ты работаешь с ним как с локальным, но на самом деле он работает в кластере. В итоге потенциально порождается большое количество неэффективностей. Тебе надо маленький сервисный объектик, а он у тебя обсчитывается на соседней машине, это требуется дополнительная сеть и прочее, и делает заодно немасштабируемой и неделимой твою логику.
Алексей Рыбак: Проблема похожа на ORM.
Максим Барышников: Да, она имеет что-то общее в своей философской части с ORM. Я большой противник ORM вообще. Вернее не ORM, а тем, чтобы им пользовались, не зная, как работает база данных.
Алексей Рыбак: ОК, про архитектурную боль рассказал. Как это решать?
Максим Барышников: В конкретном случае «Танков» концептуально архитектуру принято решение не переделывать. Она такая есть. Я уже упоминал, более 10 млн строк кода, всё это переделать невозможно. Поэтому существуют определенные практики, правила, чем надо пользоваться и так далее, которые обязаны знать все серверные программисты – у клиентских всё проще, – и за которые жестко бьют на code review.
Алексей Рыбак: Понятно. Следующий вопрос. Я понимаю, что команда большая, но как тебе кажется, ядреные компоненты, если бы делали всё с нуля, что бы сделали иначе? Может быть, отказались от каких-то базовых компонент?
Максим Барышников: Уточняющий вопрос. С теми знаниями, которые есть сейчас, в то время, когда начинались «Танки», или сейчас с теми занниями, что есть сейчас?..
Алексей Рыбак: (смеется) Это очень программистский вопрос, невероятно программистский. Прямо сейчас и с теми знаниями, которые есть сейчас. Я спрашиваю для того, чтобы люди получили какой-то опыт, а не для умственного упражнения.
Максим Барышников: С теми знаниями, которые есть сейчас, с тем опытом разработки и оперирования тем продуктом, который есть сейчас, а главное, с теми технологиями, которые есть сейчас… То есть тут не всё 100% зависит от конкретно тебя. С теми технологиями, которые есть сейчас, не стали бы делать монолитный кластер. Сделали бы жесткое разделение между этой ангарной условно частью и боевой, чтобы можно было боевую условно запускать отдельно от кластера.
Алексей Рыбак: Есть определенные зависимости, и даже в момент боя какой-то обмен информациями между и cell-app, и base-app приходится тягать, да?
Максим Барышников: Два соседних боя никак не взаимодействуют. Они по идее могут быть разделены. Игроки во время боя тоже эксклюзивно заняты. В теории можно запустить бой так, чтобы минимизировать латенси тем игрокам, которые в нем сейчас собраны; или можно просто их собрать по географическому признаку, и запустить бой рядом с ними на другом конце мира. В 2008-2009 году это было сделать невозможно с точки зрения мировой инфраструктуры, облачных провайдеров и всего прочего. У тех в принципе так не получалось. Это раз. Во-вторых, сам BigWorld дизайнился с идеей бесконечного continuous мира. По-моему, он был воодушевлен играми а-ля Ultima Online, хотя Ultima, по-моему, не была первой такой игрой. Но Ultima-подобные игры на BigWorld делать вообще волшебно.
Алексей Рыбак: Ты упомянул про алгоритмы. Я правильно понимаю, что алгоритмы в основном в обмене данных, в сетевых сообщениях. Можешь рассказать, что здесь самое алгоритмически «челленджовое»?
Максим Барышников: Много алгоритмического «челленджа» как раз в сетевой части, в части трафика от сервера к клиенту. Напомню, в каком году это вышло, и с сетями всё было довольно плохо. И там надо было извращаться максимально, чтобы передавать максимум информации в минимальный трафик. При этом еще желательно без всяких задержек, быть толерантными к потере пакетов.
Например, кодирование координат. Это алгоритм, где в фиксированном векторе ты передаешь набор координат относительно какой-то опорной точки, координаты кодируются в порядок и мантиссу относительно опорной точки. И чем больше у тебя значение координаты, тем меньше у тебя получается точность. Мы всегда передаем координаты относительно самого игрока, что логично, потому что чем ближе к нему… Чем ближе к ишроку, тем чаще мы передаем координаты, и поведение объекта получается плавнее и точнее. Чем дальше, тем реже у него происходят эти обновления, и уменьшается точность позиционирования.
Алексей Рыбак: Я правильно понимаю, что вы должны передать какой-то пакет информации, в котором будет написан объект и его новая координата? На уровне протокола, сетевого стека. Там, наверное, не TСP, а UDP?
Максим Барышников: Да.
Алексей Рыбак: В одном UDP что едет?
Максим Барышников: В одном фрейме UDP что едет? Те объекты, которые находятся в area of interest этого игрока. Там тоже есть концепция, чтобы отсекать что-то ненужное (Прим. МБ: Level of Details (LoD) примененный к сети.).
Алексей Рыбак: Скажи, пожалуйста, это число объектов, которые прилетают в одном пакете, чем ограничено?
Максим Барышников: Ограничено MTU, во-первых. Мы стараемся не вылезать за MTU. Учитывая, что это UDP, то фрагментированный UDP по сети ходит довольно плохо, поэтому мы не вылезаем за MTU. А дальше апдейты внутри приоритезированы в соответствии с логикой. То есть даже если кто-то пропускает свой черед отправить координаты из-за того, что мы в MTU уперлись, то он всё равно отправится в следующем пакете. Это позволяет достаточно надежно передавать всю эту информацию. И мы несколько раз даже расширяли точность координат, потому что видели, что мы перестарались слегка, и у нас полпакета уходит пустым.
Алексей Рыбак: Сейчас давайте тогда объявим небольшую сессию вопросов и комментариев.
Алексей Рыбак: Фрол Крючков, пожалуйста, подключайтесь.
Фрол Крючков: Спасибо большое за рассказ.
Алексей Рыбак: Друзья, еще кто-то хочет присоединиться? Прошу вас, Фрол.
Фрол Крючков: У меня достаточно примитивные вопросы. С учетом болей архитектуры как долго происходит онбоардинг новых ребят? С учетом того, что нужно понять, какие есть текущие ограничения использования тех или иных сущностей в коде, чтобы что-то не положить случайно.
Второй момент, исключительно любительский. А где используется Erlang, и в какой части всего этого стека?
Максим Барышников: Хорошо. Расскажу сейчас по очереди. Полный онбоардинг, во-первых, отличается в разных командах. В серверной команде, онбоардинг наиболее сложный, занимает до трех месяцев. Но где-то через 2-3 недели человек способен писать вполне себе приличные вещи.
Поскольку кода очень много, то программист в серверной команде местами не добирается до каких-то подсистем и кусков месяцами, а то, может быть, и годами. Но с базовой точки зрения фреймворк достаточно удобный. То есть надо понять не так много концепций, которые мы, например, проговорили сейчас в разговоре за чуть меньше часа. Немногим больше надо, чтобы рассказать больше деталей, и показать, где какая документация. А уже там, зная базовые концепции, всё становится довольно просто, и начинаешь экспериментировать. Недели через 2-3 есть хорошие шансы пройти с какой-нибудь фичей в code review. Система сложная, но мы этот процесс уже вылизали достаточно серьезно, и он проходит относительно безболезненно.
Некоторые сложности вызывает то, что много кода у нас пишется на Python, соответственно, мы берем питонщика. А у нас есть отличие от традиционного писания на Python – хотя бы то, что у нас выключен полностью garbage collector. Все эти требования, реалтаймовские жесткие ограничения по квантам времени, оно не позволяет нам иметь garbage collector. Поэтому, когда ты пишешь код на Python, то должен сам следить за памятью… в смысле за тем, чтобы у тебя объекты никуда не утекли.
Алексей Рыбак: А в Python разве как-то можно управлять, очистить что-то? Или там просто нужно аккуратно писать и переиспользовать?
Максим Барышников: Как работает garbage collector? Если у тебя что-то выходит за scope и на него нет референсов… Условно, refcount у него был 1, то он автоматически освобождается без ухода в дальнейшее поколение. Этого тебе достаточно. Ты не можешь напрямую взять и освободить память, занимаемую этим объектом, но тебе надо следить за его refcount.
Алексей Рыбак: ОК. Не плодить ссылок, не терять.
Максим Барышников: Вопрос про Erlang. Самый большой проект на Erlang – как ни странно, у нас это чат. Написать чат, в который у тебя подключится весь танковый онлайн, особенно в 2010 году это была крайне нетривиальная история. Сначала взяли ejabberd, потом он очень быстро кончился по памяти. Потом его очень долго доделывали до тех пор, пока от ejabberd ничего не осталось. Но вообще у нас самый большой проект на Erlang – это чат. Но есть еще приличное количество других.
Алексей Рыбак: Снова руку поднимает Фрол. Прошу вас.
Фрол Крючков: А performance тестирование производится каким-нибудь образом, и были ли инциденты, когда какие-то неаккуратные действия привели к тому, что просела производительность?
Максим Барышников: Performance – это 80-90% всех наших разных инцидентов, которые происходят в продакшене (если происходят). Ну, ладно, может быть, меньше, так как еще кусок инфраструктуры.
Функциональное тестирование легко делается, и в «Танках» оно вообще на высочайшем уровне. Я редко где видел такой уровень функционального, приемочного, всякого другого тестирования. А performance тестов вообще невозможно сделать достаточное количество, потому что пользователи всё равно будут вести себя не так, как ты тестировал. Поэтому performance тесты делаются в большом количестве в разных позах, в разных системах, в том числе в продакшене. Для этого есть даже несколько серверов PTS (Public Test Server), в которых заодно тестируется и performance. Но всё равно performance зачастую является тем камнем, о который мы, так или иначе, спотыкаемся или о котором заботимся чаще всего. Причем довольно часто проблемы performance не наши на нас влияют, а партнеров, инфраструктуры и так далее. В Новый год, например, прилегло приличное количество банков, когда принимали наши платежи.
Фрол Крючков: Спасибо большое.
Алексей Рыбак: Ребята, есть ли у кого-нибудь еще вопросы? Пока вопросов нет.
У меня есть вопрос. Ты рассказал про прошлое. А про будущее, уже, может быть, в твоей команде: какие самые интересные архитектурные проекты вы сейчас делаете или собираетесь делать?
Максим Барышников: Много есть интересного относительно gamedev и технологии gamedev, которая происходит внутри компании. К сожалению, не так много всего про них могу рассказать.
Алексей Рыбак: Что можешь.
Максим Барышников: В моей команде основная боль и проблема состоит в том, как она развивалась. По сути, у меня подразделение занимается backend-as-a-service, игровая платформа или игровой бэкенд. Чтобы все те вещи, которые во всех играх одинаковые – виртуальные валюты, инвентарь, хранение профиля игрока, аккаунты, legal compliance и так далее, – чтобы оно предоставлялось в игре: «Вот тебе, чувак, набор API и SDK. Бери и делай игру. А про всё остальное мы уже позаботились».
Исторически, поскольку Wargaming, его основная сфера деятельности – это все-таки делать игры, а не игровые платформы, то это был всегда второстепенный вопрос, и он всегда шел следом. Были танки, они взлетели очень быстро именно что мощно, поэтому там люди выкручивались как могли, делали какие-то сервисы конкретно под ситуацию и так далее. Именно поэтому их сейчас больше 150, потому что некоторые из них еще работают с тех самых времен. Я бы с радостью их прибил, да не могу. И всё это обвязано такой паутиной.
Основная проблема конкретно моей организации в том, что очень большая фрагментация, отсутствует правильное сцепление. Оно скорее везде связанность. И постепенно потихоньку-помаленьку развязывается этот клубок. Наверное, это самый большой челлендж, который есть у моих архитекторов, и при этом чтобы всё продолжало работать, чтобы никому из игр не надо было ничего переделывать, чтобы интерфейсы сохранялись, поведение сохранялось, данные сохранялись. Но этот клубок распутывать. На самом деле, они делают хорошую работу. За последний год много чего было в этом сделано. Но это, может быть, не очень инженерный, ну, не только инженерный челлендж, но и самая большая моя одновременно и боль, и одновременно прогресс за последний год.
Алексей Рыбак: Понятно. Скажи, пожалуйста, у вас очень специфические задачи, но, может быть, есть какие-то компоненты, которые вы открываете, которыми можно воспользоваться, может быть, что-то типа open source проектов?
Максим Барышников: Есть и достаточно приличного. Но перед тем, как рассказать, что там есть, расскажу тебе следующую историю.
Инженеры всегда за open source. Прекрасно. Мы говорим: «Ребята, мы согласны выкладывать в open source, мы всё это организуем. Что выкладывать?». Они такие: «Вот у меня здесь есть клевый кусок, но там код некрасивый. Дайте мне немножко времени, я его причешу. Мне как-то стыдновато немного перед людьми». Такие: «Ну, ладно, чувак, хорошо. Как будет готов, приходи». Кто-то все-таки приходит, но большинство после этого: «Я еще делаю», «Мне еще вот здесь не нравится», «Да меня же засмеют вот тут», «Мне надо поменять названия переменных, потому что я был не в настроении и коллег троллил», или еще что-нибудь. То есть до фазы, когда люди реально говорят: «Смотрите, эта штука имеет смысл не только в рамках моего проекта, а еще для остальных людей. Давайте поделимся», не очень много.
Но у нас есть open source policy, которое требует как минимум двух мейнтейнеров внутри у этого компонента. Потому что мы прекрасно понимаем, что если что-то отдать в open source, не дай бог этим начнут пользоваться. Если ты не будешь продолжать это мейнтейнить, то ты нанесешь скорее вред, нежели пользу open source, тем самым.
Найти двух мейнтейнеров в той же самой команде, или любых, необязательно в той же самой команде, людей, которые готовы этому посвящать время, на этом отваливается еще кусок, тех вещей, которые я бы, например, хотел видеть в open source, которые были бы мне полезны, если бы я был не в Wargaming.
Алексей Рыбак: Может быть, ты назовешь сейчас что-то?
Максим Барышников: Есть какое-то количество разнообразных библиотек Python, которые выложены на GitHub в аккаунте Wargaming.
Алексей Рыбак: Да. У вас есть аккаунт Wargaming на GitHub, заходишь и смотришь?
Максим Барышников: Да (Прим. АР: github.com/wgnet). Там есть библиотеки Erlang и еще какие-то. Но, на мой вкус, там гораздо меньше, чем Wargaming мог бы с гордостью поделиться.
Алексей Рыбак: Может быть, вы используете какое-то чье-то большое раньше, которое тоже open source, и как-то туда контрибьютите? Есть такая коллаборация, и кто-то из инженеров участвует в таких проектах?
Максим Барышников: Да, мы контрибьютим достаточно много. В PostgreSQL контрибьтили. Но там своеобразным образом. Мы спонсировали разработку определенных вещей командой PostgreSQL Professional. Еще мы недавно в ClickHouse законтрибьютили SQL Slave (00:54:01). Это из относительно недавнего. Такого рода вещи.
Алексей Рыбак: Это уже в рамках как раз аналитической команды и ребят, которые занимаются работой по DWH?
Максим Барышников: Местами. На самом деле, та штука, из-за которой нам понадобилось делать к нему SQL Slave – это snapshots состояния аккаунтов, чтобы можно было дать людям… Иногда люди творят чернь вечером за пивом, а на следующий день говорят: «Слушайте, а можно меня во вчера отодвинуть? А то я себе стату испортил и все деньги потратил». Для этого надо хранить слепки аккаунтов. Мы их хранили в MySQL, но оно у нас там занимало приличное место, мешало оперейшену и так далее, и решили переложить это в ClickHouse. Менять код, который создавал эти слепки и все прочее, у нас не получалось по календарным соображениям. Поэтому один из наших DBA психанул и написал SQL Slave для ClickHouse, где стал реплицировать эту штуку, куда «Танки» продолжали писать эти аккаунты в ClickHouse, и всё. Таким образом решил эту проблему.
Алексей Рыбак: Понятно. У нас есть еще вопросы в чате Youtube. Тут два вопроса, они разбиты на несколько сообщений. Первый вопрос от Алекса Лурье. «Я не играл в "Танки", поэтому, возможно, у вас такого нет. Что происходит, если во время у вас происходит какая-то транзакция, например, тратится какой-то расходник, а бой падает? Откатываете ли вы транзакции в этом случае? Какая связь между транзакционной базой и состоянием боя, и есть ли она?».
Максим Барышников: С точки зрения тех вещей, которые влияют на состояние аккаунта, с учетом того, что мы согласны пожертвовать боем… То есть у нас бой с точки зрения изменения аккаунтов обернут в одну большую транзакцию. По завершению боя, когда мы знаем, что уже произошло и так далее, сам бой на аккаунт присылает своеобразный кусочек WAL (write ahead log), то есть который эплаится (apply) на аккаунт только в случае успешного завершения боя. Таким образом получается относительно консистентное поведение. Если мы бой потеряли, то ОК, для аккаунта это как будто его и не было в принципе.
Алексей Рыбак: Еще вопрос от Кирилла Пальцева: «Вы сказали, что обсчета логики на стороне клиента не ведется». Я так понимаю, что это ошибка. Может быть, ты оговорился. Потому что я тоже один раз так понял, поэтому переспросил. «Насколько я слышал, большинство игр стараются минимизировать latency путем дублирования логики на клиенте, в некоторых случаях даже упрощая ее. Использовали ли вы раньше этот подход или есть подобные мысли использовать в будущем?». Я так понял, это просто оговорка. Прокомментируй.
Максим Барышников: На клиенте логика никакая не считается, в том смысле, что ты не можешь никак повлиять на состояние…
Алексей Рыбак: Ты не можешь ее подделать. Но все 3D-модели и прочие вещи отрисованы будут на клиенте.
Максим Барышников: Да. Расчеты на клиенте никак не влияют на состояние дел на сервере. Но, действительно, на клиенте часто дублируют исполнение логики для того, чтобы, например, бороться с лагом или еще что-нибудь. Допустим, клиент знает о намерении ехать вперед, грубо говоря. Он отправляет намерение на сервер. И чтобы не дожидаться, пока сервер скажет, что «Действительно, поехали», он предполагает, что сервер посчитает примерно вот так, и получает координаты, корректирует. Это обычный подход в играх. Он немножко сложнее по сути, но суть такая. Но в «Танках» сейчас это не применяется. Почему? Потому что конкретно «Танки» – это очень медленные машины, там ты не замечаешь этого лага. Это просто там не нужно. Это излишняя инженерия. Конкретно в контексте игры про Танки. То есть инженеры, безусловно, владеют всеми этими техниками, и есть реализация. Но в танках это просто не применяется.
Алексей Рыбак: Понятно. Переходим к следующей части – поговорим про менеджмент.
Вторая часть про управление будет опубликована в самое ближайшее время.
Полную видео версию беседы можно посмотреть на YouTube:
Небольшой анонс: 24-го апреля будет новый онлайн-митап MySQL@Scale c экспертами из Avito, Badoo, ECOMMPAY и ProxySQL и на него ещё есть места. Ещё у нас есть достаточно популярная фейсбук-группа "Управление и разработка крупных IT-проектов", а также телеграм-канал feedmeto, где мы делимся интересными публикациями из корпоративных (преимущественно забугорных) техноблогов.
Автор: Рыбак Алексей