Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Ограничение запросов ElasticSearch со стороны клиента

ElasticSearch — классная штука, но он не всемогущ. Некоторые запросы могут привести к OOM в кластере. Это может случиться по разным причинам, но в основном связано с дополнительными функциями: подсветка (создает мини-индекс в памяти), агрегации и т.п. — сам по себе поисковый запрос ничего не положит, если только настройки кластера не совсем убогие. Однако есть способы ограничить поисковый запрос на стороне клиента:

  1. timeout — по умолчанию его нет, но это можно изменить.
  2. terminate_after — ограничивает максимальное число документов, возвращаемое с одной шарды. Хорош в тех случаях, когда поиск подразумевает небольшое число результатов.

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

Ссылка

Недавние открытия из мира сортировок

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

  1. Алгоритм сортировки, основанный на ML: http://learningsys.org/sosp19/assets/papers/22_CameraReadySubmission_Abstract___SOSP__19_ML_Sys_workshop-4.pdf Меня удивило, что даже в таких низкоуровневых задачах машинное обучение тоже имеет какое-то применение. Алгоритм основан на поразрядной сортировке.
  2. Способы получения k-ой порядковой статистики: https://danlark.org/2020/11/11/miniselect-practical-and-generic-selection-algorithms/. Эта статья — от передовика оптимизаций: по итогам этого исследования автором был сделан патч в ClickHouse.
Ссылка

Динамические тесты в JUnit и Before/AfterEach

Вляпался недавно с динамическими тестами в ограничение JUnit: динамические тесты — неполноценные участники жизненного цикла, и, как следствие, для них не вызываются методы @BeforeEach и @AfterEach. Точнее запускаются, но перед фабрикой динамических тестов:

@BeforeEach
fun init() {
    println("init stage")
}

@AfterEach
fun cleanup() {
    println("clean up stage")
}

@TestFactory
fun dynamicTests() = listOf("dynamic1", "dynamic2").map { name ->
    dynamicTest("dynamic $name") {
        println("dynamic $name test")
        assert(true)
    }
}

Выведет:

init stage
dynamic dynamic1 test
dynamic dynamic2 test
clean up stage

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

Ссылка

Сборка контейнера со Spring-приложением

В новых версиях спринга появилась возможность создавать docker-образ через maven/gradle. Работает это через Cloud Native Buildpacks, все правильно раскладывается по слоям и выставляются оптимальные настройки JVM.

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

Но настораживает и другой тренд: сейчас система сборки обрастает плагинами, которые позволяют не только собирать приложение, но еще и управлять его версиями, ветками git, сейчас вот и докер собирать. Что дальше — деплоить на прод через maven, потому что так удобнее? Только в кубер, конечно же. CI/CD не нужны, все пихаем в исходный код!

Как будто и не помнит никто уже про Nero Burning ROM. Супер-аппы в духе времени: почта продает продукты, такси доставляет еду, магазин выдает посылки, банк продает авиабилеты, в инстаграмме постят лонгриды, в IDE встаивают видеочаты и т.д.

Ссылка

Inline-бот для кода

Раньше было лень открывать IDE — открывал онлайн-IDE. Сейчас можно еще больше облениться и просто писать боту в телеге.

@rextester_bot brainfuck
+[--------->++<]>.---------.------.++++++++.-[++>---<]>+.---[----->++<]>.---.---..

Бот — простой фасад к http://rextester.com. Поддерживает все популярные языки. Интересно, какая у них там песочница? Она спокойно позволяет запускать ps aux и ls /. Выпускники ИБ — это ваш шанс почувствовать себя кулхацкерами, не выходя из телеги:)

Ссылка

Принудительное скачивание исходников

Иногда IntelliJ жестко тупит и не скачивает исходники библиотек в проекте с maven, какие бы настройки ни стояли, и сколько бы ты не нажимал “Download Sources”. Помогут команды

mvn dependency:sources
mvn dependency:resolve -Dclassifier=javadoc

Первая скачает все доступные исходники ко всем зависимостям, а вторая — еще и javadoc к ним. В sbt это можно сделать командой sbt updateClassifiers (хотя я это не проверял). А вот в gradle такого вроде нет (с другой стороны, я и не припомню проблем с этим, может IntelliJ была со старыми иконками и работала нормально).

Ссылка

Конвертация pdf ⟷ png

В продолжение темы конвертеров.

convert -density 300 order.pdf %02d.png

сконвертирует pdf в png. А обратно склеить их в pdf можно так:

convert {00..03}.png combined.pdf

Возможно придется включить разрешения, добавив

<policy domain="coder" rights="read | write" pattern="PDF" />

в /etc/ImageMagick-[версия]/policy.xml.

Ссылка

Форматирование Kotlin и обновления

В конце прошлого года столкнулись на старой работе с багом Intellij Idea: после обновления она начала расставлять отступы немного по-другому в kotlin-коде. Сравнивали настройки — все идентично. Была версия, что в Intellij иногда почему-то игнорируется настройка continuation ident. Обновились до идентичных версий — все равно форматируют по разному, сволочи. Порылись в Youtrack’е JetBrains — нет такого бага. Так и забили…

На прошлой неделе коллега вляпался в эту же проблему. Но теперь есть хоть решение, позволяющее сохранить обратную совместимость: Kotlin obsolete IntelliJ IDEA codestyle. Новый стиль, конечно, поприятнее, но никто его в старом проекте менять не будет.

А вообще все чаще наблюдается тенденция, что обновления сферического ПО в вакууме все чаще содержат “новые нескучные иконки” и “поправили 2 старых бага, добавили 5 новых”, а не что-то действительно новое… Ну и/или я уже душный ворчливый дед:)

Ссылка

Чек-лист для алертов

Немного капитана очевидности из личного опыта:

  1. Алерты должны быть. Просто так на графики и дашборды никто смотреть не будет (возможно, кроме первых N дней, когда их только-только настроили).
  2. Алерты должны быть такие, чтобы требовали реагирования. Если реакция на алерт по умолчанию будет “а, это как обычно, это норма”, то вскоре на них и смотреть не будут.
  3. Алертов не должно быть много и в них не должно быть шума. Если за день приходит 500 алертов, то подробно с каждым возиться, разумеется, никто не будет. На старой работе делали для этого экспоненциальные интервалы уведомлений (алерт триггерится сначала раз в 5 минут, потом раз 10, потом раз в полчаса, потом раз в час, потом 2 раза в день и т.п. — точные интервалы не помню, но суть такая) и периодический агрегированный отчет (где собраны все полуважные алерты, не требующие мгновенного реагирования).
  4. Вроде как следствие предыдущего пункта — делать разный уровень критичности алертов, но для этого надо иметь продвинутую систему оповещения. И, вероятно, на 3-4 уровень критичности смотреть уже никто не будет.
  5. Если какой-то метрики, по которой генерируется алерт, нет вообще — скорее всего, на это тоже надо делать алерт. Если “нормальный” фон — 1000 ошибок в час (:sad_pepe:), а потом их внезапно 0 — маловероятно, что все ошибки исправили сразу: скорее всего, случилось что-то поинтереснее.
  6. Продвинутые чуваки делают алерты на неработающую систему оповещения.
  7. Алерты должны быть полными и актуальными. Банально, добавился новый эндпоинт — сразу же алерт к нему надо сделать. Если в алертах не будет актуальной и достоверной инфы, то зачем на них реагировать?
  8. Как следствие, код с настройками алертов должен по быть по максимуму адекватным, а не помойкой из копипасты. Даже если это убогий TICK-скрипт для Kapacitor или поганые YAML-шаблоны Helm’а для Prometheus.
  9. Алерт должен содержать информацию о том, почему этот алерт возник — какой был порог, какой значение сейчас, ссылку на дашборд и т.п. Иначе алерты будут звучать как “что-то пошло не так, сам думай, что и почему”.
Ссылка