Как мы обновили старый кластер Elasticsearch на 3 ПБ без простоев. Части 6 и 7 (финальная)
Прим. переводчика: автор статьи рассказывает, как его команда убедилась, что новый кластер Elasticsearch работает в соответствии с ожиданиями и полностью готов к production-нагрузкам. Также подводит итоги всего процесса и анализирует получившуюся архитектуру нового кластера.
Часть 6 — стратегия тестирования и развертывания
Добро пожаловать в шестую часть нашего приключения по обновлению кластера Elasticsearch. В предыдущих частях мы поговорили о структурировании работы, внесли в систему необходимые изменения, чтобы сделать миграцию возможной, воспользовались случаем и поменяли логику работы (что в иначе было бы просто невозможно сделать). Наконец, мы позаботились о том, чтобы система хорошо работала под нагрузкой. Все эти изменения стали результатом кропотливой работы и тщательного планирования. Но в конечном итоге все знали, что однажды встанет главный вопрос: не пора ли «рвануть рубильник» и окончательно перейти на новый кластер? Вряд ли кто захочет оказаться на месте человека, который возьмет на себя ответственность за такое решение. Нам этого тоже не хотелось. Логичным выходом казался подход на основе данных — именно они должны были подсказать, что время пришло.
Сработает ли все, как задумывалось?
Как упоминалось в первой части, необходимо было разработать постепенное, обратимое, исключающее простой решение для развертывания нового кластера. Это означало, что тестирование production-нагрузок должно было проходить в реальном окружении без какого-либо риска для него, а новый кластер должен был подключаться только тогда, когда станет ясно, что он работает в соответствии с ожиданиями. Ключом к этому были данные, поэтому их сбор требовалось включить как можно раньше. Зачем ждать, пока новый кластер заполнится петабайтами данных, если можно выполнять запросы независимо от объема данных, содержащихся в кластере? Собираемая статистика помогла бы ответить на вопросы:
Есть ли поисковые запросы, которые успешно выполняются в старом кластере, но не работают в новом?
Работает ли поиск в новом кластере так же хорошо, как в старом?
Дает ли один и тот же поисковый запрос одинаковые/похожие результаты при выполнении в разных кластерах?
В команде глубоко укоренилась культура экспериментирования и принятия решений на основе конкретных данных. Datadog и Kibana в сочетании с набором универсальных и специализированных инструментов тестирования и генерации нагрузки помогали нам анализировать работу систем под нагрузкой. То есть у нас уже был почти готов необходимый инструментарий. Одним из самых полезных инструментов был request-replay. Этот инструмент перехватывал и хранил все запросы, выполняемые поисковым сервисом в production, и воспроизводил их, повторно отправляя сохраненные запросы в поисковую инфраструктуру. Изначально он разрабатывался для воспроизведения реального трафика из production-окружения в development/staging-окружения, но после доработки научился воспроизводить трафик в канареечные развертывания в production-окружении.
Request-replay специально разработан для наших рабочих нагрузок и обладает некоторыми крайне полезными функциями. Он может:
воспроизводить запросы, следуя оригинальному распределению во времени, тем самым имитируя всплески трафика;
менять некоторые характеристики исходного запроса (например, диапазон дат), чтобы тот лучше соответствовал существующим данным в целевом кластере;
выбирать подмножество запросов на основе предвзятой или непредвзятой выборки вместо того, чтобы воспроизводить весь трафик;
сравнивать результаты, время и частоту ошибок у разных сервисов, а также отправлять метрики в Datadog и Kibana.
Как только второй кластер стал наполняться данными, в него сразу начали воспроизводиться запросы из production-окружения. Такое предварительное тестирование позволило обнаружить и устранить ряд более или менее очевидных проблем в самом начале. С ростом кластера неумолимо приближалось и время его окончательного ввода в эксплуатацию. В один момент пришло осознание, что нам не хватает ключевой функциональности — возможности воспроизводить запросы в реальном времени, отправляя один и тот же трафик как на старый, так и на новый кластер. Request-replay выполнял сохраненные запросы, но возможность сравнивать производительность при одинаковой нагрузке также не помешала — особенно если вспомнить, что имеющаяся архитектура уже умела сохранять запросы и воспроизводить их. Параллельное выполнение одних и тех же запросов на обоих кластерах позволило бы сравнить их производительность и провести подробный анализ.
Стратегия выката
На этом этапе представление о стратегии выката уже оформилось в наших головах: сначала запросы будут воспроизводиться в оба кластера. Любой трафик, отправленный в один кластер, будет копироваться в другой, формируя полноценный контур обратной связи. Первоначально все запросы будут направляться в старый кластер и воспроизводиться в новом. Затем доля запросов, отправляемых на новый кластер, одновременно воспроизводя их на старый кластер, будет постепенно расти (под нашим строгим контролем). В конечном итоге все запросы будут поступать в новый кластер и копироваться в старый. В этот момент воспроизведение можно будет остановить, а работу старого кластера — завершить.
Как это обычно бывает, сказать легче, чем сделать. К счастью, само устройство поисковой инфраструктуры упрощало процесс. Секретным оружием стал специально созданный интеллектуальный балансировщик нагрузки под названием search-router.
Как работает поиск
В Meltwater над продуктом работает множество команд. Для простоты давайте сосредоточимся на том, что происходит, когда поисковая подсистема получает запрос. В упрощенном виде архитектура выглядит следующим образом:
Изначально она служила для равномерного распределения нагрузки по узлам Elasticsearch. Однако оказалось, что интеллектуальный балансировщик нагрузки сильно расширяет возможности по маршрутизации трафика. На сегодняшний день search-router умеет дросселировать запросы для каждого пользователя, предотвращая перегрузку сервиса в случаях, когда «тяжелые» запросы поступают параллельно. Кроме того, он экспортирует метрики о запросах в Datadog и отсылает их пользователям с помощью заголовков HTTP-ответов. Наконец, во время миграции добавилась возможность проигрывать запросы в кластерах.
Собираем всё вместе
С архитектурой, описанной выше, стоявшая перед нами задача была простой и понятной. Search router будет отвечать за принятие решений о том, какие запросы направлять в тот или иной кластер, а исполнитель запросов request-replay сможет получать и выполнять запросы через HTTP, не ограничиваясь воспроизведением сохраненных запросов. Таким образом, search router будет выполнять как клиентские, так и воспроизводимые запросы. Оба вида запросов могли обрабатываться параллельно, однако нам не нравилась идея заставлять клиентов ждать, пока система воспроизведет сохраненные запросы. Соответственно, приоритет был отдан их запросам. Сохраненные запросы воспроизводились бы с минимальным отставанием от оригинальных, зато пользовательский опыт не пострадал бы. Итогом стала следующая архитектура:
В этой схеме search-router:
получает запрос;
решает, в каком кластере его обработать;
отправляет его в поисковый сервис в выбранном кластере;
получает ответ;
возвращает ответ клиенту;
отправляет запрос и ответ компоненту request-replay для воспроизведения в другом кластере.
Поскольку search-router мог посылать запросы в оба кластера, итоговая архитектура на самом деле выглядела чуть иначе:
Определяем судьбу запросов
Тестирование началось с воспроизведения запросов в новом кластере и сравнения результатов. Когда данные начали поступать, все вздохнули с облегчением. Оказалось, что практически всех клиентов можно безболезненно перевести в новый кластер. Практически — ключевое слово, поскольку в ряде случаев запросы, которые успешно выполнялись в старом кластере, не работали в новом. Изучив статистику, мы разработали следующий алгоритм принятия решений о судьбе запросов (то есть о том, будут ли они направляться в новый или старый кластер):
fun decide(
request: Request,
routingPercentage: Float,
alwaysRouteToOld: Set<String>
): TargetCluster {
val customerId = request.customerId
if (alwaysRouteToOld.contains(customerId)) {
return TargetCluster.OLD
}
val dateRange = request.dateRange
if (!newCluster.hasDataFor(dateRange)) {
return TargetCluster.OLD
}
val userIdHash = hashToFloat(request.userId)
if (userIdHash >= routingPercentage) {
return TargetCluster.OLD
}
return TargetCluster.NEW
}
Статистика позволила составить список клиентов, чьи запросы было «рискованно» отправлять в новый кластер. Список был сравнительно небольшим, поэтому не было смысла ждать, когда все проблемы будут устранены. Направлять нужных клиентов в старый кластер помогал новый параметр search router’а. Запрос сначала проверялся по нему. Если клиент был в списке, вся экзотика отменялась и запрос прямиком направлялся в старый кластер. Проблемы постепенно решались, а список пустел.
Также было глупо посылать в новый кластер запросы с диапазоном дат, для которого отсутствовали данные. Тут нас выручила концепция нулевого дня (Day0) — дня, когда поток данных в обычном для нас режиме стал поступать в новый кластер.
Данные заведомо присутствовали в новом кластере для любого запроса с временным промежутком позже Day0. Заполнение нового кластера подразумевало постепенную загрузку в него исторических данных, начиная с дня, предшествовавшего нулевому (то есть Day-1). Конфигурация search router’а обновлялась соответствующим образом: «день икс» сдвигался назад, а доступных для запросов данных становилось все больше. Search router анализировал тело запроса, чтобы определить диапазон его дат. Если данные для этих дат имелись в новом кластере, запрос направлялся в него.
Наконец, в новый кластер мы хотели направлять только определенную долю пользователей. Она определялась как число с плавающей точкой в диапазоне от 0 до 1. Первоначально генерировалось случайное число, и на его основе пользователь направлялся в новый или старый кластер.
Однако скоро обнаружилось, что у этого подхода есть существенный недостаток: если результаты для кластеров заметно отличались, пользователи замечали несоответствия при обновлении страницы или даже на одной и той же странице (в некоторых случаях результаты нескольких запросов к поисковой системе выводятся на одной странице). Подобные несостыковки расстраивали пользователей. Поэтому вместо генерации случайного числа было решено генерировать хеш в диапазоне от 0 до 1 для каждого пользователя из его идентификатора, и направлять запросы в определенный кластер на основе этого хеша. Так удалось добиться согласованности результатов, пока настройка системы продолжалась.
Оглядываясь назад
Когда все было сказано и сделано, весь процесс развертывания выглядел так, как показано на рисунке ниже.
Самым приятным во всем процессе была его беспроблемность. От начала и до конца все прошло гладко и спокойно. Возможность отслеживать производительность, стабильность и совместимость системы, а также уверенность в том, что в любой момент можно откатиться назад, позволили работать максимально быстро и с минимальным риском. Для этого пришлось создать новые инструменты и модифицировать инфраструктуру под них, но и отдача была соответствующей — беспроблемность процесса, спокойствие команды и безопасность бизнеса Meltwater.
В конечном итоге система кардинально изменилась внутри, но снаружи осталась прежней.
Часть 7 (финальная) — итоговая архитектура и выводы
Это седьмая и заключительная часть серии статей, повествующая об обновлении кластера Elasticsearch без простоев и с минимальным воздействием на пользователей. В ней мы поговорим о преимуществах, которые дало обновление, и подробно опишем получившуюся архитектуру.
Master-узлы — эффективное обновление состояния кластера и снижение нагрузки на CPU
Каждый раз, когда в Elasticsearch что-то меняется, например, добавляется новый узел или создается индекс, master-узлы синхронизируют состояние кластера со всеми остальными узлами.
В старом кластере master-узел просто отправлял полное состояние кластера на остальные узлы. Проблема в том, что когда в кластере более тысячи узлов и 90 тыс. шардов, его состояние «весит» сотни мегабайт. Пропускная способность сети master-узла становится узким местом, тормозя работу всего кластера:
На рисунке выше видно, какие объемы данных «скармливал» в сеть master-узел в старом кластере и как сильно они упали в новом. Огромная разница объясняется тем, что в последних версиях Elasticsearch на узлы отправляются только дельты состояния кластера, что значительно снижает нагрузку на сеть на master-узле. В новом кластере (фиолетовая линия) объемы передаваемых данных упали примерно на 90%.
Другая проблема с master-узлом в старом кластере — отсутствие параллелизма, когда за многие операции всего один JVM-поток. Порой время обработки росло линейно (или даже хуже того) по мере увеличения числа шардов. Для определенных операций в кластере приходилось использовать инстансы с самыми скоростными процессорами (в пересчете на ядро). Конечно, приходилось думать и о пропускной способности сети — master-узлы в старом кластере работали на самых дорогих инстансах AWS.
Новый подход к обновлению состояния кластера вкупе c другими улучшениями в последних версиях Elasticsearch позволили переключиться на гораздо более скромные инстансы с менее мощными процессорами и широкими сетевыми каналами. В результате стоимость master-узлов упала на 80%.
Узлы данных и их JVM
В старом кластере Elasticsearch работала на Java 8 — последней версии, поддерживаемой на момент его создания. Новый же кластер работает на Java 18 с ее продвинутым сборщиком мусора, который лучше работает с большими объемами heap-памяти. В старом кластере за сборку мусора отвечал CMS, в новом — G1. Одно это изменение значительно сократило частоту и продолжительность пауз типа «stop the world» (когда сборщик мусора приостанавливает работу программы — прим. перев.) при выполнении поисковых запросов. Сейчас сборка мусора на узлах с данными работает настолько хорошо, что о ней можно забыть.
Переход на G1 позволил поэкспериментировать с увеличением объема памяти под heap. Разработчики Elasticsearch не рекомендуют выделять под него более 32 ГБ (это касается обеих версий поисковой системы) из-за сжатых указателей объектов, однако детальное тестирование показало, что heap в 64 ГБ сокращает время поиска в нашем конкретном случае, и было решено остановиться именно на этом варианте. Плюс было еще 128 ГБ ОЗУ для дисковых кэшей, поскольку инстанс, который используется для узлов с данными (i3en.6xlarge), идет со 192 ГБ памяти.
Мы также отказались от настроек по умолчанию в паре других случаев. Во-первых, подняли число поисковых потоков до 48 на узел (вместо 37 по умолчанию для 24-ядерных машин). Во-вторых, включили транспортное сжатие для связи между узлами. Все изменения были тщательно протестированы на реальном трафике, прежде чем было решено остановиться на этих значениях.
Использование heap-памяти
Пока мы работали с кастомным форком, Lucene и Elasticsearch обзавелись многими улучшениями, касающимися heap-памяти и ее потребления в целом. Часть из них удалось бэкпортировать, но не все. Одно изменение — хранение индексов терминов на диске, а не в heap-памяти Java (появилось в Elasticsearch 7.7), — особенно нам пригодилось.
Как видно из рисунка, долю heap-памяти, занятой статическими данными (которые росли с увеличением объема используемого диска), удалось понизить с 40-50% в старом кластере до <1% в новом. Соответственно, утилизацию дисков получилось поднять с 45% до весьма приятных 83%. Фиолетовая линия — новый кластер.
Поскольку при обновлении мы перескочили через несколько промежуточных версий Elasticsearch, трудно точно сказать, какие еще изменения внесли свой вклад. Можно лишь утверждать, что улучшения в Elasticsearch в сочетании с оптимизацией работы с подстановочными знаками позволили масштабироваться по диску, а не по heap-памяти — т.е. гораздо более предсказуемо и стабильно. Другими словами, нам больше не придется переплачивать за недоиспользуемые ресурсы процессора и оперативной памяти.
В результате кластер сократился с 1100 до 600 эквивалентных узлов с данными, при этом задержки и пропускная способность как минимум не изменились.
Скользящие перезапуски кластера
Узлы с данными в старом кластере перезапускались мучительно долго. Процесс мог растянуться на пару месяцев, поэтому приходилось либо одновременно вносить множество изменений (увеличивая риск), либо оставлять все как есть (а как это совместить со стремлением оказывать качественные услуги?).
Теоретически кластер должен перезапускаться так же легко и непринужденно, как это описано в документации Elasticsearch. Увы, по ряду причин это не работало:
Нельзя было надолго останавливать индексацию из-за высокого наплыва входящих документов и обновлений, а также из-за требований к аптайму, предъявляемых к приложению.
Огромный размер состояния кластера (>200 МБ). Старые версии Elasticsearch не очень эффективно обрабатывают события присоединения/отсоединения узлов. После выхода узла из кластера проходило несколько минут, прежде чем кластер обнаруживал и обрабатывал потерю. Причем время реакции росло вместе с увеличением числа «выпавших» узлов.
То есть при >1000 узлов и ~5 минутах на перезапуск одного узла (даже в идеальных условиях, т.е. когда данные сразу готовы к использованию) на обработку всех событий выхода узлов и их присоединения к кластеру ушло бы более 80 часов.
В старых версиях Elasticsearch существующие данные на перезапущенном узле часто не были полностью готовы к использованию. Их приходилось восстанавливать с других узлов. Данных было очень много, поэтому процесс затягивался (на несколько часов) даже на инстансах с производительным I/O и широким сетевым каналом.
Таким образом, оставалось только мечтать об идеальных условиях, упомянутых выше. Вместо 80 часов рестарт продолжался недели или даже месяцы.
Другая сложность была в том, что отправка данных с одного узла на другой «съедает» дополнительное дисковое пространство на узле-источнике. Поэтому перезапускать получалось лишь несколько узлов за раз. Приходилось ждать, когда одна партия перезапустится, прежде чем перейти к другой.
К счастью, в последних версиях Elasticsearch для ускорения перезапуска используются так называемые глобальные/локальные контрольные точки. При отключении шарда (например, во время скользящего перезапуска) первичный шард отслеживает дельты с момента выхода до возвращения шарда в строй. Для восстановления данных теперь достаточно отправить только дельты. Вместе с оптимизацией работы мастер-узла с выходящими/входящими узлами это значительно ускоряет перезапуск. Весь процесс теперь занимает один день, а не пару месяцев.
Snapshot'ы индексов
Еще одной серьезной проблемой в старом кластере было создание snapshot'ов для индексов. Дело в том, что старые snapshot'ы не получалось удалять достаточно быстро, в результате резервные копии постоянно росли в размерах.
Перед удалением snapshot'а Elasticsearch итерировала все файлы в его bucket'е. Для резервных копий размером в петабайты подобная операция занимает массу времени. В новых версиях Elasticsearch работа со snapshot'ами оптимизирована и их удаление проходит гораздо быстрее.
Ответом на непрерывный рост snapshot'ов стал переезд на новый bucket S3 каждые четыре месяца. С нуля создавалась полная резервная копия данных в новом bucket'е S3, а старый оставался на всякий случай. После завершения резервного копирования данные из первого bucket'а S3 удалялись, и бюджет, наконец, мог выдохнуть с облегчением.
При старой стратегии чередования bucket'ов snapshot'ы в среднем занимали 8 ПБ. В новом кластере для их хранения достаточно 2 ПБ — старые snapshot'ы теперь удаляются быстрее, чем создаются новые. Затраты на резервное копирование сократились на 75%. Скорость создания snapshot'ов выросла на 50%, удаления — более чем на 80%.
На графиках выше показано, сколько занимает создание/удаление snapshot'а в старом/новом кластере. Фиолетовая линия — новый кластер.
Адаптивный выбор реплик
В новом кластере используется так называемый адаптивный выбор реплик (adaptive replica selection, ARS). Он маршрутизирует поисковые запросы по шардам, отправляя их на самые свободные узлы с данными, на которых присутствует данный шард, сокращая тем самым время поиска. В старом кластере была похожая функция, только поисковые запросы отправлялись в наименее загруженную зону доступности. Подробнее об этом решении можно почитать в одной из предыдущих статей.
На рисунке выше видно, что после включения ARS запросы стали обрабатываться гораздо быстрее. Значительно снизилась длина очередей, сократилось время ожидания.
Стратегия шардирования индексов
Наш кластер использует 3 зоны доступности (AZ) в AWS. Для всех данных хранятся по две реплики каждого шарда, независимо от возраста данных. Механизм распределения шардов (shard allocation awareness) следит за тем, чтобы на каждую зону доступности приходилось по одной реплике для отказоустойчивости и резервирования.
Индексация проводится по времени; есть как дневные, так и месячные индексы. Для некоторых старых и малых индексов есть даже годовые индексы. Стоит также отметить, что шардинг производится по времени публикации оригинального документа, а не по времени его получения.
Новые индексы создаются за несколько дней до того, как начинают получать данные. Автоматическое создание индексов на основе индексных запросов не допускается. Все операции по созданию индексов, настройке и оркестровке конфигурации выполняются набором кастомных инструментов, которые обеспечивают требуемый уровень детальности мониторинга и наблюдаемости, позволяя реализовать рабочий процесс «elasticsearch-infrastructure-as-code».
Оптимальным размещением этих новых, а также всех существующих индексов занимается специально созданный инструмент балансировки шардов, который размещает индексы на узлах данных таким образом, чтобы равномерно распределить нагрузку по возрасту, размеру, поиску и индексированию. Это устраняет «горячие точки» и «размывает» нагрузку по всем узлам данных в кластере. Механизм довольно похож на то, что делает Elasticsearch 8.6+, но опять же с некоторыми дополнительными настройками и функциями, а также более детальным мониторингом и наблюдаемостью.
На рисунках выше показана разница в распределении загрузки CPU между неоптимальной системой (вверху) и оптимально сбалансированной системой, оптимизированной с помощью нашего кастомного инструмента (внизу). Как видно, в сбалансированной системе ресурсы CPU используются гораздо более равномерно на всех узлах данных, кроме того, отсутствуют явные «узкие места», замедляющие поиск и индексирование.
Сбалансированность не только делает систему более быстрой и предсказуемой, но и позволяет сэкономить, ведь можно держать меньше ресурсов про запас, одновременно повышая их общую утилизацию.
Слияние индексов
При обновлениях в Elasticsearch старые версии документов удаляются и заменяются на новые версии в другом сегменте. Затем сборщик мусора собирает эти остатки с помощью автоматического слияния сегментов индекса.
Как говорилось ранее, документы часто обновляются — в шарды и сегменты попадает большое количество таких остатков. Нас это не особо заботит — производительное оборудование с быстрыми дисками и запасом мощности по CPU легко справляется с объединением сегментов. Чтобы минимизировать место на диске, занятое такими остатками, была настроена крайне агрессивная политика слияния. В частности, параметр index.merge.policy.deletes_pct_allowed, который задает размер остатков, при котором вызывается автоматическое слияние, был установлен на самое низкое значение (20%).
К слову, мы бы с радостью установили его, скажем, на 5%, чтобы принудительные объединения выполнялись еще чаще. Увы, Elasticsearch пока этого не позволяет. Таким образом, пятая часть дискового пространства в кластере используется для хранения этих остатков. В нашем случае это означает несколько сотен ТБ потраченного впустую места.
Чтобы свести к минимуму использование диска, был разработан еще один кастомный инструмент под названием merge-counselor. Он управляет всем жизненным циклом и эвристикой слияния индексов. Инструмент планирует принудительное слияние индексов, которые в этом нуждаются, в контролируемом и приоритетном порядке исходя из пользовательских правил и эвристики. Одно из правил заключается в том, что индексы старше 30 дней допускают только 5% удалений до принудительного слияния. Для более «горячих» данных правила более либеральные — индексы текущего и прошлого дня полностью управляются настройками Elasticsearch по умолчанию, которые и определяют приоритет скорости индексирования для этих данных.
На рисунке показано место на диске, занятое удаленными документами (их остатками) в кластере. В ноябре был развернут компонент merge-counselor. Принудительное объединение шардов и удаление мусора позволило освободить более 400 ТБ. Узлов с данным стало меньше на 9%, сократились и расходы на AWS.
Нетехнические плюсы
Наконец, стоит отметить некоторые нетехнические преимущества, которые принесло обновление. Теперь нам гораздо проще взаимодействовать с остальным сообществом Elasticsearch. Обратная связь по проблемам или нужным фичам, которую мы даем, соответствует актуальным задачам, стоящим перед разработчиками Elasticsearch, и, надеюсь, совпадает с интересами остальных пользователей. Добиться этого позволил переход на официально поддерживаемую версию Elasticsearch без каких-либо кастомизаций.
Как разработчикам, нам приятно идти в ногу с последними достижениями в области поисковых технологий и пользоваться всеми крутыми новинками. Жизнь «после обновления» только начинается, и в ближайшие годы нам предстоит раскрыть весь потенциал используемых решений.
Наш вклад в развитие сообщества пока находится в зародыше, хотя эту серию статей тоже можно рассматривать как первые ростки. Кроме того, мы оптимизировали способ вычисления ключей кэша запросов шардов и провели Elastic Meetup.
Надеемся, что сможем продолжать работать на благо сообщества и делиться с ним уникальной информацией о том, как некоторые функции Elasticsearch ведут себя при работе с петабайтами данных (конечно, с учетом нашей специфики и требований).
Есть интересные идеи для будущих статей (хотя не обещаем, что все они выйдут). Например, можно поговорить о том, как рабочие нагрузки ведут себя на инстансах с ARM-процессорами, как нам удается постоянно измерять точную нагрузку, создаваемую запросами в кластере. Кроме того, можно обсудить результаты экспериментов с многоуровневой структурой данных.
Заключение
Пришло время подводить итоги. Вот краткий перечень основных плюсов, которые принесло обновление:
Стабильность и отказоустойчивость системы выросли за счет лучшей обработки состояния кластера.
Общая стоимость кластера упала более чем на 60%.
Масштабирование вверх/вниз в зависимости от меняющихся потребностей бизнеса теперь проходит гораздо проще.
Изменения внедряются быстрее и легче.
Больше не нужно поддерживать кастомные модификации Elasticsearch: вся дополнительная функциональность упакована в плагины или во внешние инструменты/приложения — нет нужды развивать собственный форк Elasticsearch.
Избавление от устаревшего кода ускорило внедрение инноваций.
Значительно упростился процесс обновлений.
Появилась возможность сблизиться с остальным сообществом Elasticsearch.
В заключение мы хотели бы поблагодарить все замечательные команды в Meltwater и фантастическую службу поддержки, которая помогала нам на протяжении всего процесса обновления. Без вас оно было бы невозможным! ♥️ Также хотим выразить признательность всем, кто читал и комментировал наши статьи. Надеемся, они вам понравились!
Чтобы оставаться в курсе событий, подписывайтесь на нас в Twitter или Instagram.
P.S.
Читайте также в нашем блоге: