Программисты делятся тем, что узнали
Каждый месяц мы делимся тем, чему научились в нашей команде. В июне Якуб, Михал и Марцин обнаружили:
- как получить путь к тестовому файлу 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 💡