Читать в телеге. Когда-то там были посты не только от меня.
Текущие проблемы с потоками и асинхронщиной
Неплохой доклад про текущее состояние асинхронного выполнения задач в JVM, хоть и очень тезисный.
Вначале немного повторения кэши L1-L3 и устройство процессора в целом, про потоки, очередь задач и их конкурентное выполнение, переключение контекста. Потом внезапно переключается на JVM — каждый поток это корень для сборщика мусора. Чем больше потоков, тем медленнее он работает. Какой-нибудь FixedThreadPool не решает проблемы, т.к. у него случайный порядок выполнения.
Далее — основы асинхронного выполнения и epoll. “Многие из вас плохо читают на C, поэтому я перевел этот кусок кода на Scala”:) Как решить проблему, что потоки ничего не делают, пока ждут? Перекинуть все ожидание на 1 поток. Но даже это дороговато. Эту проблему решили в nodejs-ной библиотеке libuv, которая в одном потоке и полезную работу делает, и ждет IO.
В идеальном мире на каждом ядре работает только один поток, одна задача по максимуму выполняется на своем потоке, чтобы все было хорошо с кэшами. А еще это все спрятано от прикладного программиста. Системы эффектов близки к этому, они хорошо решают проблему управления задачами, но все разбивается о нижележащие библиотеки для работы с потоками.
Sed и разделитель
Оказывается, в sed
можно использовать почти любой разделитель, т.е. эти варианты
sed -i 's/Hello/Goodbye/g' input
sed -i 's%Hello%Goodbye%g' input
sed -i 's Hello Goodbye g' input
sed -i 'ssHellosGoodbyesg' input
будут работать одинаково. В качестве разделителя используется первый символ после s
. Увы, прокатит только однобайтный символ, Ы
не подойдет.
Спонсор это минутки — Ярослав:)
Пофлексить
Интерактивный туториал по основам FlexBox.
В начале порадовала аналогия с шашлыком (главная ось = шампур, элементы = мясо).
А ближе к концу расстроило понимание, что вроде хотели все упростить по сравнению со “обычным” стилем, а получилось как всегда.
Время коммита
Если у вас еще остались надежда на то, что история коммитов в git
хоть сколько-нибудь соответствует действительности, тогда мы идем к вам!
Переписать историю через force push
или изменить авторство — пройденный этап, пора поработать над временем.
GIT_AUTHOR_DATE="2022-11-17T18:00:00 +0200" GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit -m"weekly update"
Вуаля, на каком-нибудь GitHub никто и не заметит.
В этом, конечно, нет ничего удивительного — сервер максимум может знать время пуша (но git
это нигде не хранит) и подтвердить вашу подлинность, если коммит подписан. У джентльменов принято верить друг другу на слово.
Какие изменения принесет kubectl apply?
Если доступ к кластеру имеют несколько человек, и кто-то забыл закоммитить yaml в гит, то можно узнать кластера простым выводом в yaml
, например:
kubectl get deploy --all-namespaces -o yaml > all-deployment.yaml
Однако можно просто посмотреть на отличия манифеста, который есть в репе и того, что есть по факту на кластере:
kubectl diff -f deployment.yaml
аргументы — такие же, как у apply
. Можно еще посмотреть diff
в графическом редакторе:
KUBECTL_EXTERNAL_DIFF=meld kubectl diff -f deployment.yaml
К сожалению, diff
может провалиться из-за того, что поменялись неизменяемые поля (labels
, например). В этом случае придется получить текущее состояние, поправить поля и попробовать diff
еще раз.
Пять стадий принятия саги
- Отрицание — дергаем что надо по HTTP с ретраями.
- Гнев — переходим на асинхронные сообщения.
- Торг — Transactional Outbox решит же большую часть проблем, да?
- Депрессия — 100% надежность не достижима, все равно что-то может сломаться. Но сделаем хотя бы идемпотентно, чтобы ретраить было проще.
- Принятие — ладно, сделаем еще сагу, чтобы хотя бы следить, какие шаги сделаны в рамках процесса.
Объединение stderr и stdout в конвеере
В bash 4.0+ для перенаправления обоих потоков в конвеер вместо
somecommand 2>&1 | nextcommand
можно использовать
somecommand |& nextcommand
Может пригодится для grep
.
Идемпотентность
Одно из базовых свойств, которое стоит поддерживать при асинхронной обработке команд/событий — это идемпотентность: при повторной обработке события результат не должен меняться (разве что какие-нибудь служебные поля вроде updated_at
).
Достичь exactly-once доставки (т.е. когда гарантируется, что событие будет доставлено ровно 1 раз) в распределенных системах практически невозможно, но с помощью идемпотентной обработки и at-least-once системы (т.е. могут быть дубликаты) можно к этому очень близко подойти. В хардкорной реализации каждое сообщение имеет уникальный идентификатор, каждый клиент помнит идентификаторы обработанных сообщений и выкидывает дубликаты. Однако во многих случаях можно поступить проще и немного дополнить бизнес-логику, чтобы она учитывала возможность повторной обработки, благо многие системы поддерживают upsert или его аналог.
Чтобы не было недоразумений, это поведение стоит покрывать тестами. Наверняка в тестах есть какой-то вспомогательный метод, который проверяет поведение системы после получения какого-то события:
fun onReceiving(event: Event, assertions: () -> Unit)
Вот прямо внутрь него можно и встроить проверку идемпотентности: продублировать внутренности два раза, чтобы два раза прислать сообщение и два раза проверить результат.
Порядок ключей в словаре в питоне
В Python 3.6 была оптимизирована память, используемая dict
.
Раньше элементы хранились в массиве, каждый элемент — структура из хэша и ссылок на ключ и на значение. Поскольку используется открытая адресация, то многие ячейки были пусты и прорва памяти тратилась зря.
В новой реализации в основном массиве хранится только индекс, а хэши и указатели хранятся в отдельном списке. За счет этого размер заполненной и пустой ячейки будут одинаковыми, а список с полезной нагрузкой почти всегда плотно заполненным (при удалении там остается заглушка, чтобы список за линейное время не двигать).
Один из побочных эффектов — ключи становятся упорядочены в порядке добавления (в 3.7 это закрепили на уровне требования). С одной стороны — прикольно, но с другой, когда читаешь код, который на это полагается, то так можно и с ума начать сходить (“почему в хэш-таблице ключи отсортированы?!”). Для читаемости лучше все-таки явно OrderedDict использовать.
Неймспейсы в k8s
В документации читаем:
In Kubernetes, namespaces provides a mechanism for isolating groups of resources within a single cluster.
[...]
Namespaces are intended for use in environments with many users spread across multiple teams, or projects.
Однако эта “изоляция” очень условна, и я бы поостерегся ее использовать в мультитенантном смысле. Хотя бы потому, что, например, все сервисы имеют полное имя вида <service-name>.<namespace-name>.svc.cluster.local
и с настройками по умолчанию ничто не мешает из неймспейса user1ns
обратиться к сервису someService
в неймспейсе user2ns
просто используя someService.user2ns
в качестве хоста. Это может иметь смысл в тестовых окружениях, но на проде — страшно.
Еще можно распихать по разным неймспейсам микросервисы, но когда команды разрастутся до “доменов” (групп команд), то это разделение потеряет смысл, т.к. скорее всего у каждого домена будет свой изолированый кластер.
А вот вполне классный вариант применения — отделить в служебные неймспейсы всякие логгеры, операторы, метрики, телеметрию, CI и т.п., чтобы в основном неймспейсе остались только сами сервисы с “полезной” логикой.