Читать в телеге. Когда-то там были посты не только от меня.
Локальный запуск билда Gitlab
Как отладить непонятную проблему в CI, если нет доступа к внутренним логам? Запустить локально ее агент, благо сейчас все в контейнерах.
Но что делать, если в локально запущенном агенте билд все равно проходит? Даже если использовать агент с нужной версией?
Один из вариантов ответа — переменные окружения, которые локально, разумеется, отличаются. Однако это легко поправить — можно запустить любую команду, которая выплевывает эти переменные (например, env
) и скопировать себе (со всеми секретами и приватными ключами, которые любезно будут выведены).
В моем случае проблема была в том, что кто-то запихал в переменные окружения много всего, а в одной из задач gradle они копировались в SystemProperties
. При запуске этой задачи в отдельном процессе она тупо давилась их количеством.
Рейтинг постов канала в Telegram
Накалякал скрипт, который качает посты из указанного чата в телеге и сортирует их по метрикам.
Внезапно, самым сложным оказалось найти версию библиотеки Telethon, которая и реакции поддерживает, и еще не сломана в процессе обновления на v2. Другую я заленился смотреть, но благо узнал, что можно ставить pip
-пакеты прямо с GitHub’а:
pip install 'git+https://github.com/LonamiWebs/Telethon.git@539e3cb8081acbd9a5cc7a61c0731ca62842597e'
К сожалению, сам анализ никаких больших открытий не дал — посмотрел топ-посты в паре каналов с мемчиками, да и все.
SQL на csv
Прикольный рецепт как выполнять SQL запросы на CSV-файле: данные загружаются в in-memory sqlite, а там уже можно делать с ними что угодно.
Чтобы не запоминать зубодробительную строку, можно добавить функцию в ~/.bash_aliases
:
function sqlcsv() {
filename=$1
shift
sqlite3 :memory: -cmd '.mode csv' -cmd ".import $filename data" "$@"
}
Причем можно использовать как интерактивный режим, если передать только имя файла:
$ sqlcsv rus_cities.csv
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> select * from data where source='омск' limit 3;
"омск","кадников","о"
"омск","казань","о"
"омск","калач","о"
так и неинтерактивный, если передать один или несколько запросов:
$ sqlcsv rus_cities.csv "SELECT DISTINCT(source) from data where source like 'ц%';" \
"SELECT COUNT(DISTINCT(source)) from data WHERE source like '%ц' or source like '%цы';"
"цивильск"
"цимлянск"
"циолковский"
15
Конечно, это все можно сделать и в Excel/аналоге, но я лично потрачу больше времени на гугление нужных формул, чем на написание SQL-запроса.
Камеры на созвонах
Уже в нескольких утюгах обсуждают статью, в которой проводятся результаты нескольких исследований:
- 103 человека медицинской компании, по результатам 4 недель на основе опросов: люди устают больше с камерами, камера незначительно влияет на вовлеченность, женщины и новички от камер устают больше.
- опрос 10 000 человек, которые регулярно используют камеру на созвонах: женщины устают от встреч больше, более взрослые люди — меньше.
- 150 обучающихся: студенты на лекции, где были включены камеры, лучше сдавали тесты.
На мой взгляд, исследования недостаточно глубокие и разносторонние. Второе более-менее логичное (кому важно хорошо выглядеть в глазах других, тому тяжелее дается общение), но там нет контрольных выборок (созвоны без камер, очные встречи и т.п.). Еще стоит отметить эффект зеркала — если много смотреть на себя в “зеркало”, то это приводит к стрессу и тревожности. Третье исследование довольно узкое, а первое — наоборот, недостаточно подробное: у встреч могут быть кардинально разные цели. И ни в одном исследовании нет контрольных выборок.
В исследованиях плохо затронут вопрос повестки и качества встреч. Можно весь день провести на бессмысленных созвонах, куда тебя позвали просто “ну а вдруг надо будет”, где нет четкой повестки — там без камеры можно хоть своими делами позаниматься. А если на встрече все участники четко понимают полезность и необходимость, у встречи есть четкая цель — то там даже без камер будет продуктивно.
При этом многие источники указывают на то, что включенная камера повышает вовлеченность и помогает развивать эмпатию. Я в целом с этим согласен, но сначала стоит решить вопрос с необходимостью встречи. Добавлю, что вести семинар для студентов с выключенными камерами, где тебе отвечают 2-3 человека — очень грустно, не хватает обратной связи (особенно, когда на очном их меньше, но они активнее).
Версионирование библиотек
Интересный доклад про то, какая бывает совместимость между разными версиями библиотеками, как безопасно приносить новое и удалять старое.
Вообще целевая аудитория — разработчики библиотек, но и пользователям тоже полезно, чтобы понять, почему нужно писать всякие @ExperimentalAPI
или @OptIn
и как Kotlin дошел до жизни такой.
Как выиграть в "города" (России)
Ну, чтобы не запоминать все 1113 штук, разумеется. Обычная тактика — долбить в какую-то редкую букву (т.е. запомнить максимальное количество городов, кончающихся на определенную букву) или наоборот, запомнить кучу слов на “К” и на все редкие. Ну или не играть в такие игры вообще :)
Можно проанализировать граф: скачать с Википедии список (использовав, например, готовый парсер таблиц). А вот для визуализации можно использовать… непонятно что. Есть миллиард библиотек на питоне и js, но это ж код надо писать. Есть онлайн-инструменты типа CosmoGraph, но опций там кот наплакал. Пришлось расчехлять Gephi, чтобы обнаружить, что в нем нельзя отобразить нормально петли или экспортировать в GraphViz. Но лучше я ничего не нашел (если есть что-то прикольное, посоветуйте, пожалуйста).
У меня была идея найти гамильтонов на всем графе слов, чтобы найти непрерывную цепочку городов, но простейшие размышления привели к выводу, что такого пути нет. Да и анализировать граф игры удобнее, когда вершинами являются буквы, а ребрами — слова (как на картинке).
Из интересного:
- на “Й” начинается только Йошкар-Ола, а кончается — масса других. Но если приравнять “Й” к “И”, то будет тяжело.
- лидеры по окончаниям вполне ожидаемо “К” и “А”, а также “О” за счет бывших деревень и “В” в честь людей.
- хороший кандидат на редкую букву — это “Ц”. Вряд ли кто вспомнит 3 города: Цивильск, Цимлянск и Циолковский, а вот запомнить самому Череповец, Люберцы, Елец, Луховицы, Сланцы — легко (и еще 10 будет в запасе).
- еще можно попробовать долбиться в букву “Я”, но там 15 кончающихся против 11 начинающихся — надо будет специально запоминать (впрочем, кроме Якутска и Ярославля без подготовки тяжело что-то назвать).
Пирамида код-ревью
В продолжение темы про код-ревью — шпаргалка про то, чему стоит больше уделять внимание.
Отмечу, что она ничего не говорит про порядок проверки. И, разумеется, работает только при налаженном процессе: если кто-то хреначит коммиты в основную ветку без CI (потому что “знает, как надо”), а документации нет (даже в формате OpenAPI), то говорить тут о качестве ревью рано. Кроме того, тут еще не покрыт вопрос качества требований и их осмысления/анализа.
Миграция очередей в RabbitMQ
Как известно, в RabbitMQ не все настройки очереди можно изменить после ее создания. Но что делать, если все-таки понадобилось их поменять — например, если были изменены настройки TTL или добавлена dlq?
Одно из решений — делать все на стороне клиента или на уровне сообщений, чтобы самой очереди вообще не нужны были настройки. Но это скорее велосипед, который чреват багами (если этот велосипед пишете вы).
Другое “простое” решение — временно остановить работу с очередью, потом куда-то временно переместить данные и пересоздать ее. Увы, тут страдает доступность приложения и можно потерять данные.
Более продвинутые варианты вертятся вокруг создания новой версии очереди — в новом vhost, exchange или просто с новым именем. Зависит от потребителей и схемы релиза, но общая идея одинаковая — создать новую очередь, переключить все на нее, переместить все сообщения из старой (с помощью Shovel, например) и удалить ее.
В моем текущем проекте используется версионирование exchange и это отражается еще в имени очереди. Если надо поменять настройки, то перед релизом увеличивается версия настроек, создается новый exchange и очереди. После процедуры релиза запускается утилита, которая создает shovel’ы для переноса данных из очередей “неправильных” версий, ждет их завершения и удаляет старое. Что не удалилось — можно посмотреть ответственному за релиз.
Дополнительный плюс такой схемы — новые инстансы производят и потребляют сообщения только в своем контуре, который не как не связан со старыми инстансами.
Keep alive для ssh
Подключаешься такой по ssh
к серваку, отвлекся на пару минут, возвращаешься в консольку с ssh
, а она уже на ввод не реагирует. Бесит жутко.
Чтобы такого не было, можно настроить keep alive на сервере (но кто ж вам даст) или у себя в ~/.ssh/config
:
Host *
ServerAliveInterval 200
Почему это не включено по умолчанию — загадка :(
Dead letter queue для очередей RabbitMQ
Простейший вариант обработки сообщений из очереди — слать ack
после успешной обработки и nack
с requeue=true
при временной неудаче.
Однако такой подход работает только для временных/разовых ошибок, при серьезной проблеме потребитель будет пытаться обработать сообщение до посинения с маленьким интервалом между попытками.
Чтобы этого избежать, можно создать для таких сообщений специальную очередь — dead letter queue (dlq) и для серьезных ошибок слать nack
с requeue=false
.
А чтобы сделать несколько попыток, для каждого сообщения в dlq можно назначить TTL, и установить в качестве dlq для dlq оригинальную очередь.
Тогда при истечении TTL сообщение из DLQ попадает обратно в оригинальную очередь с заголовком, содержащим число “смертей”.
Потребитель может проверять это число и, если оно превысило предел, не обрабатывать сообщение совсем, но послать ack
.
Таким образом, TTL для dlq служит интервалом между попытками.
Если в сообщениях есть ценная информация и выкидывать их совсем нежелательно, то при превышении числа попыток потребитель может их складывать в третью очередь (parking lot) для последующего ручного исправления.
С точки зрения настроек RabbitMQ нужно добавить к параметрам оригинальной очереди
x-dead-letter-exchange = ""
x-dead-letter-routing-key = "dlq_queue_name"
а к параметрам dlq
x-dead-letter-exchange = ""
x-dead-letter-routing-key = "original_queue_name"
x-message-ttl = 100500 # milliseconds
В обоих случаях используется стандартный обменник, чтобы перенаправлять сообщения напрямую. TTL можно еще устанавливать для каждого сообщения индивидуально (если хочется иметь экспоненциально увеличивающуюся задержку), но это придется делать уже на стороне потребителя.
Разумеется, возможны и другие сценарии (с общей dlq для всего, например), но описанный — один из самых простых.