Читать в телеге. Когда-то там были посты не только от меня.
ConditionalOnMissingBean
Неприятным сюрпризом оказалось поведение аннотации @ConditionalOnMissingBean
. Казалось бы, название говорит о том, что бин добавляется в контекст только тогда, когда нет бина с таким же классом (или с классом, который указан в параметре аннотации).
Т.е. если есть
@Configuration
class MainConfiguration
@ConditionalOnMissingBean(MainConfiguration::class)
@Configuration
class TestConfiguration
то TestConfiguration
загрузится только в случае, если в контексте нет MainConfiguration
. Однако если переименовать TestConfiguration
→ ConfigurationTest
, то будут загружены обе конфигурации. WTF, переименование класса ломает функциональность?
На самом деле, все очень просто: загрузка бинов в контекст по умолчанию производится в лексикографическом порядке, а @ConditionalOnMissingBean
смотрит только на контекст, загруженный на данный момент, и во втором случае бин MainConfiguration
действительно пока еще не создан при загрузке ConfigurationTest
. Конечно, документация явно говорит об этом и предлагает использовать @ConditionalOnMissingBean
только для автоконфигураций. Решить эту проблему можно явной аннотацией @Order
или профилями, но сделать это можно только для контролируемых классов. И ради тестов такое делать — перебор. Но ни через какой @Conditional
это сделать не получится, т.к. он не влияет на порядок загрузки бинов.
Авторизация на уровне запросов к БД
Наткнулся на интересный прием для авторизации SQL-запросов: добавлять к каждому запросу join к табличкам из RBAC. Т.е. в каждый запрос прокидывается пользователь, и результаты выборки дополнительно фильтруются с учетом имеющихся у пользователя привилегий. Например, если идет запрос в таблицу с публикациями, то добавляется условие, что в таблице привилегий у пользователя либо полный доступ, либо есть доступ к таблице публикаций. Если доступ есть — то будет нормальный ответ с публикациями, если нет — пустой результат.
На первый взгляд выглядит, как костыль: авторизацию лучше делать на более верхнем слое логики (а то так можно и до хранимых процедур дойти). Вдобавок, ломается логика: ошибка авторизации неотличима от пустого ответа. Но я, наверно, слишком привык к мандатной модели доступа, когда есть всего несколько уровней привилегий (например, читатель, писатель, админ).
Есть как минимум три нюанса, при возникновении которых эта схема будет иметь смысл.
- Экономия количества походов в таблицы. Можно привести такой пример: с публикациями мы должны проверить, что есть право публиковать вообще (пользователь не read-only), что человек состоит в канале, что у него есть роль админа-публикатора. Если авторизацию контролировать на более высоком уровне, то в примере, в зависимости от организации БД, могут быть запросы к трем и более таблицам.
- Устаревание настроек привилегий из-за неатомарности операций. Есть транзакции, да, но не всегда получится все делать в рамках одной транзакции. А тут все права проверяются в рамках одного запроса.
- Мультитенантные системы (одна база с кучей пользователей). Конечно, это обычно звоночек про то, что данные организованы/изолированы не очень хорошо, но обстоятельства разные бывают, и иногда data access control необходим.
Вообще, приятнее конечно работать с простой моделью доступа. Она тупо удобнее и проще в реализации. Если цена ошибочного действия не очень высока и/или оно относительно легко поправимо, то достаточно будет просто добавить аудит. Применение супергранулярным системам доступа я вижу только в критичных системах (банки, ИБ) и бигдате. Да и даже там это не гарантия качества и безопасности — кожаные мешки умеют ошибаться назло всем ограничениям.
Медленный printf
В продолжение поста про оптимизацию буферов. Можно выжать еще больше из скорости записи, если отказаться от printf, о чем написано в топ-статье. Вообще довольно интересная статья про оптимизацию FizzBuzz, и автор еще там разворачивает циклы, использует векторные инструкции и заполняет буфера справа налево — рекомендую почитать наискосок хотя бы.
Еще один интересный факт — printf, оказывается, Тьюринг-полный. Почитать подробнее можно в статье про атаки на поток управления.
Rollout restart в kubernetes
Для обновления конфигов бывает полезна команда
kubectl rollout restart deployment your_service_name --namespace your_namespace
после которой экземпляры сервиса будут перезапущены по одному. Но иногда настройки прав доступа могут быть кривыми и не разрешать эту операцию.
Чтобы то же самое сделать вручную, нужно:
- Масштабировать сервис вверх на 1, чтобы не было алертов и/или проблем с нагрузкой.
- Удалить один из старых подов (UI обычно показывает дату создания).
- Дождаться, пока не будет поднят автоматически новый под на замену старого.
- Повторить пп. 2-3 пока не останутся только новые поды.
- Масташбировать сервис вниз на 1, чтобы вернуть все как было.
Routing key в RabbitMQ
Ключ маршрутизации (routing key) нужен для маршрутизации сообщений из обменника (exchange) в подписанные очереди. В простейшем варианте используют обменник по умолчанию (пустая строка), а в качестве ключа — имя очереди. Этот же способ можно использовать для прямой публикации в нужную очередь, в обход всех маршрутизаций.
Ключ маршрутизации проставляется при публикации сообщения в обменник. Какой-то смысл он имеет только для обменников с типом direct и topic. Для типа direct маршрутизация осуществляется по точному совпадению ключа маршрутизации: если очередь подписана на ключ маршрутизации memes
, то в нее будут попадать только сообщения с ключом маршрутизацией memes
.
Для обменников с типом topic можно читать сообщения по шаблону ключа маршрутизации, но сам ключ должен состоять из слов, разделенных точкой. Например, если очередь подписана на memes.dank.pepe.*
, то в нее попадут сообщения с ключом memes.dank.pepe.peepo
и memes.dank.pepe.monka
, но не попадут memes.dank.doge
и memes.dank
. Однако *
замещает ровно одно слово, поэтому в нее не попадут и сообщения с ключом memes.dank.pepe
или memes.dank.pepe.feels.good.man
. Чтобы получать все мемы про Пепе, нужно использовать #
, которая замещает 0 или более слов: memes.dank.pepe.#
.
P.S. Спонсор сегодняшней минутки — Саша :)
Генерация пароля
Для сайтов пароли генерируются браузером, а вот сгенерировать пароль для доступа к базе иногда нужно.
tr -dc 'A-Za-z0-9!@#$%^&*()_+' </dev/urandom | head -c 13 ; echo
Флаг -dc
говорит о том, что нужно взять только выбранные символы из входного потока.
Pattern matching в Python
Если вы не слышали новость, которая лезет из каждого утюга, этим утюгом побуду я.
В питоне появится паттерн-матчинг! Примеры можно посмотреть в репозитории Гвидо ван Россума. Причем это не просто switch с добавками, а вполне полноценная штука с поддержкой литералов, захвата в переменную, поддержкой списков/словарей, деструктуризацией, паттерн-гардами и вложенными паттернами. Даже моржовый оператор туда пихнули. До скаловской мощи не дотягивает, но этих фич более чем достаточно для бытовых нужд.
GUI для kafka
Официального UI, как, например, у RabbitMQ, для Kafka нет, но есть довольно много вариантов от сообщества.
Пожалуй, самый распространенный ‒ Kafka Tool, в котором можно сделать довольно базовые вещи вроде просмотра оффсетов и создания/удаления топиков. На прошлой работе в основном использовал kafka-manager, который вдобавок к этому умел позволял управлять партициями и перекидывать их с брокера на брокер. Однако, с момента, когда я в последний раз занимался этим вопросом, появились и новые варианты: платный Conduktor, Kafka-ui от Provectus, AKHQ …
Кажется, пока Confluent не выберет что-то или не сделает что-то свое, будут продолжать плодиться новые варианты интерфейса.
RISC
Говорят, одним из факторов успеха нового процессора от Apple было использование RISC, хотя все современные “CISC” x86-процессоры на самом деле внутри работают как RISC. Интересно, будет ли в будущем движение в сторону уменьшения числа операций или увеличение специализации продолжится без этого? Если покопаться, то у RISC есть куча вариаций:
- minimal MISC, который был у древних компов, но там были проблемы с параллелизмом на уровне команд.
- one OISC, теоретический вариант с одной инструкцией (чтобы править ими всеми) ‒ привет машине Тьюринга:)
- и даже zero ZISC, который попахивает какими-то спекуляциями.
Плюс еще миллион наборов инструкций, среди которых SPARC, RISC-V, MIPS и ARM.
Пост стыда
Когда искал примерчик кода к предыдущей заметке, обнаружил, что у меня в гитхабе висит нерабочее тестовое задание, которое я делал год назад при смене работы.
https://github.com/ov7a/orders-matching
Причем оно настолько не рабочее, что там даже кусков кода нет. Я отчетливо помню, как я писал логику в теле этих методов, что тестов было больше, что я вообще-то локально все запускал и проверял. Косвенно об этом readme говорит.
Причем, что забавно, это тестовое я даже посылал рекрутеру и мне пришел ответ, что все хорошо, даже звали на собес (если текущий фаворит откажется от оффера).
А сейчас уже и не вспомню, что там было — надо будет выделить выходной, чтобы это оформить прилично.
В общем, не будьте как я — перепроверяйте то, что выкладываете как пет-проекты на гитхаб и делайте бэкапы:)