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

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

Безопасность SELECT

Что может страшного случиться, если вызвать SELECT? С лимитом, разумеется. Он же только читает данные, ничего не случится?

Через SELECT можно:

  • вызвать хранимую процедуру/функцию, в которой может быть что угодно;
  • обратиться к вычисляемому полю, которое в свою очередь может дергать функцию;
  • выбрать запись для обновления и не отпустить лок;
  • просто захватить лок (например, advisory-lock);
  • вызвать агрегирующую функцию, которая затрагивает все данные, но возвращает один результат (поэтому лимит не поможет снизить нагрузку, но она может быть не критичной);
  • вызвать перезапись данных: при записи большого числа блоков часть из них может быть вытеснена из кэша с необновленным битом статуса транзакции, любое последующее обращение к таким блокам (например, select) вызовет обновление этого бита (чтобы он соответствовал действительности) и, как следствие, вызовет перезапись всего блока;
  • тупо “вымыть” данные из кэша;
  • стриггерить триггеры (например, аудит).

Вдохновлено кусочком этого доклада, во многом помог Игорян, за что ему большое спасибо!

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

Мои впечатления от Svelte

Решил попробовать, “каково это там на фронте” и выбрал Svelte, как самый любимый разработчиками веб-фреймворк по результатам опроса StackOverflow (к которому стоит относиться с опаской, потому что на втором месте ASP.NET Core, а на седьмом — Spring). Ключевая фишка — отсутствие виртуального DOM. Я вообще сначала думал, что он будет чуть ли не SSR и компиляция, но в процессе изучения понял, что ошибался.

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

Основа интерактивности компонентов — во взаимодействии через привязку к переменным. Привязка может быть односторонней, а может быть двухсторонней. Двухсторонняя может быть только в случае, если свойство элемента может быть изменено пользователем — это не очень очевидно и немного не универсально: если компонент написан на чистом JavaScript, то привязаться к нему надо будет через события или через промежуточный слой. А еще не будет работать привязка на изменяемое свойство контролируемой переменной, потому что обновление вызывается при присваивании самой переменной. Условной привязки нет, поэтому чтобы заставить по галочке двигаться два ползунка одновременно, а при ее снятии — независимо, мне пришлось накостылить решение через Store, который обычно используется для сложных взаимодействий по PubSub модели. Можно было сделать через события, но получилось бы не сильно лучше, имхо.

Синтаксис шаблонирования в HTML выглядит довольно простым, жаль только, что в условиях обычный JavaScript, а не TypeScript. Впрочем, ничего сложного я с ним и не делал, в Jekyll’овском шаблонизаторе на грабли тоже не сразу наступил.

Не очень очевидным оказался процесс инициализации, потому что некоторые вещи надо вызывать через onMount, после привязки к DOM, а некоторые, например, код в “статическом” блоке — сразу.

С SSR ждало разочарование — из коробки его нет, настраивать не очень понятно как, одобренный способ еще не устаканился. “Compile-centric” тоже не ощутил: да, все зависимости в dev, но почти все ошибки все равно ловить через отладку в браузере.

По дороге меня еще ждали типичные проблемы с CORS, которые я обошел использованием allOrigins, сложности CSS (я по-прежнему офигеваю, как там тяжело сделать “простые” вещи, например, вертикальное выравнивание текста по середине). Немного споткнулся и о TypeScript — работа с undefined оставляет желать лучшего, импорт типа ведет себя не очень очевидно, но тут я скорее не все понял.

Отдельной болью была отладка и адаптация под планшет. Вляпался в отличия обработки нажатий мышкой и пальцем, неработающую запись без https, отсутствие поддержки MediaRecorder (пришлось использовать полифилл, который поддерживает API не полностью). Изюминкой было то, что если не сработала привязка в элементе, то браузер показывал просто пустой экран — и попробуй догадайся, что ему не понравилось — на планшете нет консоли разработчика, подключать ее геморройно. А проблема была в том, что не поддерживался оператор ?..

В целом впечатления остались скорее приятные. От Svelte у меня почти не горело :). Идея инкапсулировать компонент в одном файле и потом использовать как HMTL-тег на мой взгляд довольно прикольная. Выглядит похоже на Vue (как он мне смутно запомнился по мимолетному знакомству).

P.S. Не знаю, зачем я продолжаю заниматься этим мазохизмом с фронтендом :) Но возможность показать что-то доступное для интерактива немного греет.

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

Git read-tree

В гите достаточно много способов скопировать данные из одного репозитория в другой. Один из них — read-tree. Ключевая идея: берем поддерево одного репозитория и вставляем его в дерево другого. Демка для зумеров.

Мне это пригодилось для подключения репозитория на сайт. Делал на основе п.2 отсюда:

git remote add -f -t main --no-tags github-pr-stats git@github.com:ov7a/github-pr-stats.git
git read-tree --prefix=github-pr-stats -u github-pr-stats/main:build/distributions
git commit

Итоговый коммит.

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

Проверяемые исключения в Scala

В релизе 3.1.0 появилась интересная фича: проверяемые исключения. Да-да, как в java, но немного по-другому.

Во-первых, проверка не обязательная, а специально подключаемая в импортах. Оборачивать все в try-catch чисто ради удовлетворения компилятора не обязательно. Во-вторых, команда разработчиков попыталась хотя бы частично решить проблемы java-подхода.

Одна из основных проблем с непроверяемыми исключениями в том, что… их не проверяет компилятор, они вне системы типов. Поэтому некоторые упарываются и отказываются от исключений совсем, заворачивая все в монады. Это генерирует много новых проблем, например, вопрос композиции этих монад (куча паттернов типа Final Tagless или Free Monad и супермонады типа ZIO — это все варианты решения). Но код с исключениями легко писать и читать, да и “happy path” не несет накладных расходов.

В новой версии компилятор проверяет, что если в теле функции есть throw, то исключение прописано в сигнатуре. Внутри сахара — контекстные параметры, которые будут удалены после компиляции. Это как неявные параметры (implicit val) в scala 2. Т.е. если функция может кидать исключения, то она требует неявный параметр, который свидетельствует о том, что оно будет обработано. А генерится этот параметр уже в блоке try-catch.

Увы, не все так идеально, как хотелось бы: есть проблема с объявлением по-настоящему чистых функций и с оборачиванием в try-catch лямбд. Но выглядит все довольно перспективно, на мой взгляд. Если эффекты можно будет выразить через исключения, то читать код будет легче.

UPD: Видос от Одерски по теме.

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

Плагины Camunda Modeler

Два самых полезных плагина, на мой взгляд — это отображение границ транзакций и линтер. Ставятся они тупо копированием в папку plugins в корне моделера.

Поскольку плагин — это код на javascript, то его можно легко изменить под свои нужны. Например, в плагине с транзакциями можно залезть в стили и сделать его поприятнее. А для линтера можно настроить правила или написать свои, даже заготовка для этого есть.

С написанием своих правил может возникнуть проблема, что документация к линтеру и библиотеке работы с BPMN оставляет желать лучшего, и у элемента есть ссылка на родителя, но не на детей. Но что-нибудь мелкое реализовать довольно легко, а создать дистрибутив можно через Github action.

Линтер BPMN можно даже встроить как проверку в maven, но это то еще извращение.

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

Устранение шумов микрофона в Linux

Без понятия, почему это не включено по умолчанию, но хотя бы можно включить. В /etc/pulse/default.pa надо добавить

.ifexists module-echo-cancel.so
load-module module-echo-cancel aec_method=webrtc source_name=echocancel sink_name=echocancel1
set-default-source echocancel
set-default-sink echocancel1
.endif

И перезапустить PulseAudio (pulseaudio -k) или перезагрузиться. После этого в настройках звука появятся дополнительные входы/выходы с пометкой echo cancelled.

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

Судьба GIL в Python

Кажется, Python близок к тому, чтобы отказаться от GIL. Статью рекомендую к прочтению.

Одна из основных проблем GIL — сборщик мусора, а именно счетчики ссылок. Чтобы они работали корректно при конкурентном доступе, их можно заменить на атомарные, но это будет серьезным ударом по производительности — до 60%. Большинство программ на Python — однопоточные, и подобные расходы непозволительны.

Сэм Гросс, один из авторов PyTorch, предложил интересное решение этой проблемы на основе соответствующего исследования. У каждого объекта теперь два счетчика ссылок — локальный (для владельца) и глобальный (для остальных тредов). Первый счетчик — быстрый и неатомарный, потому что у владельца к нему эксклюзивный доступ, второй — атомарный, но к нему, скорее всего, будет гораздо меньше обращений, потому что большинство объектов локальные.

Локальный счетчик не совсем прост: два младших бита у него зарезервированы под флаги. Первый нужен для бессмертных объектов: True, False, None, -127..128 и т.п. — считать ссылки на них бесполезно, удалять их не нужно. Второй флаг используется для долгоживущих объектов вроде модулей и функций: они почти всегда попадают в globals или создают циклические ссылки. Проще пройтись сборщиком мусора и найти неиспользуемые.

Дополнительно Гросс оптимизировал кучу мелочей в интерпретаторе, и он стал быстрее на 10%. Похоже, что такими темпами можно будет увидеть Python4 :)

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

Поиск всех пулл-реквестов пользователя в GitHub

Чтобы посмотреть вклад в Open Source, можно зайти на https://github.com/pulls и убрать фильтр открытых:

is:pr author:ov7a

Однако может потребоваться фильтр по владельцу репозитория/организации, чтобы отсеять коммиты, за которые платят зарплату:

is:pr author:udalov -user:JetBrains -user:Kotlin
СсылкаКомментировать

Приостановка процесса

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

kill -TSTP [pid]

или, если он был проигнорирован — STOP:

kill -STOP [pid]

Продолжить процесс потом можно будет через сигнал CONT:

kill -CONT [pid]

Мне эти команды пригодились для временной остановки питонячего скрипта миграции данных, который “долго запрягает, но быстро едет”. Перезапуск потребовал бы ручной перенастройки (чтобы не повторять уже выполненные операции). Скрипт проигнорировал TSTP, а вот после STOP успешно приостановил свою работу.

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