Так рождаются мемы… Под гром аплодисментов! Обзор Lego Star Wars: Skywalker Saga для Nintendo Switch / Компьютерные и мобильные игры / iXBT Live

Так рождаются мемы... Под гром аплодисментов! Обзор Lego Star Wars: Skywalker Saga для Nintendo Switch / Компьютерные и мобильные игры / iXBT Live Роботы
Содержание
  1. Итог — что выбирать
  2. Что делать, если и компенсация не работает
  3. Что же такое buffer?
  4. Acid — без изоляции
  5. Attached fork
  6. Detached fork
  7. Fork модель
  8. Push pull
  9. Аварии
  10. Балансировка нагрузки
  11. Блокировки
  12. Блокирующие вызовы
  13. Больше гибкости
  14. Версионность api
  15. Вместо зключения
  16. Еще один сценарий с аварией
  17. Избегать паразитных нагрузок
  18. Избегать сложной логики избыточной функциональности в сервисе саг
  19. Интегрироваться с клиентами
  20. Как это все проверить
  21. Как это работает
  22. Каналы
  23. Когда нужен лок
  24. Компенсация при авариях
  25. Компенсация транзакций: как это работает
  26. Масштабирование саг
  27. Мемы! абсолютные мемы!
  28. Мониторинг
  29. Не блокирующие вызовы
  30. Не пробуйте. меняйте. или не меняйте. никаких пробуйте
  31. Отсутствие изоляции
  32. Параллельность
  33. По получению результата вызова функций
  34. По порядку вызова функций
  35. Подробнее о channel
  36. Подробнее об actionchannel
  37. Подробнее об eventchannel
  38. Пример логики генерации саги, которую можно скрыть в клиентской библиотеке
  39. Проблема модели watch-and-fork
  40. Реализация оркестрируемой саги в виде сервиса pg saga
  41. Синхронизация по сокетам
  42. Теория. максимально кратко
  43. Трейсинг по saga call id
  44. Фанат, поверь в лего, положись на него
  45. Финальная мысль
  46. Я ощущаю возмущение в балансе игры

Итог — что выбирать

Сагу-оркестратор мы используем как возможность рефакторинга legacy кода. Но если бы была возможность написания всего с нуля, мы использовали бы хореографическую сагу (в том числе в планах реализация паттерна «хореограф» и перевода на него части функциональности но это уже другая история и там есть свои нюансы).

Что тут является «узким горлышком»? Если вы хотите как-то поменять сагу, вам надо пойти к команде, которая владеет этой сагой, договориться о том, что вы собираетесь что-то менять, потому что у нее в том числе тесты могут быть. Но в любом случае вам, как минимум, надо с ними договориться или законтрибьютить в их сервис тот код, который теперь будет генерировать сагу с вашим дополнительным шагом.

Смотрите про коптеры:  Робот пылесос xiaomi mi robot vacuum mop и essential сравнить

И таким образом получается, что это достаточно «узкое горлышко», потому что очень плохо масштабируется, если у вас очень много команд и различной бизнес-логики. Это минус. Плюс — это удобство переноса текущей функциональности. Все потому, что мы можем заранее жестче тестировать и гарантировать порядок выполнения.

Я за прагматичный подход к разработке, поэтому для написания сервиса саг, инвестиция в написание такого сервиса должна быть оправдана. Более того, вероятнее всего, многим нужна только часть из того, что я описал, и эта часть решит текущие потребности. Главное, заранее понять, что именно из всего этого нужно. И как много ресурсов у вас есть.

Что делать, если и компенсация не работает

Очевидно, что надо действовать по примерно такому же сценарию. Опять применять retry, идемпотентные ключи для компенсирующих транзакций, но если ничего не выходит и в этот раз, например, сервис не доступен, надо обратиться к сервису-владельцу саги, сообщая, что сага сфейлилась.

Что еще важно: представьте, что какой-нибудь шаг сервиса саги недоступен. Наверняка же еще инициатор этих действий будет делать какие-то retry. И в итоге, ваш сервис саг делает первый шаг, второй шаг, а его исполнитель недоступен, вы отменяете второй шаг, отменяете первый шаг, а еще могут возникнуть аномалии, связанные с отсутствием изоляции. В общем, сервис саг в этой ситуации занимается бесполезной работой, которая еще порождает нагрузку и ошибки.

Как надо делать? Healthchecker должен опросить сервисы, которые выполняют шаги саг, и посмотреть, работают ли они. Если сервис стал не доступен, то есть два пути: саги, которые в работе, — компенсировать, а новые саги — либо не давать создать новые экземпляры (вызовы), либо создавать, не беря их в работу executer’ом, чтобы сервис не занимался лишними действиями.

Смотрите про коптеры:  Квадрокоптер Eachine E520 и Eachine E520S - клон Mavic - Все о квадрокоптерах | PROFPV.RU

Что же такое buffer?

Заслуживает внимания, так как каждый канал имеет базовый буфер, при помощи него и формируется очередь на обработку.

Пример создания буфера:

import { buffers } from 'redux-saga'

const buffer = buffers.sliding(5);

buffers — это instance фабрики по созданию буферов с различными стратегиями.

Всего 5 стратегий, им соответствуют методы:

Acid — без изоляции

У нас есть атомарность, поскольку все шаги либо выполнятся, либо компенсируются. Есть консистентность за счет сервиса саг и локальных хранилищ в сервисе саг. И устойчивость — благодаря локальным хранилищам и их механизмам durability. Изоляции у нас нет.

$Ti reads =

Могут происходит грязные чтения — когда вы в процессе выполняете какую-то сагу, сделали что-то одно, записали, кто-то эти изменения уже прочитал, а ваша сага еще не закончена. Вы записываете еще раз, что-то меняете, а кто-то прочитает неправильные состояния.

$Ti writes =

Случаются неповторяющиеся чтения — когда вы в течение одной и той же саги будете получать разные состояния вашего объекта.

$Ti reads =

Как этого избежать:

  1. Работать с версией объекта, держать некую версию, например, у пользователя, и ее инкрементировать при каждом изменении.
  2. Проверять, что вы все еще работаете с ней же. Или же смотреть то состояние, которое вы хотите поменять, например статус, и следить, что вы применяете его к тому самому статусу, который до этого хотели сменить.
  3. Можно выстроить блокировки и сериализовывать все изменения вокруг главного объекта саги.
  4. Передавать в payload саги только события и не работать с состоянием. Эта история об eventual consistency — если вы передадите состояние объявления сервису пользователей, может быть, оно уже поменяется к тому моменту, когда событие дойдет до адресата. Нужно передавать информацию о том, что произошла, например, регистрация пользователей или мы применили пользователю премиум-услугу.

Attached fork

Прикрепленные (Attached) следуют 3 правилам:

Detached fork

Открепленные (Detached) форки живут в собственных контекстах исполнения. Родители не ожидают их завершения. Не пойманные ошибки из spawn задач не всплывают в родительский контекст. Отмена родителя не выполняет автоматическую отмену открепленного форка (их нужно отменять явно).

Fork модель

В redux-saga фоновые задачи могут выполняться в 2 режимах:

Push pull

В случае с takeEvery, обработчик просто вызывается каждый раз, при этом нельзя контролировать какие-то исключительные случаи, когда запускать задачу не нужно, или когда следует прекратить наблюдение.

В случае с take, контроль инвертирован. Вместо того, чтобы запушить action в обработчик, сага самостоятельно обрабатывает action.

Данный инвертированный подход позволяет контролиловать процесс выполнения и решать нетривиальные задачи.

Аварии

В любом сервисе могут произойти аварии, но есть известные приемы, как к ним подготовиться. В распределенной системе об этих приемах знать важно. Например, одно из самых важных ограничений — сеть не всегда надежна. Подходы, которые позволят решить проблемы взаимодействия в распределенных системах:

  1. Делаем retry.
  2. Маркируем каждую операцию идемпотентным ключом. Это нужно, чтобы избежать дублирования операций. Больше об идемпотентных ключах можно прочитать в этом материале.
  3. Компенсируем транзакции — действие, характерное для саг.

Балансировка нагрузки

Допустим, требуется ограничить количество одновременно выполняемых процессов 3 штуками. Если есть свободный процесс, то задача должна немедленно выполниться свободным процессом, иначе задача помещается в очередь и будет обработана первым освободившимся процессом.

С помощью каналов это решается так:

В примере выше мы создали канал с помощью фабрики каналов. Мы получили канал, который по умолчанию буферизирует все входящие сообщения (конечно, если нет свободного процесса, который может немедленно его обработать).

Сага watchRequests создает 3 рабочих fork саги. Созданный канал предоставляется каждой из 3 созданных саг. watchRequests использует данный канал, чтобы диспатчить в рабочие саги сообщения.

Каждая рабочая сага – ожидает сообщение в бесконечном цикле. Обработка сообщения блокирует рабочую сагу. После того, как работа сделана, цикл начнется сначала и сага вернется в режим ожидания задачи.

Данный механизм обеспечивает автоматическую балансировку нагрузки между 3 рабочими сагами – освободившаяся сага немедленно получит новую задачу.

Блокировки

Лучше их избегать. Скорее всего, если не получается это сделать, вам нужен другой инструмент.

Блокирующие вызовы

Эффект call блокирующий. Генератор останавливается, пока call не завершится.

Если нужно обработать конкурентные процессы, то блокирующий вызов не подойдет.

Больше гибкости

Кроме того, в этом паттерне, по крайней мере в теории, клиентский сервис (выполняющий шаг саги) может обращаться к сервису саг и вписываться в него, а участие в саге может быть в том числе и опциональным. Еще может быть и другой сценарий: если вы уже отправили электронное письмо, компенсировать действие невозможно — вернуть письмо назад нельзя.

Но можно отправить новое письмо, что предыдущее было неправильным, и выглядит подобное так себе. Лучше использовать сценарий, когда сага будет проигрываться только вперед, без каких-либо компенсаций. Если она не проигрывается вперед, то надо сообщать сервису владельцу саги о проблеме.

Версионность api

В нашей реализации, когда в сервисе-исполнителе клиентского шага мы хотим что-то поменять (новая версия API сервиса), мы создаем новую сагу, в которой используем новую версию API. После этого переводим всех на новую сагу и старую сагу можно удалять и далее старый метод API также можно удалить.

Тут нужно быть внимательным — изменения в том числе могут неявно затрагивать логику компенсации. В том числе перед удалением старых классов саг и методов API шагов саг, нужно подождать интервал времени, когда уже 100% по старым методам не может пройти компенсация.

Ну и естественно, если придется менять сервис саг, и изменения будут обратно несовместимыми, необходимо будет поправить все сервисы, которые взаимодействуют с сервисом саг. Серебряной пули нет — все зависит от того, насколько велик характер изменений, которые вы собираетесь внести. Но таких случаев у нас пока не было.

Вместо зключения

Итак, мы разобрали зачем был придуман механизм каналов, и в каких практических задачах используются.

Надеюсь после прочтения складывается общее представление и мир стал чуточку проще.

Еще один сценарий с аварией

Представьте, что мы опять делаем ту же самую премиальную подписку.

  1. Покупаем VAS-пакеты и резервируем деньги.
  1. Применяем к пользователю услуги.
  1. Создаем VAS пакеты.

Избегать паразитных нагрузок

Выше я уже говорил, что нужно строить healthchecker, и если какой-то узел вышел из строя, нужно прекращать выполнять эти саги. Потому что сервис саг один, а клиентов много. Вы просто излишне перегрузите ваш сервис саг.

Избегать сложной логики избыточной функциональности в сервисе саг

Как только вы ввязываетесь в эту историю, сервис саг становится самой критичной точкой вашей инфраструктуры. Если он откажет, последствия могут быть ровно такими, какую вы функциональность на него накрутите. А мы хотим на него завязать самую критичную функциональность.

Поэтому choreography паттерн саг смотрится выгоднее — там сервис саг участвует только когда что-то пошло не так. В общем виде, даже если ваш сервис саг в choreography-паттерне сломается, у вас все будет продолжать работать. Сервис саг в choreography критически нужен, например при выполнении отката.

Интегрироваться с клиентами

Обучить все ваши команды работе с сервисом саг. Этот пласт теории надо прочитать, поскольку не всем может быть очевидно, как правильно работать с персистентными системами в контексте саг. Подумайте, как сделать удобной работу с локальным хранилищем работу с различными языками в контексте саг и как это все скрыть в клиентских библиотеках.

Как это все проверить

Нужно покрыть весь сервис саг тестами. Вероятнее всего, вы будете вносить изменения, и тесты, написанные на старте, помогут избежать нежданных сюрпризов. Кроме того, необходимо проверять и сами саги. Например, как у нас устроено тестирование сервиса саг и тестирование последовательности саг в рамках одной транзакции.

Тут есть разные блоки тестов. Если мы говорим про сервис саг, он умеет выполнять положительные транзакции и транзакции компенсации, если компенсация не работает сообщает сервису владельцу саг. Мы пишем тесты в общем виде, на работу с абстрактной сагой.

С другой стороны, положительные транзакции и компенсационные транзакции на сервисах, которые выполняют шаги саг, это же простое API, и тесты этой части в зоне ответственности команды-владельца этого сервиса.

А далее уже команда владелец саги пишет end-to-end тесты, где она проверяет, что вся бизнес-логика корректно работает при выполнении саги. Еnd-to-end тест проходит на полноценном dev-окружении, поднимаются все экземпляры сервисов, в том числе сервис саг, и там уже проходит проверку бизнес-сценарий.

image
Итого:

В чем суть CDC? Есть сервис, который предоставляет контракт. У него есть API — это provider. А есть другой сервис, который вызывает API, то есть пользуется контрактом — consumer.

Сервис-consumer пишет тесты на контракт provider’а, причем тесты, которые будет проверять только контракт, — не функциональные тесты. Нам важно гарантировать, что при изменении API у нас не сломаются шаги в данном контексте. После того как мы написали тесты, появляется еще один элемент сервис-брокер — в нем регистрируется информация о CDC-тестах.

image
О том, как в Авито реализовали CDC-подход для тестирования микросервисов рассказывал Фрол Крючков на РИТ . Тезисы можно найти на сайте Backend.conf — рекомендую ознакомиться.

Как это работает

Рассмотрим на примере покупки VAS-пакетов. VAS (Values-added services) — платные услуги для продвижения объявления.

Сначала сервис владелец саги должен зарегистрировать создание саги в сервисе саг

После этого он генерирует класс саги уже с Payload.

Далее уже в сервисе саг executor поднимает из хранилища ранее созданный вызов саги и начинает выполнять ее по шагам. Первый шаг в нашем случае — покупка премиальной подписки. В этот момент в сервисе биллинга резервируются деньги.

Потом в сервисе пользователя применяются VAS-операции.

Затем уже действуют VAS-сервисы, и создаются пакеты васов. Дальше возможны и другие шаги, но они не так важны для нас.

Каналы

Redux-saga предоставляет способ запускать саги снаружи redux окружения и привязывать их к кастомному Input/Output.

Buffer отвечает за то, как работать с переполнением каналов, когда события обрабатываются медленнее, чем поступают.

Каналы в документации

Когда нужен лок

Небольшое отступление про саги в целом: если вы можете сделать свою логику без саги, то делайте. Саги — это сложно. С локом примерно то же самое: лучше всегда избегать блокировок.

Когда я пришел в команду биллинга рассказывать про саги, они сказали, что им нужен лок. У меня получилось им объяснить, почему лучше обойтись без него и как это сделать. Но если лок все-таки вам потребуется, то это стоит предусмотреть заранее. До сервиса саг мы уже в рамках одной СУБД реализовывали блокировки.

Как это сделать? В рамках одной СУБД вы можете сделать некую таблицу, в которой будете сохранять записи о блокировке, и далее в триггере, при выполнении операций над объектом этой блокировки подсматривать в эту таблицу, и, если кто-то попытается его поменять во время блокировки, генерировать исключения.

Примерно то же самое можно сделать и в сервисе саг. Главное соблюсти порядок. Я предлагаю следующий подход: сначала мы делаем блокировку в сервисе саг, если мы хотим реализовать сагу с локом, а потом уже ее спускаем до клиентского сервиса с помощью вышеописанного подхода.

Можно и по-другому, но важно, чтобы был правильный порядок. И нужно понимать, что если у вас появились блокировки, значит появятся и дедлоки. Если появятся дедлоки, значит, нужно делать детектор дедлоков. А еще блокировки могут быть эксклюзивные и разделяемые.

Компенсация при авариях

При проектировании компенсационных транзакций, в случае различных аварий, когда невозможно выполнить сагу, внимательно изучите с представителем продукта вероятные сценарии и опишите их. Важно искать компромисс между автоматизацией решения инцидентов (предпочтительный вариант) и передачей их на анализ и принятие решения человеком.

Компенсация транзакций: как это работает

Для каждой положительной транзакции мы должны описать обратные действия: бизнес-сценарий шага на случай, если что-то пойдет не так.

В нашей реализации мы предлагаем такой сценарий компенсации:

Если какой-то шаг саги завершился неуспешно, а мы сделали множество retry, то есть шанс, что последний повтор операции удался, но мы просто не получили ответ. Попробуем компенсировать транзакцию, хотя этот шаг не обязателен, если сервис-исполнитель проблемного шага действительно сломался и совсем недоступен.

В нашем примере это будет выглядеть следующим образом:

  1. Выключаем VAS-пакеты.
  1. Отменяем операцию пользователя.
  1. Отменяем резервирование средств.

Масштабирование саг

Масштабирование зависит от размеров планируемой вами системы. Рассмотрим вариант с одним экземпляром хранилища:

И только потом, если вы заранее знаете, что упретесь в производительность одного сервера в СУБД, нужно делать шардинг — n инстансов баз, которые будут работать со своим набором данных. Шардирование можно скрыть за API сервиса саг.

Мемы! абсолютные мемы!

Ещё при анонс Tt Games объявили, что собирают все девять основных фильмов в едином продукте. Заострять на сюжете сильного внимания нет особого смысла. Уверен — многие не раз смотрели фильмы Лукаса. Хотя некоторым деталям разобрать придётся, и это не относится шутки.

Отдаю должное — в своём пересказе создатели постаралась включить как можно больше моментов из фильмов. На фоне первых двух ЛЕГО игр «Skywalker Saga» наиболее самодостаточная пародия, где большую часть сюжета можно понять даже без просмотра первоисточника.

Создатели даже сгладили “неровные” углы каждой трилогии, вырезав спорные или, по мнению фанатов, неудачные элементы, как медихлорианы в приквелах. Больше всех же под нож попал «Последний Джедай», где ряд сцен и вовсе переработали кардинально, как переход Кайло Рена на тёмную сторону или путешествие Роуз и Финна.

Вообще сложно определится, какие чувства должна вызывать новейшая трилогия. Создатели будто дразнят тем, что сейчас начнут высмеивать фильмы «по чёрному», особенно в «Пробуждении Силы», но вместо этого игнорируют созданные предпосылки. Те, кто смотрел «Последнего Джедая», наверное, помнят такого персонажа как адмирал Холда.

Как вам идея того, что это будет не одна героиня а целых три? Но после введения в историю ни одна из ипостасей персонажа никак больше не проявляет себя в истории, кроме своей фирменной сцены, и будто присутствует для галочки. И при том, что урезание пошло на пользу фильмам Disney, их сюжетам это сильной чести не делает — они по-прежнему ломают законы вселенной «Звёздных Войн», хотя Лукас этим также занимался, и не воспринимаются целым произведением. ЛЕГО пародия разве что смогла слегка сгладить ощущения в их восприятии, но именно из-за того, что это пародия.

И, если продолжения от урезания стали лучше, оригинальная трилогия, наоборот, пострадала серьёзно от сокращений. Больше всех это затронуло связь принцессы Леи и других героев. Кроме роли запускающего крючка событий, девушка никак не влияет на происходящее в сюжете, а про любовную линию с Ханом вспоминают только к концу пятого эпизода, от чего в пародии всё не выглядит искренно или сколько-то романтично.

Даже все включённые сцены флирта с Леей ради шутки переписаны под С3-PO. Кстати, за домогательства всё равно извиняются перед героиней, а не дроидом. Если бы я не смотрел фильмы, то я бы даже на персонажа не обратил внимание — из яркой и запоминающейся космической принцессы Лея стала невзрачным второстепенным персонажем.

В то же время создатели не забывают продвигать мем с Люком и молоком из восьмого эпизода почти в каждом эпизоде. Персонаж даже рождается с этим напитком и за предзаказ Делюкс издания покупателю даётся ЛЕГО фигурка с этим мемом. Кто-то явно хочет, что это стало главной особенностью Люка.

Мониторинг

Нужно мониторить выполнение саг с разбивкой по всем шагам и по всем статусам. Мы собираем всю телеметрию, в том числе как долго выполняется каждый шаг саги и сами саги. Все то же самое мы должны смотреть и для компенсирующих транзакций. К тому же не забывать про checker. А еще хорошо бы обложить сервис саг метриками на каждом шаге. Вот примеры графиков, которые мы собираем.

В первую очередь смотрим на перцентили (50%, 75%, 95%, 99%), потому что по ним вы раньше всего узнаете, если что-то пошло не так.

Как определить место поломки, если сага поломалась — как я уже сказал, мы собираем метрики с разбивкой по шагам и далее. На все эти шаги саг мы можем повесить алерты. Если определенные шаги саги копятся, значит что-то пошло не так. Но возможно, что сага еще совсем не сломалась — просто произошел всплеск нагрузки в одном из сервисов исполнителей шагов саги.

Еще одна ситуация. Как определить, что какой-то шаг саги (сервис вышел из строя) совсем не работает. В данном случае healthchecker проверяет все endpoint’ы info (keep-alive) клиентских сервисов.

Ну и третий пример. Может быть авария со стороны бизнес-сценария. Ответственность бизнес-сценария, что ваша бизнес-транзакция выполняется корректно, уже полностью лежит на команде владельца саги и командах владельцев сервисов-исполнителей шагов саги.

В этой ситуации владелец отдельно взятой саги, когда он ее проектирует, должен покрыть ее тестами, в том числе end-to-end. Далее нужен мониторинг на различные бизнес-метрики саги. Команда, которая сгенерировала эту сагу, должна у себя отслеживать метрики — это зона ее ответственности.

Не блокирующие вызовы

Для не блокирующих вызовов библиотека предлагает эффект fork. Когда задача запускается через fork, она выполняется в фоне, а вызывающая его сага не блокируется и не ждет окончание процесса.

Проблема в том, что так как fork задача запущена в фоне, она не может вернут в вызывающий код какое-то значение, как это делает call. По этому, форкнутый процесс должен диспатчить события.

Особенностью fork процесса является то, что эффект cancel не отменяет его моментально. Перед удалением процесса будет выполнен блок finally, который может содержать логику, которую необходимо выполнить перед удалением процесса.

Не пробуйте. меняйте. или не меняйте. никаких пробуйте

По своей сути «Skywalker Saga» это тот же продукт, что и его предшественники, где игрок уничтожает сделанные из конструктора объекты и пересобирает их в нечто новое для продвижения по игре. Но на этот раз создатели обещали дать целую галактику, где за прохождение эпизодов открывались бы новые планеты для исследования, хотя местами и возникает небольшой диссонанс в сеттинге.

Если, например, в четвёртом эпизоде отправится на планету приквелов, или наоборот, то игра никак не изменится. Так во время «Атаки Клонов» можно найти на Татуине хижину Оби-Вана из «Новой Надежды» с фотографиями его военных подвигов, или в четвёртом эпизоде посетить Корусант после приказа 66, где целый храм джедаев, живые юнлинги и члены совета.

При заявлении об открытом мире в новой ЛЕГО игре остались знакомые уровни со сбором мининаборов и деталек для покупки персонажей и получения важного ресурса для их прокачки, кайбер блоков. И именно этот аспект у Tt Games получился лучше всего. Мало того, что этапы нередко подчёркивают особенности каждой трилогии, как масштабы приквелов или подражание оригинальной трилогии в продолжениях, так и видно, что и остались многие идеи из предыдущих проектов, вроде прохождения за две группы фигурок из «ЛЕГО Clone Wars» или переходы между космическими и наземными сегментами в «ЛЕГО Force Awakening».

Обычно разработчики стараются выбирать для уровней эпизоды, которые сложно адаптировать в открытом мире, как гонки на подах или плавание через ядро планеты.

Поменялась же сама корневая идея — на смену длинным локациям пришли эксперименты с нелинейностью, где игроку самому бы пришлось из нескольких возможных решений выбирать своё. В миссии по спасению Хана Соло из замка Джаббы Хатта такую вариативность создаёт скрытое прохождение.

Можно напрямую идти сквозь залы дворца и уничтожать всё на пути или находить обходы и выпускать пленников из тюрьмы Хатта, дабы стража отвлеклась, а персонажи могли пройти незаметно. Или во время побега Йоды из Кашиика в третьем эпизоде также можно напрямую выступить против отряда клонов с боевым транспортом или же обойти их.

Местами вариативность создаёт идея из «ЛЕГО Force Awakening», когда из одних и тех же блоков можно собрать разные объекты, что нередко влияет на то, каким маршрутом игрок будет исследовать мир. И, как и в предшественниках,  для любителей пройти миссию с любым набором героев в «Skywalker Saga» остался свободный режим, где за счёт доступа ко всем купленным фигурками больше возможностей для изучения.

Отсутствие изоляции

Лучше заранее изучить различные сценарии, кто работает с тем или иным объектом вашей саги или частью саги, конкретного шага, и что может пойти не так, если он прочитает данные в процессе выполнения саги.

Параллельность

Yield хорош для ожидания асинхронных эффектов в линейном стиле, но для параллельного исполнения не подойдет.

Для параллельного выполнения следует использовать эффект all.

Когда запускается yield all, генератор блокируется до тех пор, пока все эффекты в переданном массиве не завершатся, либо пока в каком-либо из этих эффектов не возникнет ошибка (как в Promise.all).

По получению результата вызова функций

а) синхронная — результат функции известен сразу;б) асинхронная — функция возвращает сразу «ОК», а результат возвращается потом, через обратный вызов API сервиса саг из клиентского сервиса.

Хочу вас предостеречь от ошибки: лучше не делать синхронные шаги саг, особенно при реализации оркестрируемой саги. Если вы будете делать синхронные шаги саг, то сервис саг будет ждать, пока выполниться этот шаг. Это лишняя нагрузка, лишние проблемы в сервисе саг, поскольку он один, а участников саг много.

По порядку вызова функций

а) неупорядоченная — функции саги вызываются в любом порядке и не ждут завершения друг друга;б) упорядоченная — функции саги вызываются в заданном порядке, друг за другом, следующая не вызывается пока не завершится предыдущая;в) смешанная — для части функций задан порядок, а для части нет, но задано перед или после каких этапов их выполнять.

Рассмотрим конкретный сценарий. В том же сценарии покупки премиальной подписки первым шагом будет резервирование денег. Теперь изменения у пользователя и создание премиальных пакетов мы можем выполнить параллельно, а пользователю отправим уведомления только когда закончатся эти два шага.
image

Подробнее о channel

Такие каналы обычно решают задачу общения между сагами. Используются очень редко. Например, если вам необходимо согласовать несколько запросов, начинающихся одновременно.

channel([buffer])

Имеет единственный аргумент buffer — аккумулирующий буфер (подробнее буфферы разберем ниже).

Подробнее об actionchannel

Чаще всего используется в случае необходимости реагирования на события из redux store.

actionChannel(pattern, [buffer])

Принимает два аргумента:

Краткий пример использования:

import { take, actionChannel, call } from 'redux-saga/effects'

function* watchRequest() {
  const requestChannel = yield actionChannel('INIT_REQUEST')
  while (true) {
    const {payload} = yield take(requestChannel);
    // заметим что вызов теперь блокирующий
    yield call(makeRequest, payload);
  }
}

function* makeRequest(payload) {
    // код саги
}

Подробнее об eventchannel

Обычно через него решают задачу общения через web socket.

eventChannel(subscribe, [buffer], [matcher])

Принимает три аргумента:

Краткий пример использования:

import { eventChannel, END } from 'redux-saga'
import { take, put, call } from 'redux-saga/effects'

function initSocketChannel(query) {
  return eventChannel(emitter => {
      // эмулируем получение данных через web socket
      const handshakeTimeoutId = setTimeout(() => {
          emitter('handshake - ok');
      }, 100);

      const messageTimeoutId = setTimeout(() => {
          emitter('message');
      }, 500);

      const endTimeoutId = setTimeout(() => {
          emitter(END);
      }, 1000);

      // функция отписки от канала
      return () => {
        clearTimeout(handshakeTimeoutId);
        clearTimeout(messageTimeoutId);
        clearTimeout(endTimeoutId);
      }
    }
  )
}

export function* saga() {
  const chan = yield call(initSocketChannel, query)
  try {    
    while (true) {
      const message = yield take(chan);
      // при возвращении каналом END сработает обычный brake
      console.log(`socket : ${message}`)
    }
  } finally {
    console.log('socket terminated')
  }
}

Наверняка вами было замечено присутствие константы END — это action, означающий окончание общения с каналом.

В исходном коде redux-saga представлен следующим образом:

var CHANNEL_END_TYPE = '@@redux-saga/CHANNEL_END';
var END = { type: CHANNEL_END_TYPE };
var isEnd = function isEnd(a) {
  return a && a.type === CHANNEL_END_TYPE;
};

и в исходном коде eventChannel видим следующий сценарий

function eventChannel(subscribe) {
    …
    if (isEnd(input)) {
        close();
        return;
    }
    ...
}

Пример логики генерации саги, которую можно скрыть в клиентской библиотеке

Можно сделать иначе, но я предлагаю следующий подход.

  1. Получаем request ID, по которому мы должны создать сагу.
  2. Идем в сервис саг, получаем ее уникальный идентификатор, сохраняем его в локальном хранилище в связке с request ID из пункта 1.
  3. Запускаем сагу с payload в сервис саг. Важный нюанс: я предлагаю локальные операции сервиса, который создает сагу, оформлять, как первый шаг саги.
  4. Возникает некая гонка, когда сервис саг может выполнить этот шаг (пункт 3), и наш backend, инициирующий создание саги, тоже будет его выполнять. Для этого везде делаем идемпотентные операции: кто-то один его выполняет, а второй вызов просто получит «ОК».
  5. Вызываем первый шаг (пункт 4) и только после этого отвечаем клиенту, который инициировал это действие.

В этом примере мы работаем с сагой как с базой данных. Вы же можете отправить запрос, а далее соединение может и оборвется, но действие будет выполнено. Здесь примерно такой же подход.

Проблема модели watch-and-fork

Предположим что у нас обычная watch-and-fork модель, следующего вида:

import { take, fork } from 'redux-saga/effects'

function* watchRequest() {
  while (true) {
    const {payload} = yield take('INIT_REQUEST');
    // заметим, что вызов не блокирующий
    yield fork(makeRequest, payload);
  }
}

function* makeRequest(payload) { 
    // код саги
}

Этот подход плох тем, что при отлове нескольких событий INIT_REQUEST, идущих друг за другом, будет запущено, соответственно, несколько исполнений makeRequest. Что в свою очередь может вызвать их “гонку”.

И тут нам на помощь приходит механизм каналов.

Каналы обладают буферами, тем самым помогают выстраивать в очередь приходящие события (к примеру, INIT_REQUEST), и организуют их последовательное исполнение (к примеру, makeRequest исполнится последовательно несколько раз).

Грубо говоря, каналы образуют FIFO очередь на последовательное исполнение.

Класифируются по источнику событий:

Итак, разберем вкратце каждый.

Реализация оркестрируемой саги в виде сервиса pg saga

Так выглядит сервис PG Saga.

PG в названии, потому что как хранилище сервиса используется синхронный PostgreSQL. Что еще есть внутри:

На схеме также изображен сервис-владелец саг, а внизу — сервисы, которые будут выполнять шаги саги. У них могут быть разные хранилища.

Синхронизация по сокетам

Канал может быть из чего угодно, не только из сокетов. Например, каналом может быть движение мыши, ресайз окна, нажатия на кнопку.

Вариант создания канала из веб-сокетовfirebase:

apiService это обертка над fireBase API:

Теория. максимально кратко

Я не буду подробно описывать теорию саг. Дам лишь краткие вводные, чтобы вы понимали контекст.

Как было раньше (со старта Авито до 2023 – 2023 годов): мы жили в условиях монолита, с монолитными базами и монолитными приложениями. В определенный момент эти условия стали мешать нам расти. С одной стороны, мы уперлись в производительность сервера с главной базой, но это не основная причина, так как вопрос производительности можно решить, например с помощью шардирования.

С другой стороны, у монолита очень сложная логика, и на определенном этапе роста доставка изменений (релизов) становится очень длительной и непредсказуемой: много неочевидных и сложных зависимостей (все тесно связано), тестировать тоже трудоемко, в общем масса проблем.

Решение — перейти на микросервисную архитектуру. На этом этапе у нас появился вопрос с бизнес транзакциями, сильно завязанными на ACID, предоставленными монолитной базой: нет ясности как мигрировать данную бизнес логику. При работе с Авито возникает множество различных сценариев, реализованных несколькими сервисами, когда целостность и согласованность данных очень важна, например покупка премиальной подписки, списание денег, применение услуг к пользователю, приобретение VAS-пакетов — в случае непредвиденных обстоятельств или аварий все неожиданно может пойти не по плану. Решение мы нашли в сагах.

Мне нравится техническое описание саг, которое в 1987 году привели Кеннет Салем и Гектор Гарсия-Молина — один из нынешних членов совета директоров Oracle. Как формулировалась проблема: есть сравнительно небольшое количество долгоживущих транзакций, которые длительное время препятствуют выполнению небольших, менее требовательных к ресурсам и более частых операций.

В качестве желаемого результата можно привести пример из жизни: наверняка многие из вас стояли в очереди отксерокопировать документы, и оператор ксерокса, если у него была задача копировать целую книгу или просто много экземпляров копий, время от времени делал копии других членов очереди.

Но утилизация ресурсов — это только часть проблем. Ситуацию усугубляют и длительные блокировки при выполнении ресурсоемких задач, каскад из которых выстроится в вашей СУБД. Кроме того, в процессе длительного выполнения транзакции могут возникать ошибки: транзакция не завершится и начнется откат.

Мне кажется, многие подходили к этому, даже не читая этот документ. Мы неоднократно рассказывали про наши defproc (deferred procedures, реализованные при помощи pgq). Например, при блокировке пользователя за fraud — быстро выполняем короткую транзакцию и отвечаем клиенту.

В этой короткой транзакции, в том числе, ставим задачу в транзакционную очередь, а потом асинхронно, небольшими партиями, например по десять объявлений блокируем его объявления. Мы это делали с помощью реализации транзакционных очередей от Skype.

Но наша сегодняшняя история немного отличается. Нужно посмотреть на эти проблемы с другой стороны: распил монолита на микросервисы, построенные с помощью паттерна database per service.

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

Под вышеописанные требования наиболее оптимально подходит решение в виде оркестрируемой саги.

Трейсинг по saga call id

У нас все состояния хранятся в хранилище сервиса саг. Есть API в сервисе саг, которое по идентификатору конкретного экземпляра саги возвращает текущее состояние саги.

Фанат, поверь в лего, положись на него

К технической части «Skywalker Saga» у меня возникло много вопросов как в её исполнении, так и дизайнерской части. Не уделили Tt Games достаточного внимания и камере. Вместо того, чтобы в узких помещениях или в других ситуациях менять прозрачность объектов и давать игроку больше возможностей для изучения местности, последняя сталкивается со стенкой, а модель персонажа начинает перекрывать вид на ряд деталей.

Вместе с этим платформинг в игре местами переработан под заскриптованные прыжки между такими объектами как брёвна, балки, особые выступы на зданиях или другими платформами. И такая реализация вызывает неоднозначные чувства. Нередко игра неправильно обнаруживает новые переходы или, когда персонаж доходит до конца такой цепочки, у него не получается вскарабкаться наверх и приходится заново взбираться.

Особо ярко проблема с прыжками заметна во время прохождения одной из загадок на Мустафаре, вулканической планете из третьего эпизода.

Зато видны значительные усилия в переработке боевой системы, в котором отделили beat’em up от дальнего боя. И хотя большинство персонажей может и то, и другое, разграничение поменяло восприятие игрового процесса за разные фигурки. Игра за адептов Силы больше ощущается как слэшер с возможностью блокировать атаки врагов, а за героев с бластерами или другим дальним оружием — как шутер, где попадание в те или иные части тела имеет разный эффект.

Но именно в перестрелках игра могла бы отказаться от некоторых старых идей или продумать их значительно лучше. В «ЛЕГО Force Awakening» Tt Games ввели специальные сегменты с боями из-за укрытий, где за прохождение без смертей выдавали больше деталек на покупку новых «скинов».

«Skywalker Saga» такие моменты убрала, но оставила укрытия и заскриптованные переходы между ними. Однако низкая сложности и «Синдром ЛЕГО штурмовика», когда враги не только косые, но и лазер достаточно медленный для уворота, делает наличие укрытий больше бесполезными.

А вот ближний бой даёт куда больше разнообразия. После первого удара при нажатии на другие кнопки можно переключится на захват, апперкот или уникальный приём бойца, как вентилятор цветовых мечей генерала Гривуса. Чтобы не было злоупотребления одним и тем же приёмом, враги со временем блокируют повторяющиеся атаки.

Интересными смотрятся и некоторые эстетические улучшения. Хотите уничтожить отряд штурмовиков за С3-PO и R2-D2? В «Skywalker Saga» эта мечта станет явью. Но меня больше порадовали битвы на световых мечах. В отличие от предыдущих игр, где схватки проходили нередко по одному сценарию, в новой игре такие моменты имеют и особую режиссуру камеру, и дополнительное разнообразие с запуском небольших QTE событий.

Финальная мысль

Новые ЛЕГО «Звёздных Войн» вызывают большие опасения. Первые 10 часов, когда проводишь время только в сюжете, игра даёт одни лишь положительные эмоции. Однако с переходом в открытый мир начинается сплошное однообразие и желание выключить проект как можно быстрее и не запускать никогда.

Да, серии нужны изменения после пятнадцати лет одинаковой продукции, но я не вижу ни кардинально новых идей, кроме боевой системы и подходу к миссиями, ни продуманности в целом. Механики те же самые, баланс между классами вышел хуже, хотя ряд проблем, как разница между ситхами и джедаями, игра унаследовала ещё от предшественниц, загадки остались на том же уровне, что и в предыдущих играх: взять нужного персонажа, включить способность, забрать предмет.

Заметно, что создатели смотрели в сторону других платформеров с открытым миром, в первую очередь Super Mario Odyssey. Но там, где у Nintendo огромное наследие механик, способностей и имеется свой подход к построению локаций, у Tt Games всё упрощено до примитивных триггеров и попытки закрыть недостатки фансервисом и давлением на ностальгию. При этом не только по Звёздным Войнам, но и по всей поп-культуре в целом. И судя по продажами — это работает.

Некоторые проблемы можно списать, что разработка пришлась на период эпидемии коронавируса, из-за чего даже не весь контент успели закончить. Но, может быть, не стоило делать столько одинаковых загадок, а больше сил кинуть на техническую доработку? Нет, я никогда не считал Travellers’s Tales талантливой или креативной студией.

Единственный проект, где они проявили фантазию, был сделанный оксфордским отделением Crash Twisanity (одна из немногих игр про бандикута вместе с Crash 2, которую я положительно оцениваю, несмотря на её качество).  Их проекты всегда держатся на более-менее приемлемом качестве и бешеном фансервисе по ЛЕГО и тем фильмам, что лежат в основе.

Я даже не знаю, стоит ли рекомендовать игру. Со Switch-версией подавно не стоит знакомится. Сделана она исключительно, чтобы как-то зацепить аудиторию Nintendo, из которой и состоит основная часть фан-базы платформеров.  Фанатам «Звёздные Войны» Skywalker Saga должна зайти в первые несколько часов, но сомневаюсь, что она сможет их удержать надолго.

Я ощущаю возмущение в балансе игры

С миниатюрками и их особенностями создатели не придумали ничего лучше, как взять способности из предыдущих проектов (не только «Звёздных Войн») и распределили между десятью видами персонажей. Это джедаи, адепты тёмной стороны (ситхи), мусорщики, герои, злодеи, мерзавцы, охотники за головами, дроиды двух видов и остальные персонажами, которые либо бесполезны, либо по тем или иным признакам относятся к одному классу, но из-за дизайна или канона к нему не приписаны.

Каждому классу, кроме своих особенностей, доступны и улучшения, как для всех фигурок сразу, так и для конкретных. И здесь я больше всего в недоумении от новой игры — ресурсов на улучшение впритык, а среди навыков есть как полезные, так и ненужные. Из последних — «Страх тёмной стороны» у ситхов, из-за которой случайные прохожие или враги в панике начинают убегать от персонажа, что местами мешает выполнять задания, или бесполезный тренировочный дроид летающий вокруг злодеев.

Да, игрок может отключить ту или иную способность, но потраченные на неё кайбер блоки вернуть уже не удастся. Некоторые навыки ещё и забагованные, как дополнительные щиты для кораблей, если за штурвалом сидит герой, но бонус срабатывает и при пилотировании другим героем.

И не только распределение способностей и улучшений вышли спорными в новой части. Проще всего объяснить ситуацию с дроидами. Вместе со своими панелями, к некоторым из которых протокольные ещё и ищут ключи, механики получили крюк, а их коллеги говорят на разных диалектах для получения заданий в открытом мире и разбирают себя на две половинки для прохода сквозь мелкие тоннели.

Среди астродроидов последнее умеют только модели типа BB из трилогии Disney. Разве что улучшения больше бесполезны, так как они редко используются в игре. И, если хочет прокачивать эти классы, целесообразно только взять улучшения на пропуск мини-игр и получения за их счёт дополнительного дохода.

Куда проблематичной получилась разница между ситхами и джедаями. Как и в предыдущих играх, с помощью силы такие фигурки подымают предметы и перемещают их в пространстве. В этой части они дополнительно научились проникать в сознание других живых существ (как в ЛЕГО Marvel и DC) и заставлять их делать те или иные вещи: танцевать, в панике атаковать друг друга или на время переходить под контроль игрока.

Дополнительно адепты силы в заскриптованном платформинге могут пользоваться стремянками, что, кроме них, умеют ещё и мусорщики. Последних легко отнести к золотой середине между всеми классами. Они и стрелки, и акробаты, и имеют свою особенность — сбор полезных для исследования гаджетов, вроде глайдера, пушки для уничтожения особых стен и метателя сеток.

И тут есть одна проблема с этим классом. Способностью мусорщика по созданию инструментов нельзя пользоваться, пока игрок не получить такого персонажа по сюжету и не пройдёт за него обучение. Если купить подобную миниатюрку раньше, она будет бесполезна.

Самой большой проблемой стал баланс стрелков: злодеев, героев, охотников за головами, мерзавцев. Дополнительно поделим эту категорию ещё на два типа: светлые, мерзавцы и герои, и тёмные, охотники за головами и злодеи. Для начала только один класс мировоззрения пользуется крюком: охотник за головами на тёмной стороне и герой на светлой.

Также отличается предназначение: злодеи с героями работают с панелями доступа своего класса, а наёмники берут в открытом мире заказы на устранение, если это охотник, или доставку груза, если мерзавец. Но проходить сами задания за героев этих классов необязательно. Достаточно лишь взять квест, а дальше поменять персонажа на любого другого.

Следующие отличия куда интереснее. Кроме использования панелей, злодеи уничтожают серебряные объекты гранатами и открывают сундуки с особым оружием. Проблема только в том, что персонажи-герои могут переодеваться в некоторых убитых врагов и тем самым сохраняют и свои особенности, и получают все бонусы класса антипода.

И были бы последние самыми бесполезными, если бы не мерзавцы, которые умеют лишь дополнительно запускать во время миссий особые события, обвалить дерево выстрелом или обрушить на врагов потолок. И, если со злодеями не везде можно захватить чей-то разум или переодеться в штурмовика, то кроме как взять задание на доставку, контрабандисты вне миссий нигде не нужны.

Но именно отсутствие крюка делает как мерзавцев, так и злодеев изгоями в новой игре, потому что в открытом мире это самый важный инструмент из-за его частого использования. Это и убивает разнообразие в прохождении, например, той же миссии с Леей. И, если и искать самых полезных стрелков в игре, то это будут не герои, а охотники за головами.

Они владеют крюком, уничтожают золотые объекты, а их улучшения позволяют им видеть цели заданий сквозь стены, быстрее зарабатывать деньги на покупку подсказок и других фигурок. Особенности мерзавца на этом фоне дают лишь небольшую скидку. В совокупности с увеличителями дохода, что первым делом и нужно покупать в всех ЛЕГО-играх, охотник и вовсе превращается в денежный принтер.

Оцените статью
Радиокоптер.ру
Добавить комментарий