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

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

Устройство HTTP

Занятная статья, в первой части которой автор довольно просто рассказывает основные принципы работы HTTP/1.1 (я, например, никогда не задумывался о том, как работает отправка запроса/ответа частями (chunked)), а потом плавненько переходит на куски кода на Rust и подсвечивает кучу занятных вопросов про проксирование траффика HTTP/2 (в т.ч. для защиты от плохих парней). Поразительно, сколько есть серых зон в протоколе, и как хорошо, что в повседневной разработке о них не надо думать :)

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

Удаление дубликатов в таблице-списке

Способов удалить дубликаты в таблице довольно много: с использованием CTE, JOIN, подзапроса, извращений вроде пересоздания таблица через DISTINCT и т.д. Возьмем для примера группировку с USING:

DELETE FROM elements t1 USING elements t2 WHERE t1.some_key = t2.some_key AND t1.created_at > t2.created_at;

Тут одна колонка (some_key) используется для собственно определения дубликатов, а вторая (created_at) — для сортировки их между собой.

Но что делать если в таблице всего одна колонка, как различать два элемента, чтобы удалить не все? Тут пригодятся системные колонки. В данном случае — ctid, “физическое расположение данной версии строки в таблице”:

DELETE FROM list t1 USING list t2 WHERE t1.some_key = t2.some_key AND t1.ctid > t2.ctid;
СсылкаКомментировать

Хуки СУБД и Transactional outbox

В событийно ориентированной архитектуре хуки довольно полезны: можно перед коммитом добавлять в транзакцию все события (transactional outbox, все дела), а после коммита — отправлять их в очередь сообщений. Однако стоит помнить, что количество соединений к базе ограничено и делать в них что-то длительное не стоит. И на эти грабли я наступил в процессе разработки, сделав отправку сообщений в RabbitMQ после коммита: соединения долго висели и в итоге кончались.

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

Уже потом нашел репозиторий-пост, в котором описан практически идентичный случай.

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

Перенос дашбордов Grafana

GitOps предполагает, что артефакты сначала создаются в репозитории, а потом синхронизируются с целью. Но в случае дашбордов это хоть и рабочий вариант, но очень болезненный: все-таки, большинство вещей “накликивается” через веб-морду, а не описываются через JSON (тем более, что в Grafana создать через UI одну панельку, а не весь дашборд, даже из готового JSON весьма нетривиально). При этом создание дашборда через CI позволяет его шаблонировать и относительно легко разворачивать на несколько сред (например, чтобы иметь одинаковый дашборд для stage и prod, если их дашборды в разных местах).

Однако шаблонировать можно и средствами самой Grafana. В дашборде можно создавать переменные (литералы, словари, мультивыбор), причем заполнять значения можно с помощью запроса. При этом переменные можно скрывать. Отдельно отмечу константные переменные — в них очень хорошо хранить то, что меняется между средами (например, namespace, домен и т.п.). Еще в переменные можно складывать частые фильтры и куски запросов, чтобы не копипастить их.

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

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

Эволюция JS-фреймворков

Пост, в котором весьма сжато изложена эволюция web’а от статических страниц до-javascript’ной эпохи до современных MPA-фреймворков. Описаны проблемы, которые возникали в процессе развития этой экосистемы и подходы, которые применялись в фреймворках для их решения. Требует быть чуточку в теме, но в целом хорош для ответа на вопросы типа “чем отличается React от Vue” или “почему SPA уже не модно”.

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

Павлик internal

Продолжаю делать Павликов, потому что все нужное как всегда глубоко закопано и надо выбирать стул. На этот раз на очереди internal, который ограничивает видимость элемента модулем (т.е. если он в какой-то библиотеке, то только библиотека имеет к нему доступ). Его очень любят использовать сами Jetbrains в Ktor, корутинах, Exposed и т.п.

Но на самом деле это только ограничение компилятора самого Kotlin — на уровне java нет никакой видимости “на уровне модуля”, и можно этот internal использовать как угодно. И на уровне Kotlin обойти его тоже элементарно: нужно всего лишь добавить @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE").

При этом многострадальный package-private, который сообщество хочет чуть ли не с релиза, в язык не добавляют. И очень смешно, что один из основных аргументов это “ну, все равно особо ни от чего не защищает, используйте internal”.

В общем, типичная позиция JetBrains — если хочется, то совместимость с java — священная корова, а если очень надо — то и плевать на нее.

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

Поиск алгоритма умножения матриц нейронками

Год назад была работа про использование ML для сортировок. А на прошлой неделе — статья в Nature про использование нейронок для умножения матриц. Что интересно, для поиска оптимального алгоритма задачу представили в виде игры и переиспользовали наработки от AlphaZero (которая обыгрывала чемпионов мира по шахматам, сеги и го).

Как сказал один из моих студентов: “Не знал, что нейронные сети можно использовать для чего-то полезного” :)

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

Приборка подов в k8s

Поды, завершившиеся с ошибкой, могут висеть в кубере до посинения (тикет).

Чтобы почистить все руками, можно выполнить что-то вроде такого:

kubectl -n namespace delete pods --field-selector=status.phase=Failed

Хотя, конечно, лучше разобраться с первопричиной падения подов:)

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

Обучение через использование

Немного капитана очевидности.

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

Ни для кого не секрет, что для закрепления навыка его надо регулярно использовать. И причем использовать для чего-то, должна быть какая-то цель, а не просто “ну, надо поизучать” или “все это делают и мне надо делать”. Для достижения этой цели, возможно придется, не побоюсь этой фразы, “выйти из зоны комфорта” — специально делать что-то не в привычном русле и не с помощью регулярно применяемого и освоенного навыка, а с использованием нового.

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

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