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

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

Обманчивая простота API Redis

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

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

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

СсылкаКомментировать

Понятность ошибок компилятора

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

Очень жалко, что автор не добавил в сравнение C++, но мой мем про это вы уже видели.

СсылкаКомментировать

Передача данных — убийца производительности

Занятная статья про проблемы производительности. В основном это выжимка из доклада одного из создателей BLAS и LAPACK (на которые полагается любимый многими numpy, если кто не в курсе). Сам доклад довольно неспешный, первые минут 15 там вообще идут биографические заметки, все основное в статье изложено неплохо.

TLDR: По CPU уже намасштабировались от души, сейчас основная проблема — передача данных. Лучшие компы работают на 5% мощности из-за того, что долго ждут на передаче данных. Раньше можно было сделать 1 вычисление с плавающей точкой на 1 перемещение, сейчас — уже 10-100. При этом очень мало внимания уделяется проектированию ПО так, чтобы оно учитывало особенности железа. Сейчас вполне нормально ситуация, когда асимптотически неэффективый алгоритм будет работать в разы быстрее оптимального просто за счет того, что ему нужно меньше перемещений данных.

СсылкаКомментировать

Обновление версий библиотек в Gradle

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

В Gradle для этого есть несколько плагинов. Основной просто покажет отчет с последними версиями для всех библиотек. Там же есть и ссылки на плагины для автоматического обновления в скрипте билда.

Небольшая проблема в том, что указать версию библиотеки можно кучей разных способов. Простейший — в build.gradle или build.kts. При этом там же ее можно вынести в переменную или в какую-нибудь функцию. Другой способ — каталоги версий, когда версии хранятся в отдельном файле settings.kts или вообще в toml. Разные плагины для автоматического обновления поддерживают разные варианты описания версий — один только в основном скрипте, второй — только в каталогах версий. Первый я попробовал на своем древнем поделии, и там все нормально обновилось (кроме npm, конечно).

Автоматическое обновление имеет минусы. Во-первых, подсовываются иногда beta и RC версии. Во-вторых, breaking changes и вообще списки изменений надо читать самому. Но так-то в проекте все хорошо покрыто тестами и можно не париться, правда же? :)

В Gradle еще есть возможность указать диапазоны версий, например, 1.7.+ — почти как в npm. Но тут я не могу придумать вариант, когда привязка к последней версии лучше, чем воспроизводимая сборка с фиксированными версиями.

СсылкаКомментировать

Список измененных файлов в ветке

Если для какого-нибудь пайплайна надо определить список измененных файлов, то сама команда довольно простая:

git diff-tree --no-commit-id --name-only -r %base_commit_hash% -r %commit_hash%

В качестве %base_commit_hash% и %commit_hash% можно использовать как хэш коммита, так и название ветки (origin/master, например). В CI почти наверняка эти ревизии есть в переменных окружения. В GitLab это CI_MERGE_REQUEST_DIFF_BASE_SHA и CI_COMMIT_SHA.

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

git merge-base master HEAD 

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

СсылкаКомментировать

Тьюринг-полнота почти везде

Хорошая статья-сборник про то, какие системы неожиданно или не очень являются Тьюринг-полными.

TLDR: почти любая система с вводом может быть Тьюринг-полной, и легче построить Тьюринг-полную систему, чем неполную. Есть довольно много способов построить простенький вентиль (с помощью поиска и замены, регулярок, разного времени выполнения операций, правил игр и т.д. и т.п.), а там уже и до прочих вычислений недалеко. В каких-нибудь песочницах или компонентах языка программирования (например, в каких-нибудь шаблонах, printf или mov) это вообще элементарно. Все это имеет веселые последствия для ИБ: потенциально в любой “песочнице”, даже сильно зарегулированной, можно исполнять произвольный код, не выходя из нее. А если еще взять во внимания всякие Row hammer и Spectre, то получается, что можно выполнять произвольный код на хосте, не выходя из песочницы. Хотя есть вялая надежда на языки, которые нарочно не Тьюринг-полные, например, тотальное ФП.

СсылкаКомментировать

let и also в Scala

… давно есть. Аналог let — это pipe:

someCollection
  .map(pewpew)
  .pipe(all => if (all.size % 2 == 0) all.drop(1) else all)
  .sum

а замена also — это tap:

someCollection
  .filter(ewew)
  .map(pewpew)
  .flatMap(...)
  .tap(println) // debug
  .reduce(...)

В статье, кстати, они сравниваются с башевскими | и tee, а также с |> из F#. Так что сама идея этих функций далеко не нова, Kotlin их просто сделал более попсовыми.

СсылкаКомментировать

Удаленная работа с docker

С сервисом докера общение идет через сокет, т.е. ничего не мешает с ним общаться по сети, например, через ssh. Простейший вариант — установить переменную окружения DOCKER_HOST как ssh://${USER}@${HOST}. После этого все докер команды будут выполняться на удаленной машине. Можно немного заморочиться и использовать контексты, переключаясь между ними.

Это может быть удобно во всяких скриптах деплоя, например в gitlab-ci. Вместо заворачивания в скрипт или добавления перед каждой командой ssh user@host... просто писать команды и все. Другое преимущество — переменные окружения будут браться локально, а еще локально будут копироваться артефакты через docker cp container:/container/path local/path.

СсылкаКомментировать

Модель акторов для построения бигдаты

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

Стоит отметить, что система не является основным источником информации (а только для всякой бигдаты), поэтому вопросы того, как делать все транзакционно, тут не поднимаются (впрочем, пациенты довольно изолированы друг от друга, так что это ок, наверно). А так получается практически event sourcing.

СсылкаКомментировать

Модель акторов для построения отказоустойчивой системы

Занятный, хоть и немного старый доклад про проблемы построения отказоустойчивых распределенных систем от автора языка Erlang. Сначала небольшое вступление и полунаглядные примеры, потом на 30 минуте начинается мякоть. Сформулированы 6 правил для потенциального решения проблем:

  1. Изоляция
  2. Конкурентность
  3. Обнаружение ошибок
  4. Понимание, почему они возникли
  5. Обновление кода
  6. Стабильное хранилище

Один из ключевых подходов — “let it crash”. Убийца — дворецкий, Erlang и модель акторов решают все проблемы:)

Я бы посмотрел на этот доклад с двух сторон. Во-первых, со стороны текущих проектов: а как это решается не Erlang’ом? Кубер, телеметрия, helm, postgres, какая-нибудь очередь для доменных событий — вроде уже типовой стек и он почти работает, да? А во-вторых, докладу скоро 10 лет — хорошо ли он состарился?

СсылкаКомментировать