Как я изучаю рост сообщества с открытым исходным кодом с помощью dbt
Большинство организаций тратят хотя бы часть своего времени на вклад в проект с открытым исходным кодом. Однако 100% из них в той или иной степени зависят от результатов работы сообществ с открытым исходным кодом.
На самом деле, мы все зависим. Работа сообществ встречается повсюду — в мобильном телефоне, который вас будит, машине, которая готовит вам кофе, автомобиле, который везет вас за лучшим кофе, волшебном приложении, которое приносит вам ужин, и телевизоре, который убаюкивает вас обратно ко сну. Это ваш весь день, даже если вы не инженер-программист. Назовите меня странным, но я думаю, что всем должно быть интересно, как растут и функционируют сообщества с открытым исходным кодом.
Почему меня интересует принятие открытого исходного кода
Есть два сообщества, которые вызывают у моих коллег и меня особый интерес: OpenLineage и Marquez. Мы создаем продукт на основе стандартов, конвенций и возможностей, которые там создаются, и как минимум 70% нашего инженерного времени уходит на вклад. Понимание того, как эти сообщества растут и развиваются, и как наше поведение влияет на них, важно для каждого из нас.
Во многих организациях важно измерять результаты текущих инвестиций в проекты с открытым исходным кодом, особенно при работе с заинтересованными сторонами, которые предпочитают количественные доказательства. Часто это выражается в терминах популярности и принятия — например, "сколько людей используют наши вещи" и "сколько людей их любят". График, показывающий сильное принятие проекта, вероятно, облегчит сложные разговоры... а "хоккейная клюшка" может привести к более сильному раунду финансирования.
Но у этих данных есть и тактические применения. Рассмотрим следующие сценарии:
- Частота загрузок определенного образа Docker неожиданно снизилась
- Уязвимость была исправлена в модуле Python, но люди по-прежнему загружают старую версию по какой-то причине
- Инженер, который собирается в отпуск, является самым активным участником в Slack
Это все ситуации, требующие расследования и действий, и их можно обнаружить только с помощью актуальной информации о деятельности сообщества с открытым исходным кодом. Это стало необходимым для таких команд, как наша — возможно, даже столь же необходимым, как точный прогноз продаж. К счастью, эту информацию легко найти.
В большинстве случаев нужные мне цифры находятся на веб-странице, всего в нескольких кликах. Я могу зайти на страницу GitHub проекта и увидеть количество звезд, или посмотреть на пакет на PyPI, когда мне нужно узнать, сколько загрузок он получил. Но трудно понять, как что-то ведет себя, глядя на его текущее состояние. Вместо этого нужно наблюдать за его движением! Вам нужно собирать данные, которые важны для вашего бизнеса, и изучать, как они мен яются со временем.
Вот почему я построил мини-склад для изучения роста сообщества. Несколько раз в день я собираю информацию о сообществах OpenLineage и Marquez из различных источников данных. Мои модели обрабатывают данные так, чтобы было легко проводить анализ и выявлять тенденции.
В этом посте я проведу вас через наш стек и источники. Затем я покажу вам, как я отслеживаю вовлеченность в Slack, GitHub, Docker Hub и PyPI, используя хранилище, построенное с помощью BigQuery, dbt, OpenLineage и Superset.
Стек инструментов
Я программист, который копирует и вставляет код, что означает, что начинать с пустого экрана для меня сложно. Поэтому было важно, чтобы я построил все, используя стандартные инструменты с устоявшимися сообществами и множеством примеров.
Вот инструменты, которые я выбрал:
-
Google BigQuery выступает в качестве основной базы данных, хранящей все исходные данные, промежуточные модели и витрины данных. Это могло бы быть Snowflake или Redshift, но я выбрал BigQuery, потому что один из моих источников данных уже там в виде публичного набора данных.
-
dbt загружает данные из офлайн-источников и выполняет необходимые преобразования данных после их загрузки в BigQuery.
-
OpenLineage собирает и метаданные производительности по мере выполнения моделей, чтобы я мог выявлять проблемы и находить узкие места. Также, чтобы быть предметом экосистемы для этого исследования :)
-
Superset визуализирует и анализирует результаты, создает панели и помогает мне общаться с заинтересованными сторонами.
Источники данных и метрики
Я решил начать с малого, всего с четырех источников данных: Slack, GitHub, Docker Hub и PyPI. Это дает мне хорошее представление как о деятельности, так и о принятии проектов с течением времени.
Для начала я хочу знать, сколько разговоров происходит в различных каналах и сообществах, в которых мы участвуем. Наши сообщества используют Slack, поэтому количество сообщений с течением времени (по пользователю) — это то, что я ищу. Когда происходит неожиданный всплеск активности, я хотел бы иметь возможность изучить изменение в его историческом контексте. Мне нужно иметь возможность просматривать список самых активных пользователей, обновляемый автоматически. Когда новые участники присоединяются, я хочу понять, как их активность влияет на сообщество.
Существует множество метрик, которые можно отслеживать в любом проекте на GitHub — коммитеры, pull-запросы, форки, релизы — но я начал довольно просто. Для каждого из проектов, в которых мы участвуем, я просто хочу знать, как количество звезд на GitHub растет с течением времени, и ускоряется ли рост или замедляется. Это стало ключевым показателем эффективности для сообществ с открытым исходным кодом, к лучшему или к худшему, и отслеживание его не является необязательным.
Наконец, я хочу знать, насколько активно используются Marquez и OpenLineage. Раньше, когда вы хотели использовать немного технологий, вы загружали файл. Люди, такие как я, которые изучают п оведение пользователей, отслеживали количество загрузок, как если бы это были цены на акции. Это больше не так; сегодня наши технологии все чаще распространяются через менеджеры пакетов и репозитории образов. Метрики Docker Hub и PyPI, таким образом, стали хорошими индикаторами потребления. Загрузки образов Docker и запуски python -m pip install
— это современные загрузки, и, как бы шумны ни были эти метрики, они указывают на аналогичный уровень приверженности пользователей.
Вкратце, вот метрики, которые я решил отслеживать (пока, во всяком случае):
- Сообщения в Slack (по пользователю/по сообществу)
- Звезды на GitHub (по проекту)
- Загрузки на Docker Hub (по образу)
- Загрузки на PyPI (по пакету)
Загрузка необработанных данных в BigQuery
Первым шагом было загрузить все мои необработанные данные в BigQuery. Это была самая грязная часть всей операции, без сомнения. Давайте разберем каждый источник данных по очереди.
Slack
Загрузка необработанных данных
Сразу не было ясно, как получить количество сообщений из каждого из сообществ Slack. API Slack может предоставить часть того, что мне нужно, но я выбрал вместо этого использовать Zapier для загрузки сообщений в BigQuery в реальном времени.
Zapier — это система на основе событий, которая может использоваться для автоматизации случайных задач. У нее есть коллекция коннекторов с общими компонентами цепочки инструментов и простой интерфейс для проектирования действий. Я реализовал базовый Zap, который срабатывает на каждом новом сообщении в Slack, сохраняя копию сообщения в таблице BigQuery .
Перед созданием Zap я создал таблицу под названием slack_messages
, чтобы она служила местом назначения. Схема следующая:
CREATE TABLE `openlineage.metrics.slack_messages`
(
message_time TIMESTAMP NOT NULL,
domain STRING NOT NULL,
username STRING,
realname STRING,
email STRING,
channel STRING,
permalink STRING,
text STRING
)
Затем я создал два отдельных Zap, используя триггер "New Public Message Posted Anywhere in Slack", один для сообщества OpenLineage и один для сообщества Marquez. Я выбрал действие "Create Row in Google BigQuery" и сопоставил каждое из полей, требуемых схемой.
После включения двух Zap я смог убедиться, что новые сообщения появляются в таблице slack_messages
.
Настройка источников dbt
Чтобы dbt знал об этой новой таблице, я создал новый файл models/schema.yml
, содержащий следующее:
sources:
- name: metrics
database: openlineage
schema: metrics
tables:
- name: slack_messages
Из этого dbt знает, что нужно захватить схему для этих таблиц из BigQuery во время генерации сайта документации и сохранить ее в catalog.json
для последующего использования. Для получения дополнительной информации о определении источников, взгляните на страницу Источники в документации dbt.
Явное определение внешних источников данных в dbt было важно для меня по двум причинам:
- Это позволяет нам использовать функции jinja
source()
иref()
для ссылки на эти таблицы в наших моделях, вместо жесткого кодирования имен таблиц. - Это гарантирует, что схемы включены в
catalog.json
, когда выполняетсяdbt docs generate
, что критично для сбора и отслеживания происхождения данных. Мне нужна эта информация, чтобы она могла быть передана в OpenLineage во время цикла выполнения.
GitHub
Загрузка необработанных данных
Получение текущего количества звезд GitHub в BigQuery не было особенно сложным, так как существует публичный API, который это предоставляет.
Я создал короткий скрипт на Python внутри нового каталога loaderscripts/
в моем проекте, чтобы извлечь последнее количество звезд из API GitHub и загрузить его в BigQuery. Этот скрипт вызывается в начале каждого запуска конвейера, в настоящее время как часть entrypoint.sh
моего контейнера. Вот важные части:
dataset_ref = client.dataset('metrics')
table_ref = dataset_ref.table('github_stars')
table = client.get_table(table_ref)
now = int(time.time())
for project in projects:
url = 'https://api.github.com/repos/' + project
response = requests.get(url)
watchers = response.json()['watchers']
client.insert_rows(table, [(now,project,watchers)])
Вы можете увидеть скрипт в его полном объеме здесь. Он гарантирует, что последнее количество звезд GitHub всегда доступно перед началом цикла выполнения.
Перед запуском скрипта я создал таблицу под названием github_stars
, чтобы она служила местом назначения. Схема:
CREATE TABLE `openlineage.metrics.github_stars`
(
timestamp TIMESTAMP,
project STRING,
stars INT64
)
Настройка источников dbt
Чтобы dbt знал об этой новой таблице github_stars
, я добавил ее в список таблиц в models/schema.xml
:
sources:
- name: metrics
database: openlineage
schema: metrics
tables:
- name: github_stars
Это эффективный способ отслеживания звезд с этого момента, но он не может быть использован для заполнения любых исторических данных. К счастью, есть несколько способов получить полную историю звезд для проекта. Я использовал этот инструмент для загрузки CSV-файлов истории звезд GitHub для каждого из проектов. Я объединил их в один файл и поместил его в data/github_daily_summary_history.csv
в качестве файла семян.
Схема для этих данных должна быть явно определена. Я сделал это, добавив раздел в файл dbt_project.yml
со следующим содержимым:
seeds:
openlineage_elt:
github_daily_summary_history:
+column_types:
day: date
project: string
stars: integer
Когда выполняется dbt seed
, будет создана таблица с историей звезд. Явное указание типов столбцов таким образом гарантирует, что каждое поле будет разобрано как предполагаемый тип.
DockerHub
Загрузка необработанных данных
Для Docker Hub я использовал аналогичный подход. Существует API, который предоставляет общее количество загрузок, которые каждый образ имел за всю свою историю. Я написал еще один простой скрипт в loaderscripts/
, чтобы опрашивать API и загружать количество в BigQuery. Он очень похож на скрипт GitHub, с отличием только в блоке в конце:
for image in images:
url = 'https://hub.docker.com/v2/repositories/' + image
response = requests.get(url)
pull_count = response.json()['pull_count']
client.insert_rows(table, [(now,image,pull_count)])
Вы можете увидеть скрипт в его полном объеме здесь. Как и скрипт загрузчика GitHub, он выполняется непосредственно перед началом цикла выполнения dbt.
Как и в случае со скриптом загрузчика GitHub, я создал таблицу под названием dockerhub_pulls
. Схема следующая:
CREATE TABLE `openlineage.metrics.dockerhub_pulls`
(
timestamp TIMESTAMP,
image STRING,
pull_count INT64
)
Настройка источников dbt
Снова, чтобы dbt знал об этой новой таблице dockerhub_pulls
, я добавил ее в список таблиц в models/schema.xml
:
sources:
- name: metrics
database: openlineage
schema: metrics
tables:
- name: slack_messages
- name: github_stars
- name: dockerhub_pulls
PyPI
Статистика загрузок PyPI доступна в виде публичного набора данных в BigQuery, поэтому с ними легко работать. Существует таблица file_downloads
, которая содержит одну строку на каждую загрузку, что идеально подходит для моих целей. Однако это много данных для работы, и меня интересует только небольшая их часть.
Я решил, что эта ситуация требует небольшого среза PyPI: таблицы, которая содержит только строки для пакетов, которые я изучаю, и на которую я могу направить жадный инструмент для создания панелей, не взрывая мой счет в Google Cloud.
Чтобы вырезать этот срез, я сначала добавил исходную таблицу из публичного набора данных BigQuery в models/schema.xml
:
sources:
- name: metrics
database: openlineage
schema: metrics
tables:
- name: slack_messages
- name: github_stars
- name: dockerhub_pulls
- name: pypi
database: bigquery-public-data
schema: pypi
tables:
- name: file_downloads
Затем я создал инкрементальную модель в models/pypi_downloads.sql
, которая извлекает целевые строки и столбцы из исходной таблицы. Я использовал инкрементальную модель, чтобы ее можно было периодически перезапускать для обновления моего среза последними строками из исходной таблицы:
{{
config(
materialized='incremental'
)
}}
select timestamp, country_code, url,
file.project as project, file.version as version,
file.type as type
from {{ source('pypi', 'file_downloads') }}
where (
file.project = 'marquez-python'
or file.project = 'marquez-airflow'
)
and timestamp > TIMESTAMP_SECONDS(1549497600)
{% if is_incremental() %}
and timestamp > (select max(timestamp) from {{ this }})
{% endif %}
Создание моделей данных
Итак! Я выяснил, как загрузить все необработанные данные в BigQuery, но я еще не закончил. Инструменты для создания панелей обычно хотят, чтобы данные были структурированы предсказуемым образом, а это значит, что должны быть четкие меры и измерения (почти всегда с одним из измерений, являющимся единицей времени). Я создал несколько моделей dbt, чтобы привести все в нужную форму.
Slack
Для Slack у меня было простое преобразование. Таблица slack_messages
содержит одну строку на каждое отправленное сообщение. Что мне нужно, вместо этого, это одна строка на пользователя, на сообщество, на день; единственная мера, которую я отслеживаю в настоящее время, это количество отправленных сообщений.
Чтобы создать эту таблицу, я построил новую модель в models/slack_daily_summary_by_user.sql
, содержащую:
select
DATE_TRUNC(DATE(message_time), DAY) AS day,
domain,
username,
count(*) AS messages
from {{ source('metrics', 'slack_messages') }}
group by day, domain, username
GitHub
Для GitHub проблема в том, что есть два входных источника: github_stars
, который заполняется скриптом загрузчика, и github_daily_summary_history
, который загружается из CSV-файла. Оба этих источника содержат дату, проект и количество звезд. В обоих случаях возможно наличие нескольких точек данных в день.
Что я хочу, вместо этого, это единственная таблица с одной строкой на день на проект GitHub. Я создал models/github_daily_summary.sql
, содержащую:
with combined_stars as (
select DATE_TRUNC(DATE(timestamp), DAY) AS day, project, stars
from {{ source('metrics', 'github_stars') }}
union all
select * from {{ ref('github_daily_summary_history') }}
)
select day, project, max(stars) AS stars
from combined_stars
group by day, project
Docker Hub
Данные Docker Hub требуют очень небольшого преобразования. Однако для согласованности я решил создать таблицу сводки, содержащую максимальное значение, зарегистрированное для каждого образа в данный день. Чтобы достичь этого, потребовался новый файл models/dockerhub_daily_summary.sql
:
{{
config(
materialized='view'
)
}}
select
DATE_TRUNC(DATE(timestamp), DAY) AS day,
image, max(pull_count) AS total_pulls
from {{ source('metrics', 'dockerhub_pulls') }}
group by day, image
Я решил сделать это , так как исходная таблица уже довольно компактная, и вовлеченное преобразование легковесное. В будущем я хотел бы рассчитать поле new_pulls
, которое содержит разницу между текущим total_pulls
и значением предыдущего дня. Как только я это построю, я, вероятно, изменю эту модель на таблицу.
PyPI
Наконец, данные PyPI требуют простой модели для подсчета количества ежедневных загрузок на пакет, models/pypi_daily_summary.sql
:
select
DATE_TRUNC(DATE(timestamp), DAY) AS day,
project, version, count(*) AS num_downloads
from {{ ref('pypi_downloads') }}
group by day, project, version
Настройка OpenLineage
Чтобы собирать метаданные происхождения по мере выполнения моделей, я использовал скрипт-обертку OpenLineage. Этот скрипт собирает метаданные происхождения из файлов, сгенерированных dbt во время выполнения, отправляя события OpenLineage на сервер метаданных.
Наличие этих метаданных позволяет мне изучать поток данных по мере их изменения с течением времени. Это может показаться излишним для такого маленького, базового конвейера, но у меня есть ощущение, что он не останется таким надолго. Лучше установить хорошие практики на раннем этапе.
Чтобы убедиться, что скрипт и библиотеки клиента OpenLineage были установлены в моей виртуальной среде Python (эй, ребята, всегда используйте виртуальную среду!), я выполнил:
% pip3 install openlineage-dbt
Marquez — это совместимый с OpenLineage сервер метаданных и инструмент анализа происхождения. Я запустил экземпляр, используя его скрипт docker/up.sh
:
% git clone git@github.com:MarquezProject/marquez.git
% cd marquez
% ./docker/up.sh -d
Наконец, я установил переменную окружения OPENLINEAGE_URL
на местоположение моего сервера Marquez:
% export OPENLINEAGE_URL=http://localhost:5000
Запуск конвейера
Интеграция OpenLineage извлекает некоторые важные метаданные из target/catalog.json
, который создается dbt при генерации документации. Поэтому необходимо сделать это перед запуском моделей:
% dbt docs generate
Running with dbt=0.21.0
Found 7 models, 0 tests, 0 snapshots, 0 analyses, 184 macros, 0 operations, 2 seed files, 4 sources, 0 exposures
18:41:20 | Concurrency: 1 threads (target='dev')
18:41:20 |
18:41:20 | Done.
18:41:20 | Building catalog
18:41:31 | Catalog written to /Users/rturk/projects/metrics/target/catalog.json
Затем я запустил каждый из скриптов внутри каталога loaderscripts/
, чтобы заполнить звезды GitHub и загрузки Docker Hub из их соответствующих API:
% for x in loaderscripts/*.py; do python3 $x; done
Затем, чтобы создать таблицу github_daily_summary_history
с содержимым файла в data/
:
% dbt seed
Running with dbt=0.21.0
Found 7 models, 0 tests, 0 snapshots, 0 analyses, 184 macros, 0 operations, 2 seed files, 4 sources, 0 exposures
18:40:45 | Concurrency: 1 threads (target='dev')
18:40:45 |
18:40:45 | 1 of 2 START seed file metrics.github_daily_summary_history.......... [RUN]
18:40:49 | 1 of 2 OK loaded seed file metrics.github_daily_summary_history...... [INSERT 2000 in 4.60s]
18:40:49 | 2 of 2 START seed file metrics.slack_daily_summary_history........... [RUN]
18:40:53 | 2 of 2 OK loaded seed file metrics.slack_daily_summary_history....... [INSERT 316 in 4.07s]
18:40:53 |
18:40:53 | Finished running 2 seeds in 9.48s.
Completed successfully
Done. PASS=2 WARN=0 ERROR=0 SKIP=0 TOTAL=2
Наконец, чтобы запустить модели и передать метаданные происхождения в мой локальный экземпляр Marquez:
% dbt-ol run
Running OpenLineage dbt wrapper version 0.3.1
This wrapper will send OpenLineage events at the end of dbt execution.
Running with dbt=0.21.0
Found 7 models, 0 tests, 0 snapshots, 0 analyses, 184 macros, 0 operations, 2 seed files, 4 sources, 0 exposures
18:44:15 | Concurrency: 1 threads (target='dev')
18:44:15 |
18:44:15 | 1 of 7 START view model metrics.dockerhub_daily_summary.............. [RUN]
18:44:16 | 1 of 7 OK created view model metrics.dockerhub_daily_summary......... [OK in 0.98s]
18:44:16 | 2 of 7 START table model metrics.github_daily_summary................ [RUN]
18:44:19 | 2 of 7 OK created table model metrics.github_daily_summary........... [CREATE TABLE (1.6k rows, 79.1 KB processed) in 3.03s]
18:44:19 | 3 of 7 START incremental model metrics.pypi_downloads................ [RUN]
18:44:44 | 3 of 7 OK created incremental model metrics.pypi_downloads........... [MERGE (1.7k rows, 7.2 GB processed) in 25.10s]
18:44:44 | 4 of 7 START view model metrics.slack_daily_summary.................. [RUN]
18:44:45 | 4 of 7 OK created view model metrics.slack_daily_summary............. [OK in 0.81s]
18:44:45 | 5 of 7 START view model metrics.slack_daily_summary_by_user.......... [RUN]
18:44:46 | 5 of 7 OK created view model metrics.slack_daily_summary_by_user..... [OK in 0.87s]
18:44:46 | 6 of 7 START table model metrics.pypi_daily_summary.................. [RUN]
18:44:49 | 6 of 7 OK created table model metrics.pypi_daily_summary............. [CREATE TABLE (35.9k rows, 4.7 MB processed) in 3.04s]
18:44:49 | 7 of 7 START view model metrics.daily_summary........................ [RUN]
18:44:50 | 7 of 7 OK created view model metrics.daily_summary................... [OK in 0.87s]
18:44:50 |
18:44:50 | Finished running 4 view models, 2 table models, 1 incremental model in 35.55s.
Completed successfully
Done. PASS=7 WARN=0 ERROR=0 SKIP=0 TOTAL=7
Emitting OpenLineage events: 100%|████████████████████████████████████████████████████████████████████| 14/14 [00:01<00:00, 7.89it/s]
Emitted 16 openlineage events
Теперь граф происхождения всего конвейера можно просмотреть в Marquez, который показывает взаимосвязи между наборами данных и предоставляет информацию о истории выполнения.
Визуализация результатов
Это самая простая часть, безусловно. Поскольку у нас есть набор таблиц с четко определенными мерами и измерениями, работа в системе, такой как Apache Superset, проста.
Настройка источника данных и добавление каждой таблицы в рабочее пространство Preset было легким. Сначала я подключил свою базу данных BigQuery, загрузив ключ для своей учетной записи службы.
После того как подключение к базе данных было установлено, я создал наборы данных для каждой из моих таблиц *_daily_summary
, выбрав базу данных/схему/таблицу из выпадающего списка.
С настроенной базой данных и наборами данных я смог использовать интерфейс построения графиков для изучения различных мер и измерений в хранилище. Примерно через пятнадцать минут я создал панель, которая показывает эволюцию сообществ, в которых работают мои коллеги и я.
Этот общий вид интересен — он предполагает ускорение активности во всех каналах летом 2021 года, что имеет смысл. Это время, когда произошел первый выпуск OpenLineage, а также когда были выпущены несколько докладов и подкастов. Дела замедлились по мере приближения праздников, что также соответствует действительности. Если вы не в розничном бизнесе, такое случается.
Действительно, вы можете увидеть знакомую картину каждый год на графике PyPI. Это указывает как минимум на одно: CI/CD системы не ответственны за все эти загрузки пакетов. Тренд имеет слишком много человечности, слишком много пиков и спадов, связанных с календарем.
Из этих данных PyPI можно извлечь еще кое-что, более специфичное для моего проекта. Летом несколько интеграций были перенесены из проекта Marquez в проект OpenLineage. Это означает, что marquez-airflow
стал openlineage-airflow
. Я хотел бы знать, используются ли еще старые пакеты. Когда я создаю график, используя num_downloads
в качестве метрики и package
в качестве измерения, с месячной гранулярностью, он показывает:
Сдвиг начался в августе, и на данный момент пакеты Marquez все еще составляют около половины от общего числа загрузок. Это означает, что у нас есть работа. Вероятно, где-то еще есть старая документация, которую нужно найти и обновить.
Что дальше?
Это очень простой конвейер метрик сообщества. Возможно, этот пост следовало бы назвать "как начать изучать рост сообщества". Тем не менее, он содержит все элементы большого, сложного конвейера. Далее я планирую:
- Использовать Airflow (возможно, с Astro CLI) для оркестрации загрузки данных в
dockerhub_stats
иgithub_stats
, а затем запускать необходимые шаги seed/run в dbt. - Изучить создание базовой сегментации пользователей — например, сколько из этой активности исходит от людей, которых спонсирует мой работодатель?
- Расширить список проектов, включив в него те, в которые мы вносим вклад реже, чтобы изучить возможные пересечения. Возможно, даже включить несколько совершенно не связанных проектов просто для развлечения :)
Чтобы просмотреть все это (включая Dockerfile, который я использую для контейнеризации), загляните в проект OpenLineage metrics на GitHub, где pull-запросы очень приветствуются. Меня легко найти — @rossturk на GitHub, Twitter и dbt Slack — и я всегда заинтересован в беседе о метриках сообщества.
Comments