Читать в телеге. Когда-то там были посты не только от меня.
Контейнер Морозов
Продолжаю “ломать” изоляцию сетей:) Давным-давно писал про перенаправление портов, там среди прочих указан port-forward
kubectl port-forward some_service 5672:5672
это прекрасно работает, когда сам сервис развернут в кластере k8s. Но что делать, если он виден, но находится в другом кластере/сети? Форварднуть порты еще раз с помощью временного контейнера :)
kubectl run tmp-port-tunnel --rm --image=alpine/socat -it --expose=true --port=5672 tcp-listen:5672,fork,reuseaddr tcp-connect:some_host:5672
Работа с эффектами в Scala
Хороший видос про этапы развития Scala с точки зрения работы с эффектами (с небольшим предисловием про Haskell) — better Java, акторы, Future, Monix, Scalaz-stream, Cats, FS2, Cats Effect, ZIO.
Кроме этого, Нижников еще рассказывает про общие принципы работы IO монады (звучит страшно, но на самом деле все довольно просто) и сравнивает Cats Effect и ZIO.
Значение по умолчанию в SQL-запросе
Как получить данные по запросу, а если их нет, то использовать значение по умолчанию?
Элементарно, но только если не надо потом эти данные сразу же вставить, да все это еще и в одном запросе. Если бы не было нужно значение по умолчанию, то это тоже решалось бы легко подзапросом.
Можно подзапрос объединить через UNION
с временной табличкой, содержащей строку по умолчанию, но тогда надо очень аккуратно выбрать правильное значение дополнительным условием (возможно, через флаги с их последующим вырезанием).
В итоге у меня родился такой монстр:
INSERT INTO stats(day, key, value)
SELECT '2022-08-04', key, COALESCE(stats."value", 0)
FROM (VALUES(key)) as request(key)
LEFT JOIN stats on request.key=stats.key
WHERE (stats.value IS NULL) OR (stats."day" < '2022-08-04')
ORDER BY stats."day" DESC
LIMIT 1
ON CONFLICT DO NOTHING
Тут производится LEFT JOIN
с временной таблицей request
, чтобы всегда получить хотя бы 1 строку, в которой будет либо null
, либо ответ на запрос. Потом COALESCE(stats."value", 0)
выбирает первое не-null значение.
Запретный плод для Java
Один из популярных вопросов начального уровня на Java-собесе — “можно ли переопределить статический метод?”. И правильный ответ — нельзя.
Однако, если очень надо, то это возможно:
public class Foo {
public static void bar(){
System.out.println("Foo");
}
}
Foo.bar()
Mockito.mockStatic(Foo::class.java)
whenever(Foo.bar()).then { println("not foo") }
Foo.bar()
выведет:
Foo
not foo
Разумеется, если вам это вдруг нужно, то с большой вероятностью вы делаете что-то не то (мало того, что у вас рак в виде java, так еще и со статическими методами, которые требуют переопределения). Однако мне это пригодилось, когда понадобилось протестировать работу с переменными окружения, которые как раз засунуты в статический метод System.getenv()
(для этого я использовал system stubs).
Запретный плод
>>> (666).boo()
I'm just a 666, but boo!
>>> "I can be JS too! " + 2
'I can be JS too! 2'
Одна из фишек питона — возможность monkey-patching. Но многие считают, что у него есть предел, и нельзя патчить стандартную библиотеку. Но на самом деле можно:
>>> from forbiddenfruit import curse
>>> curse(int, "boo", lambda x: print(f"I'm just a {x}, but boo!"))
>>> (666).boo()
I'm just a 666, but boo!
>>> curse(str, "__add__", lambda x,y: x + str(y))
>>> "I can be JS too! " + 2
'I can be JS too! 2'
Это знание позволит вам поиздеваться над коллегами и еще меньше доверять написанному на питоне. Ходят слухи, что библиотека была сделана для тестов, но благими намерениями…
Доступ к базе в приватной сети
Как посмотреть что-то в базе, если она в частной сети, в которую нет прямого доступа, а девопсы не настроили ничего вроде VPN? По идее, она должна быть видна приложению, но в стильно-модно-молодежной среде приложение лежит в контейнере, в котором ничего для доступа к базе (например, psql
) нет.
Решение достаточно простое — запустить одноразовый контейнер с чем нужно.
docker run --rm -it postgres:14 /bin/bash
или
kubectl run -it --rm tmp-psql --image=postgres:14 --restart=Never -n prod --command /bin/bash
Интересных флагов тут два: --rm
, который говорит о том, что надо удалить под/контейнер после использования, и --restart=Never
для того, чтобы создался только просто под, а не deployment.
(Как все просто было во времена железных серверов — просто провалился через ssh
на хост и все).
Тестовые флаги
Использование условий вида if (test)
или if (user in testUsers)
— это рак.
Во-первых, половина программирования, если не больше — про управление зависимостями. Если реально нужно отключить какую-то функциональность на тестовой среде (оплату какую-нибудь), то есть куча нормальных способов это сделать: например, подсунуть класс-заглушку или http-заглушку, если вы живете в микросервисной архитектуре.
Во-вторых, тестироваться будет не основной код, а костыли внутри условия. Причем подобные костыли часто просят добавить как раз тестировщики.
В-третьих, это источник багов (а иногда и дырок в безопасности). Сейчас не каждый понимает необходимость юнит-тестов, а уж если начнешь заикаться о тестировании тестового кода — сразу в дурку поведут.
Иногда подобным образом переключают часть функциональности (хотя для этого есть feature-флаги). Иногда так еще “упрощают” жизнь для нагрузочных тестов (т.е. тестируют не настоящую работу) или “для отладки” (хотя для этого есть ленивые логи с уровнями логгирования или внешняя инструментация).
Мне тяжело придумать нормальные обстоятельства, когда использование подобных условий будет оправдано. А поддерживать их накладно, они осложняют рефакторинг и засоряют код.
Suspend функции в Scala
В продолжение темы об излишних переусложнениях — на форуме Scala обсуждают предложение о добавлении корутин в стиле Kotlin. Со всеми недостатками двухцветных функций. Рекомендую почитать критику от де Гуза (автора ZIO) и сдержанный ответ Одерски (автора языка). Вкратце, suspend
не нужОн, если зеленые потоки есть из коробки, и они планируются в рамках проекта Loom. А в Scala Одерски видит перспективы в более мощной типизации и в capabilities.
Context receivers
В рабочем проекте попробовал добавленные недавно context receivers. Вкратце, это очередной сахар, который позволяет требовать, чтобы функция вызывалась в контексте, содержащем какой-то класс как this
:
context(Logger, DBConnection, Request)
fun someFunc() {
log(select(request.param))
}
Под капотом компилятор генерирует дополнительные параметры для вызова функции.
В текущей версии (1.6.21) все очень сыро — я споткнулся об пару багов компилятора и пару ошибок во время исполнения в довольно простых случаях. Для прода это точно не готово (я использовал в тестах). Могут получиться уродские лямбды:
suspend fun someFun(
block: suspend context(Logger, DBConnection) Request.(Param) -> Unit
)
Компилятор не всегда понимает, из какого контекста метод (и я натыкался на случаи, когда ему не удавалось это объяснить). Как и с suspend
, функции приобретают “цвет”. У this
непонятный тип — вроде объединение, но явно это не получить. Наконец, форматирование съезжает:) Больше критики можно почитать тут, похвалу — тут, а больше вариантов применения — тут.
Без сахара подобные задачи можно решать созданием одного класса Context
и вызова функций с дополнительным параметром(ами) или через receiver: fun Context.someFunc()
. Была бы поддержка типов-объединений, то тогда это было бы гораздо более читаемо (особенно с typealias
). Ну а так вообще это все ради того, чтобы неявно передавать аргумент в функцию. И подобную проблему уже давно решили с помощью имплиситов в Scala. В общем, мне кажется, что начинается излишнее переусложнение, которое еще и хреново реализовано.
Разделение рабочего и личного на ноуте
Весьма скоро после перехода на удаленку рабочие задачи и личные почти полностью стали выполнятся на одном компьютере.
Сначала я делил их путем создания второго пользователя. Но это оказалось не очень удобно — надо было переключаться, ради какой-то мелочи это было делать лень. Кроме того, некоторые вещи в личном и рабочем пересекались. Например, я городил какие-то костыли с правами, чтобы Dropbox считал двух пользователей за одного.
Потом я пробовал разделить устройства — держал древний домашний стационарник в спящем режиме и включал при необходимости. Но много работать на компе, которому недавно исполнилось 10 лет — так себе удовольствие.
Сейчас я разделяю два пространства попроще — просто два рабочих стола (виртуальных) для смены контекста, и выделенный браузер для чисто рабочих задач. И еще одно место, где требуется разделение — git. Там настроено два профиля под разные директории:
$ cat ~/.gitconfig
[includeIf "gitdir:/home/ov7a/github/**"]
path = ~/.gitconfig.personal
[includeIf "gitdir:/home/ov7a/work/**"]
path = ~/.gitconfig.work
$ cat ~/.gitconfig.personal
[user]
name = ov7a
email = home@address.ru
$ cat ~/.gitconfig.work
[user]
name = Vlad Chesnokov
email = some.work.email@company.com
Т.е. все репозитории в рабочей папке будут иметь рабочий акк, а в папке под всякую дичь — личный.
И… все, пока потребности разделять что-то еще не появилось.
P.S. В первое время удаленки я даже заморачивался и разделял работу и дом сменой футболки, и это реально помогало. Но летом в жару 30+ я на это быстро забил:)