Программисты делятся тем, что узнали

Каждый месяц мы делимся тем, чему научились в нашей команде. В июне Якуб, Михал и Марцин обнаружили:

  • как получить путь к тестовому файлу JavaScript / TypeScript, который вызывает функцию изнутри функции.
  • как выполнять присоединение к CSV-файлам ленивым способом;)
  • как расширение intarray может помочь оптимизировать план выполнения запроса.

Получить путь к файлу, который вызывает функцию изнутри функции - от Якуба

В этом месяце я узнал, как получить путь к тестовому файлу JavaScript / TypeScript, который вызывает функцию изнутри функции. Решение одновременно на удивление простое и на удивление хакерское.

Вы, наверное, знаете, как работает тестирование снимков Jest. Во время выполнения теста Jest генерирует файлы моментальных снимков с представлением некоторых компонентов, строк или структур данных. Этот каталог с файлами моментальных снимков создается в том же месте, что и тестовый файл.

Подход, использованный Jest, очевиден. Он просто берет путь к файлу, предоставленный средством запуска тестов. Однако я работаю над инструментом тестирования, который должен быть независимым от участников тестирования, и у меня нет доступа к пути к файлу теста. Я должен выяснить это… из трассировки стека.

Объект Error в JavaScript имеет свойство stack, которое содержит трассировку стека. Механизм JavaScript V8 позволяет настраивать его. Трассировка стека форматируется вызовом Error.prepareStackTrace(error, structuredStackTrace). В листинге выше я просто изменяю эту функцию на время, чтобы она возвращала массив из CallSite объектов. По этой причине я могу получить этот массив, вызвав new Error().stack.

Поскольку объект CallSite содержит функцию getFileName, остальная часть решения довольно проста. Я могу найти файл, имя которого заканчивается на .spec.js или .test.js (или их эквиваленты для TypeScript).

Вы можете узнать больше об этом в этой ветке на Stack Overflow, кроме того, есть как минимум две популярные (7 и 19 миллионов загрузок в неделю) библиотеки, которые используют этот подход: звонящий-звонящий и получить -caller-файл .

Ленивый разработчик присоединяется к CSV - автор Michał

В этом месяце мне пришлось выполнить некоторые преобразования в файлах csv. Ничего сложного. Фактически простое соединение двух файлов с дополнительной фильтрацией. Я подумал, как это сделать проще всего?

Может быть, написать приложение Scala? Что ж, мне нужно будет проверить, как выполняется экранирование файла, и решить, как анализировать файл вручную или с помощью какой-либо дополнительной библиотеки. Затем мне нужно будет выполнить эти соединения вручную и сохранить обратно в файл. Хм, слишком сложно.

Так что, возможно ... Я могу просто загрузить csv в базу данных (например, Postgres можно создать с помощью однострочной команды Docker), выполнить там соединения и фильтрацию и экспортировать обратно в csv ? Звучит неплохо. Стандартный способ загрузить csv в базу данных - определить схему таблицы и выполнить команду COPY. Однако… если база данных находится в Docker, мне нужно было бы получить этот файл там, сделать что-нибудь docker run для операции COPY и т. Д. Нет, должен быть более простой способ.

Потом случилось чудо…. оказалось, что достаточно использовать… IntelliJ. Благодаря инструментам базы данных на самом деле можно подключиться к экземпляру базы данных, импортировать CSV-файл через мастер, где IDE автоматически предлагает схему таблицы и делает все за вас. Всего несколько кликов. После выполнения объединений и фильтров можно загрузить результат в формате csv. Несколько минут и работа сделана.

Чтобы получить более подробную информацию об этих функциях, вы можете прочитать документацию JetBrains здесь.

PostgreSQL: расширение intarray в действии - Марчин

Недавно SwissBorg попросил нас помочь им улучшить работу их журналов сохраняемости Akka. Они очень быстро развиваются, и универсальный плагин Akka-persistence-JDBC стал слишком универсальным.

Они хотели бы иметь плагин, который более ориентирован на Postgres и выжимает все соки из своих функций (разделы, индексы BRIN, массивы), поэтому мы засучили рукава и начали симфония исследований и кодирования.

Одна из проблем заключалась в том, чтобы найти альтернативу для хранения тегов в текстовом столбце. Поскольку мы хотели сохранить как можно меньший общий размер индексов и использовать кеширование вместо соединений запросов, мы решили назначить уникальный целочисленный идентификатор для каждого уникального тега и сохранить эти идентификаторы в столбце int[].

Еще одним решением было разделить журнал по причинам, которые выходят за рамки этой короткой заметки, но вскоре будут рассмотрены в блоге, так что следите за обновлениями!

В итоге мы получили следующую схему:

Где каждый из разделов также разделен диапазоном sequence_number:

После заполнения таблицы случайными данными (объемом 100000000 событий) я начал измерять производительность по следующему запросу:

(Оператор @> означает _содержит все элементы_. Подробнее см. Документация по функциям работы с массивами)

Результат меня не удивил - на удаленном экземпляре Postgres (Aurora) это заняло около 12 секунд. Давайте посмотрим на план выполнения:

Планировщик запросов использует индекс BRIN в столбце упорядочивания (индексы BRIN содержат информацию о диапазонах блоков, а поскольку блоки могут содержать записи, выходящие за пределы диапазона условия (упорядочение ›1000000 И упорядочение‹ = 10000000), требуется дополнительная перепроверка).

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

Попробуем улучшить схему и создать индекс GIN в столбце тега:

И повторно запустите тот же запрос.

На этот раз это заняло примерно 10 секунд (!). Я ожидал какого-то толчка из-за наличия индекса…

Давайте попробуем разгадать загадку и еще раз взглянем на план выполнения запроса:

Стоимость запроса выглядит немного лучше, но все же это не то, что мы ожидали. По какой-то причине Postgres не хочет учитывать вновь созданный индекс GIN при выполнении поиска (хотя, вероятно, это оказывает некоторое влияние на эффективность фильтрации).

СУБД не предоставляет никаких подсказок по запросу, и запрос занимает слишком много времени.

Обречены ли мы на неудачу?

No!

Postgres поставляется с удобным расширением под названием intarray (доступным также в AWS Aurora). Судя по названию, он содержит некоторые дополнительные возможности для int[] типов столбцов, таких как функции, операторы и (что сейчас для нас наиболее интересно) поддержку индексированного поиска!

Давайте включим расширение и заменим индекс на тот, который использует специальный оператор класса:

Давайте попробуем запрос еще раз:

Разницу трудно не заметить! 1 секунда против 10–12 секунд - это намного больше, чем я ожидал.

План запроса сильно изменился:

Похоже, что gin__int_ops в массиве intarray смог заставить планировщик запросов использовать вновь созданные теги GIN-индекс вместе с заказывающим BRIN-индексом.

Могу сказать, что этот модуль расширения - инструмент, который больше всего помог мне в этом месяце!

А что вы узнали в июне? Дайте нам знать! :)

Кстати, мы всегда ищем выдающихся профессионалов, которые присоединятся к нашей команде!

Questions? Ask us anything about remote work, how does the cooperation with us look like, what projects do we have, or about anything else - on the dedicated Slack channel 💡