Типы источников¶
Модель выполнения¶
Любой запрос в итоге проходит через движок федерации, и именно он позволяет соединять любой источник с любым другим. Как источник туда попадает, зависит от того, как Provisa до него достаёт, и по этому признаку каждый источник относится к одной из пяти групп.
| Категория | Есть прямой драйвер | Есть федеративный коннектор | Примеры |
|---|---|---|---|
| Прямая поддержка (direct-capable) | Да | Да | PostgreSQL, MySQL, MariaDB, SingleStore, SQL Server, Oracle, DuckDB |
| Только федерация | Нет | Да | Redshift, Druid, Exasol, Hive, Iceberg, Delta Lake, Hive (на базе S3) |
| Прямое чтение (реплика) | Да | Да | Snowflake, Databricks, ClickHouse — драйвер читает данные и создаёт реплику; запросы выполняются против реплики в активном движке |
| Прямой (без межисточникового коннектора) | Да | Нет | Athena — инструкции для одного источника выполняются целиком в Athena; межисточниковые соединения выгружают полную реплику |
| Материализация → Федерация | Нет | Нет | REST/OpenAPI, удалённый GraphQL, gRPC, Neo4j Cypher, SPARQL, WebSocket, RSS, CSV, SQLite, Parquet, Ingest (приёмник push-данных), GovData, SharePoint, Salesforce, ServiceNow, облачный инвентарь, Splunk, Qdrant, Azure Cosmos DB, Logs, Google Workspace, Microsoft 365 |
Источники с прямой поддержкой отвечают на запрос, затрагивающий только их, через собственный драйвер, быстрее 100 мс и минуя движок федерации. Полный коннектор у них остаётся, поэтому с другими источниками они по-прежнему соединяются через движок.
У источников только с федерацией нет прямого драйвера. Любой запрос доходит до них через слой федерации.
У источников с прямым чтением (реплика) есть DirectDriver, который читает из хранилища нативно (в формате Arrow, где это возможно) и создаёт реплику в хранилище материализации активного движка. Затем запросы выполняются против этой реплики. См. Хранилища как именованные источники.
У источников прямого типа (без межисточникового коннектора) есть DirectDriver, который выполняет инструкцию для одного источника целиком в самом источнике, передавая туда все фильтры, столбцы и лимиты. Коннектора Trino для них нет, поэтому, когда инструкция соединяет такой источник с другим, драйвер читает данные и создаёт реплику, а соединение выполняется против этой реплики в активном движке.
У источников материализации федеративного коннектора тоже нет. Provisa получает их данные при запуске или во время запроса и кеширует их как Parquet в S3 или в PostgreSQL. Оттуда движок федерации достаёт до них в межисточниковых запросах.
Все источники¶
Provisa регистрирует 73 типов источников — те, что предлагает форма Sources. Приведённые ниже таблицы охватывают все 73; индекс — это счётчик.
| # | Группа | Типы источников |
|---|---|---|
| 1–15 | RDBMS | postgresql, mysql, mariadb, singlestore, sqlserver, oracle, duckdb, cockroachdb, yugabytedb, greenplum, tidb, firebird, airport, saphana, hiveserver2 |
| 16–24 | Облачные хранилища данных | snowflake, bigquery, databricks, redshift, athena, fabric, synapse, trino, motherduck |
| 25–29 | Аналитика / OLAP | clickhouse, druid, exasol, elasticsearch, pinot |
| 30–33 | Data lake / открытые табличные форматы | iceberg, delta_lake, hive, hive_s3 |
| 34–38 | NoSQL | mongodb, cosmosdb, qdrant, cassandra, redis |
| 39–40 | Источники на основе кода | node, python |
| 41–43 | Потоковая передача | kafka, websocket, rss |
| 44 | Приёмник push-данных | ingest |
| 45–46 | Граф и семантика | neo4j, sparql |
| 47–51 | На основе файлов | sqlite, csv, parquet, files, logs |
| 52–53 | Наблюдаемость и прочее | google_sheets, prometheus |
| 54–61 | Корпоративный SaaS | sharepoint, salesforce, servicenow, jira, cloudops, splunk, google_workspace, microsoft_365 |
| 62–67 | API-источники | openapi, graphql_remote, grpc_remote, GitHub, GitLab, Stripe |
| 68 | GovData | govdata |
| 69–70 | Проверки качества данных | soda, great_expectations |
| 71 | Профайлер данных | data_profiler |
| 72 | Наборы данных Kaggle | Kaggle (загружается через форму Sources; регистрируется как источник files — см. Наборы данных Kaggle) |
| 73 | Wikipedia | Wikipedia (регистрируется как источник files, который обходит указанные страницы — см. Wikipedia) |
Справочник по каждому типу источника, поддерживаемому Provisa. «Прямой драйвер» означает, что однопоточные запросы выполняются напрямую к источнику (менее 100 мс). «Имя коннектора» — это федеративный коннектор, используемый, когда источник участвует в многопоточных (multi-source) JOIN.
RDBMS¶
| Тип источника | Прямой драйвер | Имя коннектора | Диалект | Мутации |
|---|---|---|---|---|
postgresql |
asyncpg | postgresql | postgres | Да |
mysql |
aiomysql | mysql | mysql | Да |
mariadb |
aiomysql | mariadb | mysql | Да |
singlestore |
— | singlestore | singlestore | Федеративные |
sqlserver |
aioodbc | sqlserver | tsql | Да |
oracle |
oracledb | oracle | oracle | Да |
duckdb |
duckdb | memory | duckdb | Да |
cockroachdb |
asyncpg (протокол pg) | postgresql | postgres | Да |
yugabytedb |
asyncpg (протокол pg) | postgresql | postgres | Да |
greenplum |
asyncpg (протокол pg) | postgresql | postgres | Да |
tidb |
aiomysql (протокол mysql) | mysql | mysql | Да |
firebird |
— | — (расширение DuckDB) | — | Нет |
airport |
— | — (расширение DuckDB) | — | Нет |
saphana |
SQLAlchemy (hana+hdbcli) |
— | — | Нет |
hiveserver2 |
impyla (HiveServer2 поверх Thrift) | — | hive | Нет |
Совместимые по протоколу базы данных переиспользуют JDBC-драйвер, нативный асинхронный драйвер и диалект базового протокола. CockroachDB, YugabyteDB и Greenplum работают по протоколу PostgreSQL, а TiDB — по протоколу MySQL. Им нужны только записи в реестре, без нового кода коннектора.
firebird (Firebird 3/4/5) и airport (сервер Arrow Flight) — зарегистрированные типы источников, до которых расширения сообщества DuckDB достают на месте, когда DuckDB — активный движок. У них нет ни прямого драйвера, ни федеративного коннектора.
saphana¶
SAP HANA читается собственным драйвером и реплицируется в хранилище активного движка, поэтому доступна на любом движке. Форма принимает хост, порт (по умолчанию 39015), имя пользователя, пароль и базу данных; в многотенантной системе база данных называет тенанта. Register Table перечисляет схемы, которые пользователь может читать, без системных схем HANA.
hiveserver2¶
Конечная точка HiveServer2, читаемая напрямую поверх Thrift, — для развёртывания Hive, где эта точка единственная доступная. Она отличается от hive и hive_s3, которые читают файлы и metastore хранилища и никогда не обращаются к HiveServer2. Строки читаются через конечную точку и реплицируются в хранилище активного движка. Форма принимает хост, порт (по умолчанию 10000), имя пользователя, пароль и базу данных; federation_hints.auth_mechanism выбирает аутентификацию (по умолчанию PLAIN, например GSSAPI для Kerberos).
Облачные хранилища данных¶
| Тип источника | Прямой драйвер | Имя коннектора | Диалект | Мутации | Примечания |
|---|---|---|---|---|---|
snowflake |
SnowflakeDriver | snowflake | snowflake | Федеративные | Чтение через snowflake-connector-python; создаёт реплику; account/warehouse/role в federation_hints |
bigquery |
— | bigquery | bigquery | Федеративные | Нет DirectDriver; достигается через движок федерации или через ATTACH движка BigQuery |
databricks |
DatabricksDriver | delta_lake | databricks | Федеративные | Чтение через databricks-sql-connector (Cloud Fetch, Arrow); создаёт реплику; http_path обязателен в federation_hints |
redshift |
— | redshift | redshift | Федеративные | — |
athena |
AthenaDriver | — | athena | Частичные (INSERT всегда; UPDATE/DELETE/MERGE только для Iceberg) | Безсерверные запросы к S3; инструкции для одного источника выполняются целиком в Athena; перекрёстные объединения выгружают реплику; region обязателен в federation_hints |
fabric |
MssqlWarehouseDriver | — | tsql | Федеративные | Microsoft Fabric Warehouse; T-SQL поверх TDS, аутентификация Azure AD; создаёт реплику |
synapse |
MssqlWarehouseDriver | — | tsql | Федеративные | Azure Synapse SQL; T-SQL поверх TDS, аутентификация Azure AD; создаёт реплику |
trino |
SQLAlchemyDriver | — | — | Федеративные | Удалённый координатор Trino/Presto, чтение через диалект trino SQLAlchemy; создаёт реплику на любом движке |
athena¶
Amazon Athena — бессерверный сервис запросов к данным в S3, тарифицируемый за каждый просканированный байт. Инструкция только по таблицам Athena идёт прямо в Athena, с переданными туда фильтрами, списками столбцов и LIMIT, поэтому Athena сканирует и тарифицирует только те байты, которые нужны этой инструкции. Инструкция, соединяющая таблицу Athena с любым другим источником, сначала загружает полную реплику из Athena. При этом читается вся таблица, и Athena её тарифицирует; затем соединение выполняется против реплики. Как часто выполняется загрузка, решают настройки Replicate и защиты от нагрузки источника, и это примечание — единственное предупреждение об этих затратах.
Обнаружение. Register Table вызывает API метаданных Athena: ListDatabases, ListTableMetadata и GetTableMetadata. Эти вызовы не выполняют запросов, поэтому перечисление баз данных и таблиц и чтение столбцов таблицы ничего не стоят в пересчёте на байты. Столбцы таблицы запрашиваются только тогда, когда вы выбираете эту таблицу, а ключи партиций идут после обычных столбцов. array<…>, map<…> и struct<…> становятся text, а неизвестный тип отклоняется с указанием его имени.
Поля источника.
| Поле | Где | Обязательно | По умолчанию | Примечания |
|---|---|---|---|---|
database |
источник | Да | — | База данных Glue, используемая по умолчанию |
username |
источник | Нет | — | ID ключа доступа AWS; может быть ${secret:…} или ${env:…} |
password |
источник | Нет | — | Секретный ключ доступа AWS; может быть ${secret:…} или ${env:…} |
region |
federation_hints |
Да | — | Регион AWS, например us-east-1 |
workgroup |
federation_hints |
Нет | primary |
Рабочая группа Athena |
s3_staging_dir |
federation_hints |
Нет | по умолчанию группы | Куда Athena записывает результаты; обязателен, если рабочая группа не задаёт расположение |
catalog |
federation_hints |
Нет | AwsDataCatalog |
Каталог данных Athena |
role_arn |
federation_hints |
Нет | — | ARN роли IAM, принимаемой для каждого вызова |
Если и username, и password пусты, используется цепочка учётных данных boto3: роль экземпляра, переменные среды (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) или именованный профиль. Одно без другого отклоняется.
Запись. Любая зарегистрированная таблица принимает INSERT. UPDATE, DELETE и MERGE работают только на таблице Iceberg, а формат фиксируется при регистрации (registered_tables.table_format). На таблице Hive запись, запрашивающая UPDATE, DELETE или MERGE, отклоняется с указанием таблицы и операции. Athena не возвращает записанные строки, поэтому предложение RETURNING отклоняется, а GraphQL не предлагает для этих таблиц полей мутаций. Пути записи — INSERT, UPDATE, DELETE и COPY. INSERT … ON CONFLICT, UPDATE … FROM, DELETE … USING и TRUNCATE отклоняются с указанием формы.
- id: analytics-athena
type: athena
database: my_glue_database
username: ${env:ATHENA_ACCESS_KEY_ID}
password: ${env:ATHENA_SECRET_ACCESS_KEY}
federation_hints:
region: us-east-1
workgroup: primary
s3_staging_dir: s3://my-bucket/athena-results/
С ролью, принимаемой для каждого вызова (цепочка учётных данных для базовой сессии):
- id: analytics-athena
type: athena
database: my_glue_database
federation_hints:
region: us-east-1
role_arn: arn:aws:iam::123456789012:role/AthenaQueryRole
s3_staging_dir: s3://my-bucket/athena-results/
MotherDuck¶
motherduck — база данных DuckDB, размещённая в MotherDuck. На движке DuckDB она подключается на месте; на движке Postgres она остаётся коннектором через foreign data wrapper pg_duckdb.
Подключение. database задаёт базу данных MotherDuck или общий ресурс по его адресу. password — токен MotherDuck, который разрешается как любой секрет источника.
- id: reference
type: motherduck
database: analytics # attached as md:analytics
password: ${secret:<name>} # the MotherDuck token, or a reference to it
Движок DuckDB выполняет ATTACH 'md:<database>'; для общего ресурса database — это адрес ресурса (_share/...), поэтому подключение выглядит как md:_share/....
- Обработка токена. Движок входит в систему через
SET GLOBAL motherduck_token = ?, передавая токен как связанное значение. Его нет в тексте ни одной инструкции, поэтому он не попадает ни в лог, ни в трассировку, ни в ошибку. - Нет токена. Источник без
passwordотклоняется с указанием причины до того, как что-либо подключается (a MotherDuck source signs in with its token (the password field); none is set). Без этой проверки расширение начало бы интерактивный вход и ждало бы около минуты. Источник безdatabaseтакже отклоняется. - Одна учётная запись на движок. Один движок входит в одну учётную запись MotherDuck. Второй источник MotherDuck с другим токеном отклоняется с указанием причины: "one DuckDB engine reads one MotherDuck account".
- Запись идёт через движок, как и на движке Postgres.
Гибридное (двойное) выполнение не требует настройки. На движке DuckDB все источники подключены к одному соединению, поэтому инструкция, соединяющая таблицу файлового источника DuckDB с таблицей MotherDuck, выполняется как одна инструкция DuckDB, а MotherDuck решает, где выполняется каждая часть.
Перемещение данных. При двойном выполнении MotherDuck может переносить локальные строки на свою сторону для тех частей, которые он выполняет. Поэтому такая инструкция может отправить данные из локального файла в учётную запись MotherDuck организации. Не соединяйте файл с данными, которые должны оставаться на узле, с источником MotherDuck, если отправка их в эту учётную запись недопустима.
Источник MotherDuck обслуживают только движки DuckDB и Postgres. Ни у одного другого движка нет для него коннектора, и ничто не выгружает туда его таблицы как реплики.
Аналитика / OLAP¶
| Тип источника | Прямой драйвер | Имя коннектора | Диалект | Мутации | Примечания |
|---|---|---|---|---|---|
clickhouse |
ClickHouseDriver | clickhouse | clickhouse | Федеративные | Чтение через clickhouse-connect (HTTP); secure: "true" в federation_hints для TLS |
druid |
— | druid | druid | Нет | — |
exasol |
— | exasol | exasol | Нет | — |
elasticsearch |
HTTP (нативные движки) | elasticsearch (Trino) | — | Нет | В Trino коннектор читает его, а свойства берёт из DSL сопоставления (mapping DSL) этого типа; на любом другом движке Provisa читает индекс по HTTP (индексы и сопоставление для регистрации таблицы, scroll-чтение для создания реплики) и загружает строки |
pinot |
— | pinot | — | Нет | Коннектор Trino pinot; pinot.controller-urls = хост:порт контроллера Pinot |
Data Lake / открытые табличные форматы¶
Эти типы источников работают только через федерацию. У них нет ни прямого драйвера, ни диалекта.
| Тип источника | Имя коннектора | Путешествие во времени (Time Travel) | Примечания |
|---|---|---|---|
iceberg |
iceberg | Да (аргумент as_of) |
— |
delta_lake |
delta_lake | Да (аргумент as_of) |
— |
hive |
hive | Нет | — |
hive_s3 |
hive | Нет | Hive на базе S3 |
NoSQL¶
mongodb, cassandra и redis имеют коннекторы Trino (redis строит свои свойства из DSL сопоставления этого типа).
| Тип источника | Имя коннектора | Мутации |
|---|---|---|
mongodb |
mongodb | Нет |
cassandra |
cassandra (Trino); чтение CQL через cassandra-driver на любом другом движке | Нет |
redis |
redis (Trino); чтение через redis-py без HTTP на любом другом движке | Нет |
Redis на размещённых движках. Redis недоступен, когда движок организации — один из подготовленных движков размещённой платформы. Trino читает таблицы каталога Redis из файлов описаний в собственном каталоге (директории) каталога, а размещённому движку её нельзя выдать во время работы. Сохранение или применение источника Redis там отклоняется с указанием имени (HTTP 422, sources.redis_needs_engine_files); самостоятельно размещённое развёртывание читает Redis, как описано выше.
Qdrant¶
qdrant читается и записывается собственным клиентом Qdrant, qdrant-client. Qdrant — только live: это свойство типа источника, а не настройка. Отвечает он своим векторным индексом и Query API, и ни одна реплика не может ответить так же, поэтому его таблицы никогда не реплицируются. Для источника Qdrant и его таблиц нет выбора Replicate; значение replicate или row_materialize для них отклоняется.
Подключение. host — URL экземпляра с портом (https://<cluster>.cloud.qdrant.io:6333 или собственный http://qdrant:6333); password — его ключ API, ссылка на секрет, разрешаемая при чтении источника; для экземпляра без ключа он пуст. federation_hints.prefer_grpc: true читает через порт gRPC Qdrant вместо REST. У источника одна схема, default.
Таблицы коллекций. Коллекция — это таблица. Register Table предлагает столбцы коллекции: id (идентификатор точки, её ключ, путь @id), поля payload (индексированное поле типизируется по индексу, любое другое — как подсказывают выбранные точки-образцы; вложенные поля разворачиваются вместе с путём) и по одному столбцу json на вектор (путь @vector или @vector.<name> для именованного вектора), предлагаемому невыбранным. Столбец вектора называет зарегистрированную модель эмбеддингов, эмбеддинги которой он хранит; модель выбирается из тех, чья размерность равна размеру вектора (в Register Table и на вкладке «Тип и ключи» редактора); выбор модели регистрирует вектор.
Инструкции к одному источнику Qdrant. Инструкция, читающая только одну коллекцию, выполняется в Qdrant, если API Qdrant выражает её точно:
- условия по payload превращаются в фильтр Qdrant (
=,<>,IN, диапазоны по числам и датам,IS NULL,AND,OR,NOT); семантика NULL в SQL сохраняется, поэтомуlang <> 'en'пропускает точки безlang; - условие только по
id(=илиIN) — это одинretrieve; любое другое чтение — отфильтрованныйscroll; ORDER BY cosine_similarity(<vector>, <query>) DESC LIMIT k— это одинquery_pointsпо вектору с расстоянием Cosine, фильтр применяется до выбора top-k. Текст запроса преобразуется в эмбеддинг моделью вектора. Ответ сопровождается предупреждением, что top-k приблизительный и выбран через индекс Qdrant.
Всё остальное (агрегат, GROUP BY, вычисляемый или маскированный столбец, порядок, отличный от «сначала наиболее похожие») обрабатывает движок, как описано ниже.
Инструкции, которые обрабатывает движок. Когда инструкция соединяет коллекцию с другим источником или вычисляет по её столбцам, часть, относящаяся к коллекции, читается из Qdrant вживую для этой инструкции и передаётся движку как строки. Эта часть — столбцы коллекции, условия инструкции только по коллекции (в том числе её правила строк) и, если коллекция читается одна в своём SELECT, упорядочивание по сходству и LIMIT этого SELECT. Часть, которую Qdrant не может выразить точно, отклоняется с указанием таблицы и причины. Часть без условия, без ключа и без top-k прочитала бы всю коллекцию и тоже отклоняется. Условие по параметру ($1) остаётся движку.
Запись. INSERT, UPDATE и DELETE для коллекции выполняются в Qdrant:
INSERT … VALUESуказывает столбецidи записывает новые точки.id, который в коллекции уже есть, отклоняется с указанием его значения. Проверка сначала читает идентификаторы, поэтому не атомарна с записью.INSERT … ON CONFLICT (id) DO UPDATE SET c = EXCLUDED.c, …— это upsert. Столбцы, которые инструкция не задаёт, сохраняют значения.UPDATEиDELETEдействуют на точки, выбранные ихWHERE, в виде фильтра Qdrant; условие, не имеющее формы фильтра, отклоняется.- Текст, записанный в столбец вектора, преобразуется в эмбеддинг моделью столбца. Записанный в него вектор должен иметь размерность модели.
Таблицы поиска. Векторный поиск — это таблица поиска: её query_template содержит запрос Query API.
{"collection": "docs", "query": "@query_vector", "using": "text",
"filter": {"must": [{"key": "lang", "match": {"value": "@lang"}}]},
"limit": "@k", "score_threshold": 0.5, "model": "text-embedding-3-small"}
query— всегда аргумент;using,filter,limit,score_thresholdиmodelнеобязательны. Любое значение вusing,filter,limitилиscore_threshold, равное ровно"@name", тоже является аргументом.- Каждый аргумент — параметр таблицы (
query_arg), строго приводимый к объявленному типу и помещаемый в запрос как значение JSON, но не в его текст. Аргументqueryимеет типarray(double)и обязателен. - Вызывающий передаёт аргументы как любой параметр:
SELECT id, score, title FROM docs_search WHERE _nf_query_vector = '[0.1, 0.2, …]' AND _nf_lang = 'en'в SQL, аргументы полей в GraphQL. - Необязательный аргумент, который не передан, ничего не фильтрует: его условие исключается из фильтра. Без
limitпоиск возвращает значение Qdrant по умолчанию — 10 точек. - Qdrant применяет фильтр до выбора ближайших точек, поэтому он точный. Любое другое условие инструкции сужает уже возвращённые поиском точки; новых оно не добавляет.
score— собственная метрика коллекции для искомого вектора: для коллекции Cosine — косинусное сходство. Register Table перечисляет векторы коллекции с их размерностью и расстоянием.modelназывает зарегистрированную модель эмбеддингов (vector_models, admin → AI Models). Текст, переданный как запрос, тогда преобразуется этой моделью, а переданный вектор должен иметь размерность этой модели. Безmodelзапрос должен быть вектором.
Поиск читается для каждой инструкции с её аргументами, а его строки хранятся под этими аргументами в течение TTL таблицы. Таблица поиска доступна только для чтения.
Потоковая передача¶
| Тип источника | Механизм | Мутации |
|---|---|---|
kafka |
Федеративный коннектор Kafka; схема через Confluent Schema Registry (Avro, Protobuf, JSON Schema), ручное определение или вывод по образцу | Нет; живой вывод таблицы публикует в Kafka |
websocket |
Внешний источник WebSocket — подключение, подписка, получение событий; результаты материализуются | Нет |
rss |
Лента RSS 2.0 / Atom — опрос, водяной знак по pubDate/updated; результаты материализуются | Нет |
Kafka на размещённых движках. На подготовленных движках размещённой платформы источник Kafka должен указывать реестр схем (schema_registry_url). Источник, столбцы топиков которого объявляет только модель, отклоняется с указанием имени при сохранении или применении (HTTP 422, sources.kafka_needs_schema_registry), так как его описаниям таблиц понадобился бы каталог (директория), которую нельзя выдать работающему движку. Учётные данные SASL попадают на эти движки в памяти (см. Безопасность).
Приёмник push-данных¶
| Тип источника | Механизм | Мутации |
|---|---|---|
ingest |
Внешние сервисы отправляют события через POST JSON; результаты материализуются | Нет |
Граф и семантика¶
| Тип источника | Механизм | Мутации |
|---|---|---|
neo4j |
Cypher через HTTP API, результаты кешируются в PostgreSQL | Нет |
sparql |
SPARQL 1.1 через POST, результаты кешируются в PostgreSQL | Нет |
На основе файлов¶
Три механизма охватывают файлы. Источники из одного файла и обходчик файлов используют поле path вместо host/port. Третий, Logs, задаёт свои файлы glob-шаблоном в mapping и читает только файлы на сервере Provisa.
Файловые базы данных и однофайловые источники. Источник sqlite, duckdb, csv или parquet указывает path на один файл. (Источник duckdb хранит своё расположение в поле database формы.)
| Тип источника | Расположения | Мутации |
|---|---|---|
sqlite |
локальный путь, https://, s3://, gs://, az:// |
Да для локального файла; нет для удалённого |
duckdb |
локальный путь, https://, s3://, gs://, az:// |
Да для локального файла; нет для удалённого |
csv |
локальный путь, https://, s3://, gs://, az:// (движок DuckDB) |
Нет |
parquet |
локальный путь, https://, s3://, gs://, az:// (движок DuckDB) |
Нет |
Путь с любой другой схемой (например, ftp://) отклоняется: "DuckDB reads no file at a ftp:// location".
Учётные данные для удалённого расположения. Укажите их в federation_hints источника. Каждый набор действует только для бакета или контейнера самого пути, поэтому учётные данные одного источника никогда не используются для расположения другого. Источник без учётных данных читается как публичное расположение.
| Расположение | Ключи federation_hints |
|---|---|
s3:// |
access_key_id, secret_access_key; необязательные region, endpoint (S3-совместимое хранилище, например R2 или MinIO) |
gs:// |
gcs_access_id, gcs_secret_key (ключ HMAC) |
az:// |
azure_account_name, azure_account_key |
Удалённые файлы SQLite и DuckDB доступны только для чтения. Движок DuckDB подключает их на месте с READ_ONLY, и таблица на таком файле не поддерживает insert, update или delete. На движке, который не подключает SQLite, удалённый файл SQLite читается через временное соединение DuckDB, подключающее его только для чтения, и его строки выгружаются как обычно.
Загрузка файла. В форме источника sqlite, duckdb, csv или parquet загрузите файл вместо ввода пути. Файл потоком записывается на диск, никогда не удерживаясь в памяти, в папку организации по пути files/<source id>/<file name>, и путь источника указывает на него.
- Замена. Повторная загрузка занимает место прежнего файла, каким бы ни было его имя, и источник подготавливается заново, чтобы следующее чтение увидело новый файл.
- Удаление. Удаление источника удаляет загруженный файл. Больше ничто его не удаляет.
- Кто. Загружать могут только роли с возможностью
source_registration. - Ограничение размера. Настройка
sources.max_upload_bytes(переменная окруженияPROVISA_SOURCES_MAX_UPLOAD_BYTES) ограничивает файл: по умолчанию 1 ГиБ (1073741824 байт), минимум 1, значение читается в начале загрузки. Файл большего размера отклоняется с HTTP 413 с указанием лимита, и ничего из него не сохраняется. - Путь. Пока путь указывает на загруженный файл источника, поле пути в форме доступно только для чтения.
- Имена. Идентификатор источника и имя файла могут содержать только буквы, цифры,
.,_и-. - Хранение. Загруженный файл хранится как любой файл организации: в папке организации, зашифрованным, если зашифрована папка организации, и виден каждому узлу на общем монтировании.
Запись в локальный файл SQLite или DuckDB и блокировки. Запись берёт блокировку файла и никогда её не ждёт. Если файл удерживает другой процесс или узел, запись отклоняется с указанием источника и того, что его файл заблокирован.
- SQLite. Запись устанавливает
PRAGMA busy_timeout = 0, поэтому заблокированный файл сразу даёт ошибку. - DuckDB. DuckDB разделяет файл только между открытиями только для чтения. Поэтому подключение движка и собственное соединение источника держат файл только для чтения, и каждый узел читает его одновременно. Запись приостанавливает держателей в этом процессе, открывает файл на чтение и запись только для этой инструкции и закрывает его. Если при открытии не удаётся взять блокировку, запись отклоняется.
Общие монтирования. В зашифрованной папке организации, монтирование которой разделяют несколько узлов (настройка org_files.shared_mount), запись в файл SQLite или DuckDB в этой папке отклоняется сразу, с указанием источника. Слой шифрования папок (gocryptfs) не передаёт блокировки файлов другим узлам, поэтому ничто не помешало бы двум узлам писать одновременно. Чтение по-прежнему работает.
Папка, разделяемая без шифрования, должна быть смонтирована с блокировками, которые доходят до каждого узла: монтирование NFS с включёнными блокировками, не nolock и не local_lock. Никакая проверка этого не контролирует; это требование к развёртыванию.
Обходчик файлов (источник files). Он указывает path на glob-шаблон, рекурсивно обходит его и регистрирует директорию как федеративный каталог таблиц. Он читает множество форматов через множество транспортов:
| Форматы | Транспорты |
|---|---|
| CSV, TSV, JSON, YAML, Excel (XLS/XLSX), Parquet, Arrow и документы, преобразуемые в таблицы — HTML, Markdown, DOCX, PPTX | Локальная файловая система, HTTP(S), s3://, hdfs://, ftp:///ftps://, sftp://, iceberg://, SharePoint (REST и Microsoft Graph) |
- id: sales_files
type: files
path: s3://bucket/sales/**/*.csv # glob; local and http(s):// also supported
Любой источник files может также объявить crawl в своём mapping с теми же настройками, что перечислены в разделе Wikipedia: тогда источник сначала читает веб-страницы и выгружает каждую HTML-таблицу данных и каждый связанный файл данных в своей директории как таблицу.
На движке DuckDB files читается нативно, через представление-сканер read_csv_auto для каждого <table>.csv в разрешённой директории. На движке без собственного коннектора files строки загружаются через тот же сервер Calcite pgwire, поставляемый с коннектором (pgwire-file), который используют sharepoint и splunk. См. Корпоративные SaaS-коннекторы ниже.
Logs¶
Источник logs превращает файлы журналов на сервере Provisa в одну таблицу. Вы задаёте файлы полным путём или glob-шаблоном и выбираете формат, в котором записаны их записи. Каждая запись становится строкой. Запись, которую формат не может прочитать, сохраняется вместе с исходным текстом и причиной и никогда не отбрасывается.
sources:
- id: mac-install-log
type: logs
mapping:
glob: /var/log/install.log* # the live file and its rotated, compressed copies
format: macos_install
Mapping принимает четыре ключа и по имени отклоняет любой другой: glob и format (оба обязательны), а также pattern и record_start, которые принимает только формат regex.
mapping:
glob: /var/log/myapp/*.log
format: regex
pattern: '(?P<ts>\S+ \S+) (?P<level>[A-Z]+) (?P<message>.*)'
record_start: '\d{4}-\d{2}-\d{2} ' # a stack trace joins the line it follows
В форме Sources источник Logs называется «Log files» и находится в группе File. Кнопка Preview до сохранения показывает файлы, которым соответствует glob, столбцы, которые даёт формат, и первые 20 строк самого нового файла.
Glob. Это должен быть полный путь; ~ раскрывается, а относительный путь отклоняется («name the files by their full path»). Сопоставление идёт функцией glob из Python в нерекурсивном режиме, поэтому ** ведёт себя как * и сопоставляет в пределах одного уровня каталога. Берутся только обычные файлы, в порядке имён. Пустой файл пропускается, пока в нём нет байтов. Glob может ссылаться на переменную окружения, например ${env:LOG_DIR}/*.log. Источник хранит ссылку в том виде, в каком она записана, и раскрывает её при каждом чтении файлов; незаданная переменная отклоняется с указанием её имени.
Собственные столбцы каждой строки. Они идут перед столбцами формата:
| Столбец | Тип | Содержит |
|---|---|---|
_file_id |
text | Идентичность файла: контрольная сумма его первых 4096 байт после распаковки |
_file_name |
text | Путь, по которому прочитана запись |
_line_offset |
bigint | Смещение в байтах первой строки записи в распакованном тексте |
_entry |
bigint | Каким по счёту элементом записи является строка: 0, кроме пакетных записей, например Records в файле CloudTrail |
_raw |
text | Запись в том виде, в каком она записана |
_parse_error |
text | Почему запись или одно из её значений не удалось прочитать; NULL, если удалось |
_landed_at |
timestamp | Когда строка была загружена |
_file_id, _line_offset и _entry образуют ключ строки, поэтому запись, прочитанная дважды, остаётся одной строкой.
Форматы и пресеты. Выпадающий список форматов группирует их по семействам; последний вариант, regex, — ваш собственный шаблон.
| Семейство (подпись в форме) | Названия форматов |
|---|---|
| Common formats | rfc3164, rfc5424, macos_install, clf, combined, jsonl, logfmt, w3c, cef, gelf |
| Amazon Web Services | aws_alb, aws_nlb, aws_elb, aws_cloudfront, aws_s3_access, aws_cloudtrail, aws_vpc_flow, aws_waf, aws_route53_resolver |
| Databases and runtimes | postgresql_stderr, postgresql_csvlog, postgresql_jsonlog, mysql_error, mysql_slow, mongodb, redis, log4j2_default, logback_default, python_logging |
gcp_cloud_logging, android_logcat, chrome_net_export |
|
| Containers and Linux | kubernetes_cri, journald_json, linux_auditd, linux_dmesg |
| Microsoft | ms_iis, ms_windows_firewall, ms_dns_debug, ms_dhcp_audit, ms_exchange_tracking, ms_sqlserver_errorlog, azure_storage_analytics, azure_monitor, windows_evtx |
| Network security monitoring | zeek_tsv, zeek_json, suricata_eve, snort_fast |
| Web servers and proxies | nginx_error, apache_error, haproxy_http, squid_access, envoy_access, caddy_access, traefik_access |
| Your own | regex |
Итого 56 встроенных форматов и regex.
Как формат создаёт столбцы. Формат получает столбцы одним из четырёх способов.
- Фиксированные столбцы. Большинство форматов сами задают столбцы и типы. Например,
rfc3164даётtimestamp,facility,severity,host,process,pidиmessage;facilityиseverityвычисляются из значения<PRI>. Строка BSD syslog не содержит года, поэтому берётся год последней записи в файл, а если месяц строки позже месяца этой записи — предыдущий год. - Поля, названные записью.
jsonlиlogfmtберут столбцы из полей, встреченных в первых строках файлов (настройкаlogs.preview_lines, по умолчанию 1000). Каждый столбец получает самый узкий тип, подходящий для всех увиденных значений: boolean, затем integer, float, timestamp и json, иначе text. Поле, которое несёт более поздняя запись, но для которого нет столбца, попадает в json-столбецextra. - Поля, названные заголовком.
w3cи пресеты на его основе читают строку#Fields:каждого файла;dateиtimeобъединяются в один столбецtimestamp.aws_cloudfront,ms_iis,ms_windows_firewall,ms_exchange_trackingиzeek_tsvтак же читают строки заголовка, начинающиеся с#, аaws_vpc_flowчитает строку заголовка, начинающуюся сversion. Место, где прочитана строка заголовка, сохраняется вместе с файлом, поэтому файл, который дочитывают позже, сохраняет свои поля. regex. Каждая именованная группа, например(?P<level>…), — это столбец типа text.
Экран Register Table перечисляет эти столбцы с их типами и отмечает ключ. При чтении строк каждое значение приводится к типу, который его столбец имеет в зарегистрированной таблице, поэтому применяется тип, изменённый там. Значение, которое столбец вместить не может, становится NULL, а причина записывается в _parse_error.
Запись длиннее logs.max_line_bytes (по умолчанию 65536) сохраняет в _raw свои первые байты и не разбирается; _parse_error указывает её длину. Файл, начинающийся с метки порядка байтов UTF-8 или UTF-16, читается в этой кодировке, любой другой — как UTF-8.
Записи из нескольких строк. Формат с шаблоном начала записи собирает запись из строки, соответствующей ему, и всех следующих за ней строк, которые ему не соответствуют. Трассировка стека остаётся вместе со своим исключением, а запись о медленном запросе остаётся целой. Строки до первого совпадения образуют отдельную запись; формат обычно её отклоняет, но строка сохраняется. Встроенные форматы с началом записи: rfc3164, macos_install, aws_cloudtrail, postgresql_stderr, postgresql_csvlog, mysql_error, mysql_slow, log4j2_default, logback_default, python_logging, chrome_net_export, ms_sqlserver_errorlog и azure_monitor. С regex задайте record_start сами.
Два формата читают свои записи сами, а не построчно. kubernetes_cri склеивает в одну запись частичные строки (P), на которые их разбила среда выполнения, а windows_evtx читает двоичный файл .evtx.
Файл, в который ещё пишут, читается до последней полной строки. Для многострочного формата он читается до начала своей последней записи, которую следующая строка может продолжить. Когда в файл не писали 2 секунды, эта последняя запись считается целой.
Ротация. Файл определяется контрольной суммой его первых 4096 байт после распаковки, а не именем. Когда system.log ротируется в system.log.0, а затем сжимается в system.log.0.gz, все копии — один и тот же файл. Чтение продолжается с байта, до которого дошло прошлое чтение, поэтому ни одна строка не читается дважды.
- Сжатие определяется по первым байтам файла, а не по имени: gzip и bzip2. Сжатый файл считается завершённым.
- Ротированная копия и её сжатый двойник, если соответствуют обе, читаются один раз — из сжатой.
- Файл, оказавшийся короче уже прочитанного смещения, был усечён: это новый файл, он читается с начала. Строки старого остаются.
- Файлы короче 4096 байт, начинающиеся одинаково, различаются по inode, поэтому остаются двумя файлами.
- Файл, которому glob больше не соответствует, забывается; его строки остаются как история.
Обновление и живая доставка. Таблица Logs — это реплика. За файлами никто не следит. Вместо этого, когда сигнал изменений источника — probe или ttl_probe, его проба вычисляет дайджест пути, inode, размера и времени последней записи каждого подходящего файла; если дайджест отличается, запрашивается обновление. Типы проб, которые принимает источник Logs, — hash и none.
Обновление читает только новое. Каждый файл читается с места, до которого дошёл прошлый проход, а строки записываются по ключу, по 5000 за раз, прежде чем сохраняется новая позиция. Проход, прервавшийся между этими двумя шагами, читает те же записи заново, и строки получаются теми же. Каждый проход помечает свои строки в _landed_at — столбце, который живая доставка использует как водяной знак. Изменение определения таблицы — это полная перестройка, которая читает каждый файл с начала.
Разрешённые корни. Настройка оператора logs.allowed_roots перечисляет каталоги, в которые может разрешаться glob. Значение по умолчанию — ["/var/log"]; это список на карточке Security в Admin → Settings, который также задаётся через PROVISA_LOGS_ALLOWED_ROOTS, а изменение требует подтверждения. Каждый корень разрешается после раскрытия ~ и символических ссылок.
Действуют две проверки. Фиксированная часть glob до первого подстановочного знака должна разрешаться внутри корня; это проверяется при сохранении источника и при каждом просмотре или чтении. Каждый подходящий файл также должен разрешаться внутри корня после раскрытия его символических ссылок, поэтому ссылка за пределы /var/log отклоняется. Отказ называет путь, во что он разрешился, и разрешённые корни:
/home/me/app.log resolves to /home/me/app.log, outside the directories log files may be read from: /var/log
При сохранении код — schema.logs_path_refused; при просмотре — 403 с logs.path_refused.
Отказ на хостинге (SaaS). Развёртывание, в котором работает много организаций, не читает никаких собственных файлов журналов. Каждая проверка пути отказывает первой — при сохранении, просмотре и чтении — сообщением «<path>: a server that holds many organizations reads no log file of its own». Эти файлы принадлежат серверу, а не какой-либо одной организации, поэтому ни один арендатор не вправе их читать. Развёртывание определяет, обслуживает ли оно много организаций, по состоянию своей мультитенантности.
Наборы данных Kaggle¶
Kaggle — это площадка для скачивания файлов с данными. Добавленный вами набор данных Kaggle регистрируется как источник files, и запрашивается он так же, как любой другой источник files.
Добавление набора данных. Откройте Sources → Subscriptions → Kaggle. Форма работает в два шага: поиск наборов данных Kaggle требует токена, поэтому выбор появляется только после того, как ваш токен прошёл проверку.
- Токен. Введите токен API Kaggle и нажмите «Validate». Проверка вызывает
POST https://www.kaggle.com/api/v1/datasets/create/newс пустым телом.401означает, что токен недействителен, любой другой ответ — что действителен. Kaggle проверяет пустое тело до того, как что-либо создать, поэтому ничего не сохраняется. - Выбор. Поле поиска по мере ввода запрашивает
GET /api/v1/datasets/list, и каждый результат показывает название и описание набора данных. Выберите один и нажмите «Add Dataset».
«Add Dataset» скачивает пакет из GET /api/v1/datasets/download/{owner}/{ref}, распаковывает его файлы CSV и Parquet в <PROVISA_DATA_DIR>/kaggle/<owner>/<ref>/<file-stem>/<file-name> и создаёт один источник files, чей path — этот корневой каталог. Сервер никогда не хранит токен.
Затем зарегистрируйте нужные таблицы на обычном экране Register Table. Каждый файл показан как отдельная таблица, как в любом другом источнике files.
Только CSV и Parquet. Пакет с файлом .sqlite или .db отклоняется с сообщением об этом. Подготавливаются только файлы CSV и Parquet.
Обновление. Когда Kaggle публикует новую версию набора данных, вызовите GraphQL-мутацию refreshKaggleSource с ID источника и действительным токеном. Она заново подготавливает файлы на месте и очищает кэш файлового endpoint, чтобы следующий запрос увидел любое изменение схемы. Какой набор данных скачивать, говорят kaggle_owner и kaggle_ref, сохранённые в federation_hints при создании источника.
Своего YAML нет. Источник Kaggle создаётся только через форму источников. При экспорте в YAML он выглядит как type: files с kaggle_owner и kaggle_ref в federation_hints. Чтобы снова скачать данные из Kaggle, нужно обновление в интерфейсе или мутация refreshKaggleSource. В изолированной среде укажите в YAML path каталог, который вы подготовили сами.
Wikipedia¶
Wikipedia обходит названные вами страницы. Каждая таблица данных на странице и каждый связанный файл CSV, TSV, Excel, JSON или Parquet становится таблицей источника. Как и Kaggle, он регистрируется как источник files с crawl в mapping. Добавление источника ничего не регистрирует: стюард регистрирует нужные таблицы через обычный экран Register Table.
Поля настройки.
| Поле | По умолчанию | Значение |
|---|---|---|
| Pages to start from | нет (обязательно) | По одной на строку: название страницы или адрес страницы в выбранной редакции |
| Language edition | en |
Код редакции, например en, de или ja |
| Link depth | 1 |
На сколько ссылок от начальной страницы читать; 0 читает только начальные страницы |
| Page limit | 25 |
Наибольшее число страниц, читаемых за один обход |
| Contact | пусто | Отправляется с каждым запросом, чтобы Wikipedia могла с вами связаться, как требует её политика |
| Follow "See also" links | выкл. | Выключено — раздел исключается из статьи |
| What this edition calls its "See also" section | пусто | Запрашивается, только когда флажок выше выключен и Provisa не может назвать раздел для выбранной редакции. Название раздела так, как его показывает страница, например Voir aussi |
| Directory for the crawl's files | пусто | Куда записываются таблицы и файлы данных. Пусто: <PROVISA_DATA_DIR>/crawl/<source id> |
Provisa знает заголовок «See also» этих редакций: en, de, es, it, pt, nl, pl, sv, ja, zh, ru. Для любой другой редакции при выключенном «See also» форма спрашивает, как редакция называет этот раздел; введите его название так, как его показывает страница (например, Voir aussi). Источник, сохранённый без него, отклоняется (wikipedia.see_also_heading_needed).
Какие ссылки отслеживаются. Ссылка выбирается по тому, чем её называет собственная разметка страницы, а не по имени в её адресе: такие имена различаются в каждой языковой редакции и встречаются также в обычных заголовках.
- Отслеживаются: ссылки на другие статьи, расположенные в тексте статьи, в списках и таблицах данных. Метка разметки —
a[rel='mw:WikiLink']. - Не отслеживаются: ещё не написанные страницы, сама страница, поиск по книжным источникам, страницы файлов, другие вики и внешние сайты.
- Не читаются вовсе (удаляются до сбора ссылок и таблиц): навигационные блоки, информационные блоки, примечания и цитаты, категории, оглавление, изображения и служебные сообщения.
- Не читаются, пока вы не включите параметр: раздел «See also».
Таблицами становятся только таблицы, которые редакторы помечают как таблицы данных (table.wikitable), а таблица менее чем из 2 строк не предлагается.
Расширенные настройки обхода. Форма держит их свёрнутыми под «Crawl settings» и показывает каждую с её значением по умолчанию; оставьте поле пустым, чтобы сохранить его. В mapping источника files те же имена идут под crawl:, а имя вне этого списка отклоняется.
| Настройка | Значение по умолчанию для Wikipedia | Значение |
|---|---|---|
start_urls |
названные вами страницы | Страницы, с которых начинается обход |
max_depth |
1 |
Глубина ссылок |
max_pages |
25 |
Лимит страниц |
request_delay |
1 seconds |
Задержка между запросами |
user_agent |
Provisa/<version> (+https://provisa.dev) file-crawler, с добавленным вашим контактом |
Имя, под которым обход представляется |
content_selector |
#mw-content-text .mw-parser-output |
CSS-селектор: ссылки и таблицы читаются только внутри него |
remove_selectors |
список частей, описанный выше | CSS-селекторы для исключаемых частей. Указание заменяет весь список, включая правило «See also» |
link_selector |
a[rel='mw:WikiLink'] с исключениями |
CSS-селектор, которому должна соответствовать ссылка, чтобы её отслеживать |
link_exclude_patterns |
пусто | Регулярные выражения; ссылка, в адресе которой одно из них найдено, не отслеживается |
follow_external_links |
false |
Переходить по ссылкам на другие сайты |
allowed_domains |
не задано | Домены, которые обход может посещать |
table_selector |
table.wikitable |
CSS-селектор, которому должна соответствовать таблица |
generate_tables_from_html |
не задано | Становятся ли HTML-таблицы таблицами |
html_table_min_rows |
2 |
Наименьшее число строк в таблице |
html_table_max_rows |
не задано | Наибольшее число строк в таблице |
allowed_file_extensions |
csv, tsv, xlsx, xls, json, parquet |
Типы связанных файлов, которые становятся таблицами |
html_cache_ttl |
1 days |
Как долго хранится обход |
max_html_size |
не задано | Наибольшая читаемая страница, например 10MB |
max_data_file_size |
не задано | Наибольший читаемый файл данных, например 100MB |
Имена таблиц. Таблица, найденная обходом, называется <page>__<name>. Имя — это подпись (caption) таблицы, а если её нет, то последний заголовок над ней; таблица без того и другого получает имя table_1, table_2 и так далее. Обе части приводятся к нижнему регистру, а каждая последовательность прочих символов заменяется на _. Например, таблица под заголовком «Tallest buildings» на странице List_of_tallest_buildings становится list_of_tallest_buildings__tallest_buildings. Связанный файл данных сохраняет своё имя.
Обновление. Обход хранится в течение периода кеша, по умолчанию 1 days (html_cache_ttl). Страницы загружаются заново только после истечения этого времени.
Наблюдаемость и прочее¶
prometheus имеет коннектор Trino (свойства строятся из DSL сопоставления этого типа). google_sheets — зарегистрированный тип источника без коннектора Trino, материализующийся через конвейер API-кеша.
| Тип источника | Имя коннектора | Мутации |
|---|---|---|
google_sheets |
— (материализуется) | Нет |
prometheus |
prometheus | Нет |
Корпоративные SaaS-коннекторы¶
Google Workspace и Microsoft 365 читаются собственными загрузчиками; см. Google Workspace и Microsoft 365. SharePoint, Salesforce, ServiceNow, Jira, облачная инвентаризация и Splunk читаются через драйверы, которые работают отдельным процессом и говорят по протоколу PostgreSQL (pgwire-sharepoint, pgwire-salesforce, pgwire-servicenow, pgwire-jira, pgwire-cloudops, pgwire-splunk), поэтому прямого драйвера нет ни у одного. На движке DuckDB endpoint драйвера подключается вживую через расширение postgres: Register Table перечисляет таблицы коннектора из подключённого каталога, запросы читают коннектор на месте, а фильтры и проекции передаются ему. Любой другой движок загружает строки в хранилище материализации для федерации. Пакеты драйверов скачиваются под ОС и архитектуру из закреплённого релиза (pgwire-<connector>-<version>-<os>-<arch>.tar.gz; macOS arm64, Linux x86_64, Windows x86_64). Вместо этого оператор может для каждого коннектора указать папку пакета, собранного для этого хоста, в Admin → Настройки → Серверы коннекторов (connectors.<connector>.bundle_dir); см. Admin. Все пять коннекторов Trino (у Jira его нет) всегда сравнивают имена без учёта регистра, как и сами эти продукты.
sharepoint¶
Списки SharePoint перечисляются как схемы и представляются в виде запрашиваемых таблиц. Два метода аутентификации: CLIENT_CREDENTIALS (по умолчанию) и на основе сертификата через PFX-сертификат. Секретные значения в mapping разрешаются через движок секретов до достижения коннектора.
| Поле источника | Свойство коннектора | Примечания |
|---|---|---|
base_url или host |
site-url |
URL сайта SharePoint |
username |
client-id |
ID клиента приложения Azure |
password |
client-secret |
Секрет клиента приложения Azure |
database |
tenant-id |
UUID клиента (тенанта) Azure |
mapping.auth_type |
auth-type |
CLIENT_CREDENTIALS (по умолчанию) или CERTIFICATE |
mapping.certificate_path |
certificate-path |
Путь к PFX при auth_type: CERTIFICATE — должен быть АБСОЛЮТНЫМ |
mapping.certificate_password |
certificate-password |
Пароль PFX — ключ должен присутствовать, пустая строка для PFX без пароля |
Аутентификация по сертификату на движках, отличных от Trino, несёт два дополнительных правила, оба применяются при построении операнда model.json сервера Calcite pgwire. certificate_path должен быть абсолютным: сервер работает с директорией своего пакета как рабочей директорией, поэтому относительный путь разрешается внутри кеша runtime-зависимостей, и PFX не находится. certificate_password должен присутствовать в mapping, даже если у PFX нет пароля. В этом случае это пустая строка; адаптер Calcite полностью отклоняет пустой (null) пароль, а отсутствующий ключ трактуется как ошибка конфигурации, а не молча читается как пустой пароль. Отсутствующее или относительное значение вызывает MissingConnectorConfig с именем поля.
Когда коннектор не предоставляет information_schema.columns, зарегистрируйте таблицу с явными определениями столбцов (полученными из Microsoft Graph API) через мутацию registerTable.
- id: hr-sharepoint
type: sharepoint
base_url: https://kenstott.sharepoint.com
username: ${env:SP_CLIENT_ID}
password: ${env:SP_CLIENT_SECRET}
database: ${env:SP_TENANT_ID}
mapping:
auth_type: CLIENT_CREDENTIALS
Аутентификация по сертификату, с абсолютным путём и всегда присутствующим паролем:
- id: hr-sharepoint
type: sharepoint
base_url: https://kenstott.sharepoint.com
username: ${env:SP_CLIENT_ID}
database: ${env:SP_TENANT_ID}
mapping:
auth_type: CERTIFICATE
certificate_path: /etc/provisa/certs/sharepoint.pfx
certificate_password: ${env:SP_CERT_PASSWORD}
salesforce¶
Каждый sObject, который доступен учётным данным для запросов, — это таблица, которую стюард может зарегистрировать (Account, Opportunity, пользовательские объекты *__c); автоматически ничего не регистрируется. Столбцы и типы берутся из describe объекта. Фильтры, проекции, сортировки и лимиты передаются в Salesforce в виде SOQL.
Источник Salesforce указывает URL входа «Мой домен» организации и один полный набор учётных данных, выбираемый через mapping.auth_type. URL входа — полный URL https://… (например, https://acme.my.salesforce.com); голый хост или любая другая схема отклоняется с указанием поля. Неполный набор отклоняется при использовании источника с указанием недостающего значения. Каждый секрет может быть ссылкой ${secret:…} или ${env:…}. Секрет, введённый как буквальное значение, сохраняется в хранилище организации, а источник хранит ссылку на него.
| Поле источника | Свойство коннектора | Примечания |
|---|---|---|
base_url или host |
login-url |
URL «Мой домен» организации (Настройка, затем Мой домен). login.salesforce.com не работает с потоком client credentials |
username |
client-id |
Ключ клиента подключённого приложения |
password |
client-secret |
Секрет клиента подключённого приложения |
mapping.auth_type |
— | CLIENT_CREDENTIALS (по умолчанию), USERNAME_PASSWORD или ACCESS_TOKEN |
mapping.sf_username, mapping.sf_password |
username, password |
Только USERNAME_PASSWORD, вместе с ключом и секретом клиента |
mapping.security_token |
security-token |
Только USERNAME_PASSWORD; необязательно |
mapping.access_token, mapping.instance_url |
access-token, instance-url |
Только ACCESS_TOKEN; без ключа и секрета клиента |
mapping.api_version |
api-version |
Необязательно, например v61.0 |
Для CLIENT_CREDENTIALS подключённому приложению нужны Enable Client Credentials Flow и пользователь Run As; каждое чтение и каждая запись выполняются с правами этого пользователя. Организации, созданные начиная с Summer '23, по умолчанию блокируют поток «имя пользователя и пароль».
Чтение. В Trino источник читается через каталог salesforce, который только читает. В любом другом движке движок подключает pgwire-сервер источника и читает его на месте.
Запись. INSERT, UPDATE и DELETE над зарегистрированным sObject — это мутации: они управляются, попадают в аудит и обрабатываются по правилам среды, как любая другая мутация. В каждом движке, включая Trino, запись выполняется на pgwire-сервере источника; в Trino этот сервер работает только для записи. Каждая инструкция отправляется в Salesforce и фиксируется при выполнении, поэтому её нельзя откатить. Сервер не возвращает записанные им строки: инструкция с предложением RETURNING отклоняется, а GraphQL не предлагает поля мутаций для этих таблиц; SQL INSERT, UPDATE, DELETE и COPY — пути записи. Запись — это одна простая инструкция над одной таблицей: INSERT … ON CONFLICT, UPDATE … FROM, DELETE … USING, MERGE и TRUNCATE отклоняются с указанием формы. Источник SharePoint записывается так же.
Запуск. Прежде чем принимать подключения, сервер источника читает столбцы каждого sObject — около четырёх минут для организации с 1200 объектами. Это оплачивается один раз: результаты describe хранятся в собственном каталоге состояния источника внутри каталога данных экземпляра. Сервер запускается при регистрации или загрузке источника. В Trino каталог salesforce хранит собственные результаты describe, по одному каталогу на источник в /data/trino/cache/salesforce-describe внутри координатора, поэтому и там цена оплачивается один раз, если этот путь — том, переживающий контейнер: том trino_cache стека compose или том кеша координатора в Helm-чарте (trino.coordinator.cache.persistence). Пока сервер не принимает подключения, Register Table показывает, что коннектор запускается, а запись получает то же сообщение, а не ждёт.
- id: sales-salesforce
type: salesforce
base_url: https://acme.my.salesforce.com
username: ${env:SF_CONSUMER_KEY}
password: ${env:SF_CONSUMER_SECRET}
mapping:
api_version: v61.0
Поток «имя пользователя и пароль»:
- id: sales-salesforce
type: salesforce
base_url: https://acme.my.salesforce.com
username: ${env:SF_CONSUMER_KEY}
password: ${env:SF_CONSUMER_SECRET}
mapping:
auth_type: USERNAME_PASSWORD
sf_username: ops@acme.com
sf_password: ${env:SF_PASSWORD}
security_token: ${env:SF_SECURITY_TOKEN}
servicenow¶
Любая таблица экземпляра доступна для регистрации, и ничего не регистрируется автоматически. На нагруженном экземпляре тысячи таблиц, поэтому Register Table группирует их по темам. Каждая тема — это отображаемое имя пакета ServiceNow или «(no package)» для таблицы, не входящей ни в один пакет. Каждая таблица указана как name — label. Выбор таблицы читает её столбцы с экземпляра; перечисление таблиц этого не делает.
Источник ServiceNow указывает полный URL https://… инстанса, а также имя пользователя и пароль. URL берётся из base_url или host; голый хост или любая другая схема отклоняется с указанием поля (instanceUrl). Отсутствующее имя пользователя или пароль отклоняется с указанием поля. Каждое значение может быть ссылкой ${secret:…} или ${env:…}. Секрет, введённый как буквальное значение, сохраняется в хранилище организации, а источник хранит ссылку на него.
| Поле источника | Свойство адаптера | Примечание |
|---|---|---|
base_url или host |
instanceUrl |
Полный URL https://… инстанса (например, https://acme.service-now.com). Голый хост или другая схема отклоняется |
username |
username |
Имя пользователя для базовой аутентификации |
password |
password |
Пароль для базовой аутентификации |
Чтение. В Trino источник читается через каталог servicenow, который только читает. В любом другом движке движок подключает pgwire-сервер источника и читает его на месте. От инстанса запрашиваются только выбранные столбцы (sysparm_fields). Фильтр передаётся в инстанс (sysparm_query) только в форме, которую адаптер проверил на реальном инстансе; все прочие фильтры, а также каждая сортировка и лимит применяются к уже прочитанным строкам.
Ссылочное поле представлено двумя столбцами: <field> содержит sys_id referenced записи, а <field>__display — её отображаемое значение. Пустое поле — NULL. Поле, тип которого адаптер не может отобразить, вызывает ошибку с указанием таблицы, поля и типа.
Поиск таблиц (естественный язык) сопоставляется по имени, метке и теме каждой таблицы. После регистрации таблицы её столбцы присоединяются к поиску.
Жизненный цикл сервера. Сервер обслуживает только зарегистрированные из него таблицы. Регистрация или удаление таблицы перезапускает сервер и повторно провизионирует источник на движке; запрос, поступивший во время перезапуска, завершается ошибкой.
Запись. INSERT, UPDATE и DELETE на зарегистрированной таблице — это мутации: управляемые, проходящие аудит и обрабатываемые по правилам среды, как любая другая мутация. В каждом движке, включая Trino, запись выполняется на pgwire-сервере источника. Сервер возвращает строку, которую инстанс содержит после записи; клауза RETURNING поэтому принимается, и GraphQL предлагает поля мутаций для этих таблиц. Запись — это одна инструкция над одной таблицей: INSERT … ON CONFLICT, UPDATE … FROM, DELETE … USING, MERGE и TRUNCATE отклоняются с указанием формы.
Import-сеты. Если у таблицы есть активная карта преобразования (transform map) в неё, Register Table предлагает её import-сеты, и стюард может привязать таблицу к одному из них (таблица с несколькими картами называет каждую). Вставки в привязанную таблицу проходят через Import Set API инстанса и преобразование той карты; вставки в непривязанную таблицу, а также каждое обновление и удаление записываются в таблицу напрямую. Строка, отклонённая преобразованием, отклоняется с указанием этой строки; уже записанные строки остаются записанными. Привязка проверяется при сохранении таблицы: она должна быть import-сетом активной карты преобразования в эту таблицу, в источнике ServiceNow.
- id: helpdesk-sn
type: servicenow
base_url: https://acme.service-now.com
username: ${env:SN_USERNAME}
password: ${env:SN_PASSWORD}
jira¶
Сайт Jira Cloud читается и записывается через собственный адаптер Calcite. Таблица источника Jira — это форма: один проект и один тип задачи, а также поля, которые читают её столбцы. Register Table перечисляет каждую форму, по которой аккаунт может создать задачу, сгруппированные по проектам; при выборе формы её поля считываются из Jira, а простое перечисление форм поля не загружает. У каждой формы есть столбцы key (её первичный ключ), id, summary, status, created и updated, а затем по одному столбцу на каждое выбранное поле, названному так, как его называет адаптер: имя поля в snake case (Story Points становится story_points). Поле, имя которого уже совпадает с именем столбца, называется по идентификатору поля (customfield_10099), так что можно выбрать любое поле. Поле пользователя содержит идентификатор аккаунта; его отображаемое имя — отдельное поле (assignee.displayName, столбец assignee_name). Столбцы, которые Jira записывает сама, доступны только для чтения: key, id, created, updated, отображаемое имя поля пользователя и поле формы, неизвестной адаптеру, которое он отдаёт как текст JSON. Регистрация помечает такой столбец текстом «Read only: Jira sets it» в его описании, если описания нет, а запись, которая задаёт этот столбец, отклоняется сервером с указанием столбца.
Таблица формы указывает свой проект и тип задачи в своих arguments (project, issue_type), а каждый столбец читает поле, которое называет его path (идентификатор поля или имя поля), либо поле с собственным именем. Таблица, зарегистрированная из админки, получает оба значения из Jira; в конфигурации они задаются явно. Таблица Jira, фиксирующая что-либо иное или не имеющая одного из них, отклоняется с указанием её имени.
| Поле источника | Свойство адаптера | Примечание |
|---|---|---|
base_url или host |
baseUrl |
Полный адрес https://… сайта (например, https://acme.atlassian.net). Голый хост или другая схема отклоняется |
username |
email |
Адрес электронной почты аккаунта Atlassian |
password |
apiToken |
Токен API этого аккаунта, созданный на id.atlassian.com в разделе Security, API tokens |
Связи. Таблица issue_links источника содержит связи между задачами его форм: link_id, link_type, outward_key и inward_key, каждая связь один раз. Отношение между двумя формами объявляется через неё как junction, а link_type служит дискриминатором, так что GraphQL и Cypher переходят от задачи к связанной задаче по типу связи.
Чтение. Поиск по ключу (key =, key IN) выполняется выборкой по ключу, так что ключ, не называющий ни одной задачи, не даёт строки. Сравнение даты или числа отправляется в Jira как JQL. Все прочие фильтры и каждая сортировка применяются к уже прочитанным строкам: равенство по тексту, опции, статусу или пользователю не отправляется, поскольку JQL отклоняет значение, не называющее ничего, там, где SQL возвращает ноль строк. Многозначное поле (метки, множественный выбор, компоненты, версии) — это текст с массивом JSON, и записывается оно так же.
В DuckDB, PostgreSQL и ClickHouse движок подключает pgwire-сервер источника и читает его на месте. В Trino у источника нет собственного каталога, и он загружается через свой сервер.
Жизненный цикл сервера. Сервер обслуживает только зарегистрированные из него формы, а также свой список форм, свой список полей и связи. Регистрация, изменение или удаление формы перезапускает сервер и повторно провизионирует источник на движке.
Запись. INSERT, UPDATE и DELETE над формой — это мутации, записываемые на pgwire-сервере источника в каждом движке. Строки вставляются пакетами по 50; обновление или удаление выполняется задача за задачей, по ключу. Установка status выполняет переход workflow в этот статус и отклоняется с указанием достижимых статусов, если у workflow задачи их нет. Задача с подзадачами не удаляется. Сервер возвращает каждую записанную строку по её ключу, поэтому RETURNING принимается, и GraphQL предлагает поля мутаций. В issue_links INSERT создаёт связь, а DELETE удаляет её; UPDATE отклоняется.
- id: support-jira
type: jira
base_url: https://acme.atlassian.net
username: ${env:JIRA_EMAIL}
password: ${env:JIRA_API_TOKEN}
tables:
- source_id: support-jira
table: incident
arguments: {project: KAN, issue_type: Incident}
columns:
- {name: key, data_type: varchar, is_primary_key: true}
- {name: summary, data_type: varchar}
- {name: status, data_type: varchar}
- {name: priority, data_type: varchar}
- {name: flagged, data_type: varchar, path: customfield_10021}
cloudops¶
Облачный инвентарь представляет ресурсы Azure, AWS и GCP как один набор таблиц: compute_resources, storage_resources, kubernetes_clusters, database_resources, network_resources, iam_resources, container_registries и compute_security_groups, связывающую вычислительный ресурс с его группами безопасности. У каждой таблицы один набор столбцов для всех трёх облаков, а её столбец cloud_provider называет облако, из которого пришла строка. Источник только читает, а его таблицы регистрирует стюард, как и любые другие.
Источник называет одно или несколько облаков. Для каждого облака действует правило «всё или ничего»: облако, у которого заполнена лишь часть обязательных значений, отклоняется с указанием недостающего, как и источник, не называющий ни одного облака. Источник читает ровно те облака, которые называет. Все значения находятся в mapping; каждый секрет может быть ссылкой ${secret:…} или ${env:…}. Секрет, введённый как буквальное значение, сохраняется в хранилище организации, а источник хранит ссылку на него.
| Облако | Ключ mapping |
Свойство коннектора | Примечания |
|---|---|---|---|
| Azure | azure_tenant_id |
azure.tenant-id |
Обязательно |
| Azure | azure_client_id |
azure.client-id |
Обязательно; ID клиента регистрации приложения |
| Azure | azure_client_secret |
azure.client-secret |
Обязательно; секрет |
| Azure | azure_subscription_ids |
azure.subscription-ids |
Обязательно; через запятую |
| AWS | aws_access_key_id |
aws.access-key-id |
Обязательно |
| AWS | aws_secret_access_key |
aws.secret-access-key |
Обязательно; секрет |
| AWS | aws_region |
aws.region |
Обязательно, например us-east-1 |
| AWS | aws_account_ids |
aws.account-ids |
Обязательно; через запятую |
| AWS | aws_role_arn |
aws.role-arn |
Необязательно; роль, принимаемая для доступа между аккаунтами |
| GCP | gcp_credentials_path |
gcp.credentials-path |
Обязательно; АБСОЛЮТНЫЙ путь к JSON-файлу ключа сервисного аккаунта |
| GCP | gcp_project_ids |
gcp.project-ids |
Обязательно; через запятую |
| — | cache_ttl_minutes |
cache.ttl-minutes |
Необязательно; как долго результат используется повторно, прежде чем облака будут опрошены снова (по умолчанию в адаптере 5) |
Файл ключа GCP открывает тот, кто читает источник. В Trino это сервер Trino, поэтому путь должен существовать на нём. В любом другом движке это pgwire-сервер источника на хосте Provisa, чей рабочий каталог — его собственный каталог состояния, поэтому относительный путь отклоняется.
В Trino источник читается через каталог cloudops. В любом другом движке движок подключает pgwire-сервер источника и читает его на месте, как источник SharePoint.
- id: cloud-estate
type: cloudops
mapping:
aws_access_key_id: ${env:CLOUDOPS_AWS_ACCESS_KEY_ID}
aws_secret_access_key: ${env:CLOUDOPS_AWS_SECRET_ACCESS_KEY}
aws_region: us-east-1
aws_account_ids: "111111111111,222222222222"
azure_tenant_id: ${env:CLOUDOPS_AZURE_TENANT_ID}
azure_client_id: ${env:CLOUDOPS_AZURE_CLIENT_ID}
azure_client_secret: ${env:CLOUDOPS_AZURE_CLIENT_SECRET}
azure_subscription_ids: "sub-1,sub-2"
splunk¶
Результаты поиска Splunk доступны для запросов как таблицы (например, internal_server). URL коннектора берётся из base_url либо формируется как https://{host}:{port} со значением порта по умолчанию 8089. Аутентификация: когда mapping.use_token равно true (по умолчанию), password передаётся как API-токен; когда false, username и password передаются как отдельные учётные данные.
| Поле источника | Свойство коннектора | Примечания |
|---|---|---|
base_url / host + port |
url |
base_url, иначе https://host:port (порт по умолчанию 8089) |
password |
token или password |
токен, когда use_token: true |
username |
user |
только когда use_token: false |
database |
app |
ограничить конкретным приложением Splunk |
mapping.datamodel_filter |
datamodel-filter |
фильтр по модели данных |
mapping.disable_ssl_validation |
disable-ssl-validation |
для самоподписанных сертификатов |
На пути реплики через pgwire (любой движок, кроме Trino) те же четыре необязательные настройки становятся ключами операнда Calcite model.json: app, token/username+password, datamodelFilter и disableSslValidation. Последние два получают типы, к которым их приводит SplunkSchemaFactory: строку и булево значение.
- id: ops-splunk
type: splunk
host: splunk
port: 8089
password: ${env:SPLUNK_TOKEN}
mapping:
use_token: true
disable_ssl_validation: true
Google Workspace и Microsoft 365¶
google_workspace и microsoft_365 читают почту, календари и задачи учётных записей, которые называет источник, и загружают их как реплики. Это не коннекторы Calcite: у каждого свой загрузчик, обращающийся к API провайдера — Gmail, Google Calendar и Google Tasks с одной стороны и Microsoft Graph с другой. Форма перечисляет их в группе Enterprise как «Google Workspace (Gmail)» и «Microsoft 365 (Outlook, Exchange)».
Таблицы. Оба типа создают одни и те же канонические таблицы: те же имена, типы столбцов и порядок. Для объединения двух не нужны ни приведение типов, ни переименование; столбец, для которого у одного из провайдеров нет значения, равен NULL. Таблицы перечислены в схеме default, и каждая строка начинается с учётной записи, из которой она прочитана. Источник предлагает таблицы тех ресурсов, которые он читает:
| Ресурс | Таблицы | Записываются инструкциями |
|---|---|---|
mail |
messages, message_recipients, folders, message_folders, threads, attachments |
Нет |
calendar |
calendars, events, event_attendees |
events, event_attendees |
tasks |
task_lists, tasks |
tasks |
Столбцы перечислены в канонической схеме почты. events хранит одиночные события, главные события серий с правилом повторения в виде строк RFC 5545 и исключения; развёрнутых вхождений серии она не хранит никогда.
Регистрация. Сначала администратор организации вводит клиент организации для платформы в Admin → Email: ID клиента и секрет, а для Microsoft 365 ещё и ID каталога (tenant) или домен. Тот, кто добавляет источник, их не видит и не вводит. Затем источник принимает одну из двух форм:
- Один почтовый ящик. Источник называет один адрес в
accounts. Его владелец нажимает Connect в форме и одобряет клиент организации. Полученное одобрение хранится в хранилище состояния организации, а не в настройках источника. В Google Workspace эта форма дополнительно задаётsign_in: google_account. - Почтовые ящики организации. Источник задаёт
mailboxesровно одним из вариантов:everyone: true,group: <address or id>илиlist: [addresses]. Учётных данных он не хранит, и подключать его никто не должен. Организация читает со своим собственным доступом, и только после того, как администратор включил «Read the organization's mailboxes». В Google этот доступ — ключ сервисного аккаунта с делегированием на уровне домена плюс адрес администратора для обращений к каталогу. В Microsoft это грант client-credentials регистрации приложения.
При второй форме одна таблица содержит строки всех ящиков. Роль, которой разрешено читать таблицу, читает все ящики, если её не ограничивает правило строк по столбцу account.
sources:
- id: team-mail
type: microsoft_365
mapping:
resources: [mail, calendar, tasks]
mailboxes:
group: support@acme.com
mail_since: "2026-01-01"
calendar_check_days: 30 # required when calendar is read
calendar_send_updates: false
Настройки. Каждый модуль по имени отклоняет ключ, который он не читает.
Ключ mapping |
Тип | Значение |
|---|---|---|
resources |
оба | Какие из mail, calendar, tasks читает источник; не менее одного |
accounts |
оба | Адрес единственного ящика (форма «один ящик») |
mailboxes |
оба | everyone, group или list (форма организации) |
sign_in |
google_workspace |
google_account (форма «один ящик») |
mail_since |
оба | YYYY-MM-DD; ничего, полученное раньше, не хранится |
mail_content |
google_workspace |
full или headers — только заголовки и метки (область gmail.metadata Gmail). Обязателен, если читается почта |
mail_search |
google_workspace |
Поиск в том виде, как его вводят в строку поиска Gmail |
mail_labels |
google_workspace |
Названия меток; читается только почта, несущая каждую из них |
mail_include_spam_trash |
google_workspace |
По умолчанию false |
calendar_send_updates |
оба | Сообщает ли запись участникам события; по умолчанию выключено |
calendar_check_days |
microsoft_365 |
На сколько дней в обе стороны от сегодняшнего дня смотрит проверка изменений; целое число, 1 или больше, обязательно, если читается календарь |
Google отклоняет поиск в области gmail.metadata, поэтому mail_content: headers вместе с mail_search или mail_since отклоняется.
Области доступа (scopes). При входе запрашиваются только области отмеченных ресурсов:
| Ресурс | Google Workspace | Microsoft 365 (делегированные) |
|---|---|---|
mail |
gmail.readonly или gmail.metadata только для заголовков |
Mail.Read |
calendar |
calendar.calendarlist.readonly, calendar.events |
Calendars.ReadWrite |
tasks |
tasks |
Tasks.ReadWrite |
| каждый источник | userinfo.email |
offline_access, User.Read |
Для формы организации Microsoft требует разрешения приложения Mail.Read и User.Read.All, а для календарей и задач ещё Calendars.ReadWrite и Tasks.ReadWrite.All. Сервисный аккаунт Google авторизован на области из DELEGATED_SCOPES, которые также включают чтение пользователей каталога и участников групп.
Обновление и актуальность. Эти источники принимают типы проб hash и none, а токен изменений предоставляет их загрузчик:
- Gmail: дайджест текущего history id каждого ящика, один вызов
users.getProfileна ящик. - Почта Microsoft: каждая сохранённая delta-ссылка, пройденная на один элемент.
- Календари и задачи Google: тег и время последнего изменения каждого календаря; тег каждого списка задач, а также id и время обновления каждой задачи.
- Календари и задачи Microsoft: строки изменений каждого ящика за
calendar_check_daysдней в обе стороны от сегодняшнего дня.
Проверка, которую выполнить нельзя, запрашивает чтение; она никогда не сообщает «unchanged». Таблицы календарей и задач читаются целиком при каждом обновлении. Реплики почты источника Microsoft 365 или Google Workspace хранятся на месте: каждая часть ящика записывается по мере поступления и сразу доступна для запросов. Источник Google Workspace, сужающий выборку через mail_search, при каждом обновлении читается заново целиком, потому что только Google может сказать, чему соответствует поиск.
Запись. INSERT, UPDATE и DELETE для events, event_attendees и tasks уходят провайдеру по одному пути записи для обоих типов. UPDATE или DELETE называет свои строки по provider, account и id; участник называется по event_id и address. Одно вхождение серии называется по series_id и original_start_at. Запись, задающая столбец вне набора записываемых столбцов таблицы, отклоняется по имени. Google пишет с sendUpdates=all, когда calendar_send_updates включена, и с sendUpdates=none, когда выключена. Microsoft Graph не может сделать такое изменение незаметно, поэтому изменение состава участников встречи отклоняется с указанием этой настройки.
Исключения. Почта только для чтения; ни одна инструкция не изменяет почтовый ящик. calendars, task_lists и все таблицы почты не принимают записи. Папки Spam и Trash в Gmail исключены, пока не включён mail_include_spam_trash. Google Tasks API хранит только день, на который назначен срок задачи, и не имеет напоминаний.
API-источники¶
Зарегистрируйте любую HTTP-конечную точку как запрашиваемую таблицу.
| Тип API | Обнаружение | Вывод типов столбцов |
|---|---|---|
openapi |
Разбор спецификации OpenAPI | Примитивы → нативные типы, объекты → JSONB |
graphql_remote |
Интроспекция схемы | Примитивы → нативные типы, объекты → JSONB |
grpc_remote |
Server reflection | Примитивы → нативные типы, объекты → JSONB |
Ответы API получаются, кешируются в PostgreSQL (настраиваемый TTL) и представляются как типы GraphQL. Кешированные таблицы участвуют в федеративных запросах наравне с любым другим источником.
Правила JSONB: сложные столбцы (объекты, массивы), хранящиеся как JSONB, не поддерживают фильтрацию. Доступ к вложенным полям использует извлечение через ->> в SQL. Связи объявляются между таблицами через скалярные столбцы внешнего ключа. Столбцы-блобы JSONB не являются целями соединения (join). Используйте продвижение JSONB (JSONB promotion), чтобы преобразовать вложенные поля в нативные скалярные столбцы, когда требуется фильтрация или соединение по ним.
GitHub и GitLab¶
GitHub и GitLab — фирменные источники на основе graphql_remote: каждый появляется в форме Sources под своим именем и регистрируется как удалённый источник GraphQL, запоминающий свой бренд. Форма принимает персональный токен доступа и ничего больше. Конечная точка (https://api.github.com/graphql, https://gitlab.com/api/graphql) и схема поставляются вместе с Provisa, поэтому схема при регистрации не интроспектируется. Токен проверяется при добавлении источника. Добавление источника не регистрирует таблиц: каждая таблица, которую предлагает схема, доступна в пространстве имён источника (gh, gl), а стюард регистрирует нужные. Поле, которое токену читать нельзя, при регистрации таблицы исключается.
Stripe¶
Stripe — фирменный источник на основе openapi, построенный так же, как GitHub и GitLab поверх graphql_remote. В форме Sources он появляется под именем Stripe в группе Enterprise. Он регистрируется как источник OpenAPI, строка которого записывает бренд в federation_hints, а путь спецификации — brand:stripe.
Что он охватывает. REST API Stripe через опубликованную Stripe спецификацию OpenAPI (stripe/openapi, spec3.json). Спецификация поставляется вместе с Provisa, закреплённой по версии и сжатой gzip, и при регистрации не загружается. Поставляемая — версия API 2026-09-30.endive с 425 путями. Каждый вызов несёт Stripe-Version, равный этой версии, поэтому каждый ответ имеет форму, описанную в спецификации, какую бы версию ни использовал аккаунт по умолчанию. Поля, которые Stripe возвращает, но которых нет в спецификации, добавляются из stripe.additions.json.
Как и в любом источнике OpenAPI, каждая операция GET — это предлагаемая таблица, а каждая другая операция — предлагаемая команда. Добавление источника не регистрирует ни одну из них: управляющий данными регистрирует нужные.
Аутентификация. Форма просит одно — токен доступа (BRAND_AUTH: token). Используйте секретный или ограниченный API-ключ Stripe. Токен отправляется как bearer-токен и один раз проверяется запросом GET /v1/balance до добавления источника. Отклонённый токен завершает регистрацию ошибкой openapi.credential_rejected; отсутствующий — ошибкой openapi.credential_required.
Обновление и актуальность. Таблица Stripe — это реплика, как и любая таблица OpenAPI. Её кешированные ответы живут cache_ttl источника, который запрос регистрации устанавливает равным 300 секундам, если не указано иное. Применимы типы проб OpenAPI. Пресет может назвать параметр, с помощью которого обновление получает только изменившееся. Бренд Stripe такого параметра не называет, поэтому каждое обновление читает свои таблицы целиком.
Исключения. Устаревшая платёжная интеграция Stripe с токенами и Sources исключена из поставляемой спецификации. Список приведён в разделе Исключения в источнике, построенном по спецификации. Собственные exclusions оператора добавляются к исключениям бренда.
stripe также присутствует в SourceType среди заглушек типов Supabase wrappers. Источник Stripe, добавленный через форму, — это источник openapi, а не эта заглушка.
Исключения в источнике, построенном по спецификации¶
Опубликованная спецификация OpenAPI описывает всё, что принимает удалённый API, включая то, что его поставщик объявил устаревшим и что развёртывание ни в коем случае не должно отправлять. Источник OpenAPI может нести исключения: то, что есть в спецификации, но источник не предлагает.
| Исключается | Задаётся | Эффект |
|---|---|---|
| Операция | method и path |
Не предлагается для регистрации как таблица или команда |
| Аргумент запроса | name, для одной операции (method и path) или, при отсутствии обоих, для каждой операции, где он есть |
Не входит в аргументы операции, не показывается, никогда не отправляется |
Исключения задаются под exclusions в mapping источника:
sources:
- id: shop-api
type: openapi
path: https://shop.example.com/openapi.json
mapping:
exclusions:
operations:
- {method: POST, path: /v1/tokens}
arguments:
- {name: source, method: POST, path: /v1/customers}
- {name: legacy_token} # on every operation that has it
Они применяются к спецификации при её загрузке, поэтому список для регистрации, сгенерированные команды и отправляемый запрос видят спецификацию, в которой исключённых элементов не существует. Можно исключить свойство тела, а также параметр запроса, заголовка или cookie; параметр пути исключить нельзя, потому что он часть адреса операции.
Исключение, называющее операцию или аргумент, которых нет в спецификации, приводит к сбою загрузки спецификации этого источника и называет каждую устаревшую запись, так что список, переставший соответствовать более новой спецификации, не остаётся незамеченным.
Фирменный источник поставляется со своими исключениями как частью кураторской настройки, а исключения оператора добавляются к ним. Источник Stripe исключает устаревшую интеграцию платежей Stripe через токены и источники: создание списания (charge), API Tokens и Sources, добавление карты или банковского счёта клиенту из токена, а также аргументы source, card, bank_account и default_card в операциях записи клиентов и оплаты счетов. Платежи принимаются через Payment Intents, Payment Methods и Setup Intents, которые источник предлагает. Чтение и удаление платёжных источников, которые у аккаунта уже есть, остаются доступными.
GovData¶
Открытые данные правительства США. Доступ разделён по тематическим группам.
Источнику задаются одна или несколько тем, и каждая тема приносит перечисленные ниже схемы адаптера. Схема может принадлежать более чем одной теме (census). Схемы ref и geo — связующие: справочные данные и география, через которые соединяются таблицы каждой темы, поэтому каждый источник обслуживает их независимо от заданных тем. Список задан один раз на сервере, и тест удерживает его в соответствии со схемами, которые обслуживает закреплённый пакет адаптера.
| Тема | Открываемые схемы |
|---|---|
COMMERCE |
sec, patents, cftc |
ECONOMY |
econ, econ_reference, ag, banking, fiscal, housing, transport |
EDUCATION |
census, edu, research |
HEALTH |
health |
CYBER |
cyber_threat, cyber_vuln |
PUBLIC_SAFETY |
crime, disasters |
ENVIRONMENT |
lands, environment |
WEATHER |
weather |
ENERGY |
energy |
GOVERNMENT |
fedregister, fec, officials, law |
DEMOGRAPHICS |
census |
| каждый источник | ref, geo |
ALL — сокращение подписки для всех тем; в форме Sources такого выбора нет.
sources:
- id: federal-commerce
type: govdata
subject: COMMERCE
domain_id: federal-analytics
description: U.S. commerce and securities data
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
id |
Да | — | Уникальный идентификатор |
subject |
Да | — | Одно из значений темы выше |
domain_id |
Да | — | Домен, которому принадлежит этот источник |
description |
Нет | "" |
Описание для человека |
Источник AskAmerica, созданный в админке, принимает один учётный параметр — ключ API. Provisa предъявляет ключ API AskAmerica и получает краткоживущие учётные данные хранилища только для чтения, с которыми читаются данные; неизвестный или отозванный ключ отклоняется там с указанием имени.
Затем источник — это источник с протоколом Postgres, как источники SharePoint, Salesforce и
облачного инвентаря: Provisa запускает для него встроенный сервер pgwire-govdata, передавая ключ и
эти учётные данные в окружение сервера, а каждый движок обращается к этому серверу так же, как к
серверу соседнего источника. DuckDB, Postgres и ClickHouse подключают его и читают его таблицы на
месте, поэтому таблица AskAmerica соединяется с таблицами других источников, лежит под
представлениями и доставляется как любая другая. У Trino для него нет коннектора, и он читает
реплику, выгруженную через сервер: копию таблицы, хранящуюся в хранилище материализации, настолько
свежую, насколько свежо её последнее обновление по настройкам кеша и репликации таблицы, а не
прочитанную во время запроса. Таблица читается из схемы адаптера, которую она называет (sec,
econ и так далее).
Сервер монтирует свои схемы из объектного хранилища, прежде чем принять подключение, — при первом запуске это занимает минуты. Он запускается при сохранении источника; пока он не готов, форма Register Table сообщает, что источник ещё запускается.
Где хранится пароль источника¶
Пароль источника никогда не хранится рядом с остальными настройками подключения. Строка sources
в плоскости управления содержит столбец password_ref, хранящий то, что было введено как пароль:
само значение или ссылку, например ${env:PG_PASSWORD} либо ${secret:SNOWFLAKE_KEY}. Она
разрешается в момент, когда источник подключается, внутри организации, от имени которой выполняется
запрос.
Значение без ${…} используется как есть. ${env:VAR} читает переменную окружения процесса
развёртывания и не требует привязки. ${secret:NAME} именует секрет, которым владеет организация,
поэтому он разрешается только в рамках собственных операций этой организации: интроспекционные
точки администрирования и терминал запросов, к которым обращается каждая поверхность, устанавливают
эту привязку.
Поле хранит то, что вы в него ввели, как бы ни был зарегистрирован источник:
- Из конфигурации. Файл — это запись: Provisa копирует значение или ссылку в
password_refдословно. - Из формы Sources. То, что введено в поле пароля, сохраняется дословно — значение или ссылка.
Provisa ничего не записывает для него в хранилище организации и не придумывает имя секрета. Чтобы
не держать учётные данные в строке плоскости управления, создайте секрет сами (см.
Секреты) и введите
${secret:NAME}в поле.
Значение, введённое как значение, хранится в базе данных плоскости управления так, как введено. Повторный ввод пароля заменяет текст поля. Удаление источника убирает его строку и ничего не убирает из хранилища: созданный вами секрет остаётся вашим.
password_ref не переносится между окружениями. Ветка (branch) или скопированное
окружение предоставляет собственные значения подключения, а хранилище, которое именует ссылка,
принадлежит тому окружению, которое её предоставило.
Профайлер данных¶
Профайлер данных — это источник, который хранит только имя, расписание и значения запуска по умолчанию. Он никогда не интроспектируется. К нему добавляются таблицы других источников, и каждый запуск измеряет их и записывает свои строки в плоскость управления организации, где они читаются как таблицы любого другого источника. О создании профайлера и добавлении в него таблиц см. Тестовые данные.
Проверки качества данных¶
Проверщик качества данных — это просто ещё один тип источника. Результат его сканирования — это данные: каждый результат проверки является наблюдением, поэтому он загружается обычным путём источников и получает всё то же, что и любой другой источник, от периодичности, актуальности, событий и lineage до управления доступом, RLS, таблицы-сетки и экспорта.
Поддерживаются два проверщика, и выбор между ними — вопрос лицензии в той же мере, что и возможностей.
| Тип источника | Диалект контракта | Дополнение (extra) | Лицензия | Разрешено в облачной плоскости |
|---|---|---|---|---|
soda |
Soda contract YAML | pip install .[soda] (soda-postgres) |
Elastic License 2.0 | Отказано — см. ниже |
great_expectations |
Expectation suite JSON | pip install .[gx] (great-expectations[postgresql]) |
Apache 2.0 | Разрешено |
Elastic License 2.0 запрещает предоставлять программу третьим лицам как размещённый или управляемый сервис, а запуск Soda в SaaS-плоскости от имени арендатора был бы именно этим. config/capabilities.yaml фиксирует это как cloud_eligible: false у опции soda, и размещённая плоскость читает этот флаг. Размещённое развёртывание, которому нужен Soda, указывает на endpoint Soda, который оператор запускает сам.
Provisa ничего не встраивает и не линкует. Сканирование выполняется в дочернем интерпретаторе (python -m provisa.dq.worker) — единственном месте, где импортируется soda_core или great_expectations. Поэтому проверщик с доступным исходным кодом никогда не попадает в серверный процесс, а если проверщик падает, он роняет подпроцесс и не трогает цикл событий.
Источник указывает на собственный endpoint pgwire Provisa. Именно поэтому один драйвер postgres может проверить таблицу, за которой стоит Snowflake или Iceberg: проверщик сканирует федеративное представление, а не систему под ним. К этому подключению применяется политика, поэтому идентичность сканирования вы объявляете явно. Если бы она наследовалась, отфильтрованный набор строк мог бы незаметно пропустить проверку.
sources:
- id: dq
type: soda
domain_id: sales-analytics
description: Soda contract scans over the governed estate
mapping:
host: localhost
port: 5439 # Provisa's pgwire endpoint
database: provisa
user: dq_scanner # the scan identity, declared explicitly
password: ${env:PROVISA_DQ_PASSWORD}
Одна таблица результатов на контракт, и контракт — это вся регистрация. Таблица несёт dq_contract, текст контракта дословно, и больше ничего о своей форме. Столбцы, водяной знак и продвижения выводятся из него.
tables:
- source_id: dq
schema_name: quality
table_name: orders_scan
domain_id: sales-analytics
change_signal: ttl_probe
cache_ttl: 3600
columns:
- name: scan_id # declared only to carry visible_to; replaced at parse
visible_to: [analyst, admin]
dq_contract: |
dataset: provisa/sales/orders
columns:
- name: customer_id
checks:
- missing:
threshold:
metric: percent
must_be_less_than: 1
checks:
- row_count:
must_be_greater_than: 0
Из этого текста регистрация выводит четыре вещи:
- Происхождение (lineage). Контракт уже называет свой целевой набор данных, поэтому регистрация разбирает его так же, как
extract_inputsразбирает SQL, и сопоставляет с управляемой таблицей. Определение одно, и второй копии, которая могла бы разойтись, нет. Контракт, называющий неуправляемый набор данных, явно падает при регистрации и поэтому никогда не загружает строки, которых никто не просил. - Столбцы. Конверт результата принадлежит проверщику: 16 поставляемых столбцов, от
scan_idдоdiagnostics. У объявленных столбцов читается только ихvisible_to, который должен совпадать у всех, после чего они заменяются. - Водяной знак.
scan_timeстановится водяным знаком, что делает загрузку добавлением. История сканирований накапливается без отдельной подсистемы истории. - Продвижения (promotions).
freshness_max_timestampиdataset_rows_testedпродвигаются из jsonbdiagnosticsв типизированные столбцы. Другие можно добавить так же, как для любого jsonb-столбца.
Для расписания новые поля не нужны. change_signal вместе с cache_ttl задают частоту опроса, mv_debounce_quiet и mv_debounce_max_delay сворачивают всплеск изменений выше по потоку в одно сканирование, календарное зерно делает его периодическим, а expected_events задерживает сканирование, пока его входы не станут актуальными на всё окно. Цикл опроса и есть планировщик сканирований.
outcome принимает значения pass, fail, warn, error или skipped. Ни одно из них само по себе ничего не запрещает. Если нужно принуждение, объявите его отдельно: preflight или MV над загруженными результатами. Загруженное наблюдение не обязано быть детерминированным, поэтому здесь допустимы проверки, которые никогда не смогли бы управлять preflight, например оценка аномальности, изменение за скользящее окно или актуальность относительно текущего момента.
Контракт пишется в интерфейсе, в панели качества данных на странице редактирования таблицы, и сырой текст контракта там всегда является источником истины. Пробный запуск выполняет контракт против живой таблицы и показывает результаты, не загружая их. Так вы поймаете контракт, чьё имя набора данных разрешилось в неожиданное место и который иначе загружал бы одни успешные строки.
Пользовательские коннекторы¶
Оператор может дать нативным движкам федерации (Postgres, DuckDB и ClickHouse) доступ к новому типу источника, объявив для него коннектор в config/custom_connectors.yaml. Код не нужен.
Так нативные движки, работающие без кластера, получают ту же расширяемость через конфигурацию, что есть у движка Trino на его собственном уровне. Там один универсальный JDBC-коннектор параметризуется под каждый тип источника, каждый тип получает тело каталога .properties, а Provisa добавляет собственные плагины коннекторов Trino (Splunk, SharePoint, Calcite).
Файл поставляется пустым. Встроенные коннекторы покрывают источники, которые работают без дополнительной настройки, а всё в этом файле пишет оператор. Чтобы использовать другой путь, например в тестах, задайте PROVISA_CUSTOM_CONNECTORS.
Виды дескрипторов¶
| Движок | Вид | Механизм | Что предоставляет дескриптор |
|---|---|---|---|
postgres |
pg_fdw |
SQL/MED (стандарт ISO) | extension, server_options, user_mapping, supports_import, table_options, remote_schema |
duckdb |
duckdb_attach |
INSTALL/LOAD + ATTACH | extension, probe_symbol, attach_template, remote_schema |
duckdb |
duckdb_scan |
INSTALL/LOAD + представление-сканер | extension, probe_symbol, scan_template |
clickhouse |
clickhouse_database |
CREATE DATABASE ENGINE=… (автоматически раскрывает каждую удалённую таблицу) |
ch_engine, engine_template |
clickhouse |
clickhouse_table |
CREATE TABLE ENGINE=… на таблицу (столбцы из реестра) |
ch_engine, engine_template (может содержать {table}) |
clickhouse |
clickhouse_scan |
CREATE TABLE ENGINE=…, ClickHouse сам выводит схему |
ch_engine, engine_template |
Postgres универсален. SQL/MED: это стандарт ISO, поэтому каждый соответствующий ему FDW разделяет одну и ту же форму DDL: CREATE SERVER … FOREIGN DATA WRAPPER <fdw> OPTIONS(…), опционально CREATE USER MAPPING, затем либо IMPORT FOREIGN SCHEMA (когда supports_import: true), либо явный CREATE FOREIGN TABLE для каждой таблицы (когда false). Дескриптор pg_fdw предоставляет только специфичную для конкретного FDW часть — имя расширения, ключи опций сервера, ключи сопоставления пользователей, флаг импорта, опции таблицы. Поэтому любой FDW, соответствующий стандарту, можно настроить только через конфигурацию.
DuckDB поддерживает два механизма. Расширение, раскрывающее каталог через ATTACH, использует duckdb_attach; раскрывающее табличную функцию для чтения использует duckdb_scan. Расширение, не подходящее ни под один из этих шаблонов, не поддерживается.
ClickHouse поддерживает три механизма, по одному на форму движка интеграции: реляционный движок DATABASE, который автоматически раскрывает каждую удалённую таблицу (clickhouse_database, например Redis/MySQL), потабличный движок, столбцы которого предоставляет реестр (clickhouse_table, например мост JDBC/ODBC; engine_template может содержать placeholder {table}, который среда выполнения подставляет), и движок файлов/lake/URL, схему которого выводит сам ClickHouse (clickhouse_scan, например HDFS/URL). SQLite (движок DATABASE, файл, без сервера) и Hudi (lakehouse, без копирования) поставляются «из коробки».
Неизвестное значение kind явно падает при запуске, поэтому опечатка в дескрипторе никогда не оставит тип источника недоступным втихую.
Проверка доступности (probe gating)¶
Доступность проверяется в момент подключения по стандартному каталогу обнаружения каждого движка:
- Postgres — проверяет
pg_extension, затемpg_available_extensions. - DuckDB — выполняет
INSTALL/LOADи проверяетduckdb_functions()на наличие объявленногоprobe_symbol. - ClickHouse — проверяет
system.table_enginesна наличие объявленногоch_engine; отсутствие в сборке приводит к падению сразу.
Объявленное расширение, которое не устанавливается, приводит к падению сразу. Никакого молчаливого пропуска, никакого запасного варианта (fallback). Коннектор, чья проверка не проходит, просто неактивен для этого развёртывания.
Переменные шаблона¶
Каждое значение server_options, значение user_mapping, attach_template и scan_template может использовать placeholder-ы вида {field}. Доступные поля:
{id}, {host}, {port}, {database}, {username}, {password}, {path}, {schema_name}, {table_name}, а также любой ключ из federation_hints. Шаблоны attach для DuckDB получают ещё {alias} — внутренний псевдоним каталога, который Provisa даёт подключённой базе.
Шаблон, ссылающийся на неизвестное поле, приводит к падению сразу в момент подключения, выявляя несоответствие дескриптора и источника до того, как некорректный DDL достигнет движка.
Примеры¶
Postgres — MongoDB через mongo_fdw (без импорта схемы; столбцы предоставляются на уровне таблицы)
# config/custom_connectors.yaml
connectors:
- engine: postgres
source_type: mongodb
kind: pg_fdw
extension: mongo_fdw
mechanism: attach_r
server_options:
address: "{host}"
port: "{port}"
user_mapping:
username: "{username}"
password: "{password}"
supports_import: false
table_options:
database: "{database}"
collection: "{table_name}"
DuckDB — файлы Excel через read_xlsx (табличная функция-сканер)
- engine: duckdb
source_type: xlsx
kind: duckdb_scan
extension: excel
install_from_community: false
probe_symbol: read_xlsx
scan_template: "read_xlsx('{path}')"
Когда любой из дескрипторов на месте, регистрация источника с объявленным source_type направляется через пользовательский коннектор при условии успешной проверки доступности. Никаких других изменений конфигурации не требуется.
Хранилища как именованные источники¶
Snowflake, Databricks и ClickHouse можно зарегистрировать как именованные источники независимо от того, какой движок федерации активен.
После регистрации хранилище читается через DirectDriver источника, который создаёт реплику в хранилище материализации активного движка, и запрос выполняется против этой реплики. На обычном прямом пути (asyncpg, aiomysql) движок полностью обходится. Здесь движок по-прежнему выполняет запрос, но против локальной реплики, а не обращается по сети к хранилищу при каждом запросе.
Чтение выполняется в формате Arrow там, где хранилище это поддерживает: Databricks использует Cloud Fetch, Snowflake использует fetch_arrow_table, а ClickHouse использует нативный колоночный HTTP-интерфейс.
Расширенные параметры подключения, которые не помещаются в стандартные поля host/port/username/password, идут в federation_hints:
sources:
- id: my-databricks
type: databricks
host: my-workspace.azuredatabricks.net
password: ${env:DATABRICKS_TOKEN}
federation_hints:
http_path: /sql/1.0/warehouses/xxxx # required — the SQL Warehouse connection detail
- id: my-snowflake
type: snowflake
host: org.snowflakecomputing.com
username: svc_provisa
password: ${env:SNOWFLAKE_PASSWORD}
federation_hints:
account: myorg-myaccount # required — Snowflake account identifier
warehouse: COMPUTE_WH # optional — virtual warehouse to use
role: PROVISA_ROLE # optional — Snowflake role
- id: my-clickhouse
type: clickhouse
host: ch.example.com
port: 8123
database: analytics
username: default
password: ${env:CLICKHOUSE_PASSWORD}
federation_hints:
secure: "true" # optional — enables TLS on the HTTP interface
Регистрация в качестве именованного источника не зависит от выбора того же хранилища в качестве движка федерации. Источник Snowflake на движке DuckDB создаёт реплику в DuckDB, а не в Snowflake.
Облачные объектные данные и данные озера (файлы parquet, csv, iceberg и delta_lake в S3, GCS или R2) — отдельный тип источника. Они подключаются на месте, когда у активного движка есть ATTACH-коннектор для этого типа, поэтому реплика не создаётся и движок сканирует объектное хранилище напрямую. Учётные данные таких источников тоже указываются в federation_hints:
sources:
- id: r2-events
type: parquet
path: s3://my-bucket/events/2026/*.parquet
federation_hints:
access_key_id: ${env:R2_ACCESS_KEY}
secret_access_key: ${env:R2_SECRET}
account_id: ${env:R2_ACCOUNT_ID} # Cloudflare R2 account (S3-compatible)
Поля конфигурации источника¶
Все источники разделяют общий набор полей.
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
id |
Да | — | Уникальный идентификатор; буквенно-цифровой, с дефисами/подчёркиваниями |
type |
Да | — | Тип источника (см. таблицы выше) |
host |
Нет | "" |
Имя хоста или IP |
port |
Нет | 0 |
Номер порта |
database |
Нет | "" |
Имя базы данных |
username |
Нет | "" |
Имя пользователя |
password |
Нет | "" |
Пароль; используйте ${env:VAR} или ${secret:NAME} вместо буквального значения (см. ниже) |
path |
Нет | null |
Путь к файлу или облачный URI для файловых и объектных/lake-источников |
base_url |
Нет | null |
Базовый URL для источников OpenAPI |
pool_min |
Нет | 1 |
Минимальный размер пула соединений |
pool_max |
Нет | 5 |
Максимальный размер пула соединений |
use_pgbouncer |
Нет | false |
Направлять соединения через PgBouncer |
pgbouncer_port |
Нет | 6432 |
Порт PgBouncer |
cache_enabled |
Нет | true |
Включить кеширование ответов API |
cache_ttl |
Нет | null |
TTL кеша в секундах; наследует глобальное значение по умолчанию, если null |
cache_catalog |
Нет | null |
Федеративный каталог для кеша API; по умолчанию — собственный каталог источника |
cache_schema |
Нет | api_cache |
Схема внутри каталога кеша |
naming_convention |
Нет | null |
Переопределить глобальное соглашение об именовании для этого источника |
federation_hints |
Нет | {} |
Параметры сессии, передаваемые движку федерации, и расширенные параметры подключения для источников-хранилищ |
mapping |
Нет | {} |
Настройки коннектора, специфичные для типа, для NoSQL- и SaaS-источников (например, auth_type SharePoint, auth_type Salesforce, use_token Splunk) |
allowed_domains |
Нет | [] |
Ограничить источник конкретными доменами; пусто = без ограничений |
description |
Нет | "" |
Описание для человека |
Источники Kafka¶
Топики Kafka настраиваются отдельно под ключом kafka_sources, ключом выступает id зарегистрированного источника kafka.
kafka_sources:
- id: kafka-support
topics:
- id: tickets
topic: support.tickets
domain_id: sales-analytics
description: "Inbound support tickets"
default_window: 1h
columns:
- name: id
- name: subject
- name: status
- name: created_at
| Поле | Описание |
|---|---|
id |
Должен совпадать с id источника с type: kafka |
topics[].id |
Логическое имя этого топика внутри Provisa |
topics[].topic |
Имя топика Kafka |
topics[].domain_id |
Домен, которому принадлежит этот топик |
topics[].description |
Описание для человека |
topics[].default_window |
Временное окно по умолчанию для оконных запросов (например, 1h) |
topics[].columns |
Определения столбцов для схемы топика |
Видимость столбцов¶
Поле visible_to на каждом столбце — это список ID ролей, которым виден этот столбец.
columns:
- name: email
visible_to: [admin] # only admin role sees this column
- name: region
visible_to: [admin, analyst] # both roles see this column
Столбцы, отсутствующие в списке visible_to роли, не появляются в GraphQL-схеме этой роли и не могут запрашиваться или упоминаться в фильтрах.
Связи¶
Связи соединяют две зарегистрированные таблицы и появляются как вложенные поля в GraphQL.
relationships:
- id: orders-to-customers
source_table_id: orders
target_table_id: customers
source_column: customer_id
target_column: id
cardinality: many-to-one
| Поле | Обязательно | Описание |
|---|---|---|
id |
Да | Уникальный идентификатор этой связи |
source_table_id |
Да | Таблица, содержащая внешний ключ |
target_table_id |
Да | Таблица, на которую ссылаются; пусто для вычисляемых связей |
source_column |
Да | Столбец в исходной таблице |
target_column |
Да | Столбец в целевой таблице; пусто для вычисляемых связей |
cardinality |
Да | many-to-one или one-to-many |
materialize |
Нет | Автоматически создать материализованное представление для межисточниковых соединений. На связи, опирающейся на связующую таблицу (junction), представление охватывает двухшаговый обход, а не прямое соединение |
refresh_interval |
Нет | Интервал обновления материализованного представления в секундах (по умолчанию: 300) |
target_function_name |
Нет | Имя функции БД для вычисляемых связей |
function_arg |
Нет | Какой аргумент функции получает значение исходного столбца |
alias |
Нет | Читаемое имя типа связи (например, WORKS_FOR) |
graphql_alias |
Нет | Именует поле SDL, которое эта связь предоставляет в родительском типе. Если отсутствует, имя выводится из field_name целевой таблицы и кардинальности связи. |
disable_cypher |
Нет | Если true, исключить эту связь из рёбер графа Cypher |
source_json_key |
Нет | Извлечь этот ключ из исходного столбца как JSON-объект перед JOIN |
via_table |
Нет | Имя зарегистрированной таблицы связующего звена (junction), через которое проходит это ребро. Указание этого поля делает ребро опирающимся на junction; оставление пустым делает его ребром по внешнему ключу |
via_source_column |
Нет | Столбец junction, сопоставляемый с source_column. Через запятую и позиционно для составного ключа |
via_target_column |
Нет | Столбец junction, сопоставляемый с target_column |
via_type_column |
Нет | Столбец-дискриминатор, когда одна junction-таблица несёт несколько типов связей |
via_type_value |
Нет | Значение дискриминатора, к которому привязано это ребро |
via_label_source |
Нет | Какая номинация именует тип Cypher: column (значение дискриминатора), table (имя таблицы junction) или fixed (объявленный псевдоним). Все приводятся к верхнему регистру со знаком подчёркивания |
Связи, опирающиеся на junction¶
Ассоциативную таблицу можно объявить полноценной связью Cypher, а не узлом. Тогда её столбцы становятся атрибутами связи:
relationships:
- id: pets-bonded-pair
source_table_id: pets
target_table_id: pets
source_column: id
target_column: id
cardinality: one-to-many
via_table: pet_companions
via_source_column: pet_id
via_target_column: companion_pet_id
via_type_column: relation_type
via_type_value: bonded pair
via_label_source: column
Связующая таблица — обычная зарегистрированная таблица, и она должна быть зарегистрирована раньше, чем связь сможет её назвать. Объявляйте по одной связи на каждое значение дискриминатора. Три строки над pet_companions дают BONDED_PAIR, LITTERMATE и SHARES_ENCLOSURE как три отдельных типа Cypher, и каждый несёт остальные столбцы строки связующей таблицы как свойства ребра. Поставляемая демо-конфигурация объявляет именно это.
Ребро через связующую таблицу существует только в Cypher. Генератор соединений GraphQL строит своё условие ON для одной пары столбцов, и второму переходу там места нет, поэтому такие рёбра не попадают ни в сгенерированный SDL, ни в pg_constraint. Сама связующая таблица по-прежнему доступна для запросов как отдельное корневое поле. Из узловой части графовой схемы Cypher она убирается, поэтому никогда не появляется как метка узла.
materialize: true работает и на таком ребре. Материализуется весь обход: представление хранит переход от источника, переход через связующую таблицу, дискриминатор и собственные столбцы связующей таблицы рядом со столбцами цели. Связующая таблица — третье плечо соединения, поэтому то, пересекает ли ребро источники, оценивается по всем трём таблицам. Связующая таблица, лежащая в другом источнике, чем две связываемые ею таблицы, материализуется, даже если те две находятся в одном источнике. Каждое объявление материализует один тип ребра, поэтому представление, построенное для bonded pair, никогда не отвечает на обход littermate.
Значения кардинальности:
many-to-one: каждой исходной строке соответствует одна целевая строка (FK к PK)one-to-many: каждой исходной строке соответствует несколько целевых строк (обратный случай)
Правила безопасности на уровне строк¶
Правила RLS вставляют предложения WHERE во время выполнения запроса, привязанные к роли и, опционально, к таблице или домену.
rls_rules:
- table_id: orders # applies to orders table only
role_id: analyst
filter: "region = current_setting('provisa.user_region')"
description: Analysts read only their own region's orders.
- domain_id: sales-analytics # applies to every table in domain
role_id: analyst
filter: "tenant_id = current_setting('provisa.tenant_id')"
description: Analysts read only their own tenant's rows.
Когда для одной и той же роли существуют и правило на уровне домена, и правило на уровне таблицы, правило на уровне таблицы имеет приоритет.
| Поле | Обязательно | Описание |
|---|---|---|
table_id |
Условно | Таблица, к которой применяется правило; взаимоисключающе с domain_id |
domain_id |
Условно | Домен, к которому применяется правило; применяется ко всем таблицам домена |
role_id |
Да | Роль, к которой применяется это правило |
filter |
Да | SQL-предикат, вставляемый в WHERE; может ссылаться на переменные сессии |
description |
Да | Для чего нужно правило — так, как его читает проверка доступа |
Функции и вебхуки¶
Функции БД¶
Отслеживайте функцию базы данных и представляйте её как запрос или мутацию GraphQL.
Источники баз данных также могут автоматически обнаруживать свои хранимые процедуры и функции из каталога поставщика (pg_proc, information_schema.routines или эквивалентов у других поставщиков), устраняя необходимость вручную регистрировать каждую из них. Обнаружение читает prokind и provolatile: неизменяемые/стабильные (immutable/stable) функции регистрируются как параметризованные отношения (аргументы процедуры становятся параметрами запроса, в той же форме, что и GET-таблицы OpenAPI), а изменчивые (volatile) процедуры регистрируются как мутации/отслеживаемые функции. Обнаруженные процедуры проходят через управление (governance) на Этапе 2 так же, как и зарегистрированные вручную.
functions:
- name: get_customers_by_region
source_id: sales-pg
schema: public
function_name: get_customers_by_region
returns: customers
domain_id: sales-analytics
description: "Returns customers filtered by region"
visible_to: [admin, analyst]
kind: query
arguments:
- name: p_region
type: String
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
name |
Да | — | Имя поля GraphQL |
source_id |
Да | — | Источник, содержащий функцию |
schema |
Нет | public |
Схема базы данных |
function_name |
Да | — | Фактическое имя функции базы данных |
returns |
Да | — | ID зарегистрированной таблицы, которую возвращает функция |
arguments |
Нет | [] |
Список определений аргументов {name, type} |
visible_to |
Нет | [] |
Роли, которым назначена функция (пусто: каждая роль). mutation дополнительно требует права write |
domain_id |
Нет | "" |
Домен, которому принадлежит эта функция |
description |
Нет | null |
Описание поля GraphQL |
kind |
Нет | mutation |
"query" или "mutation" |
Вебхуки¶
Представьте внешнюю HTTP-конечную точку как запрос или мутацию GraphQL.
webhooks:
- name: notify_support
url: http://localhost:9999/notify
method: POST
timeout_ms: 3000
domain_id: sales-analytics
description: "Send a support notification"
visible_to: [admin]
kind: mutation
arguments:
- name: message
type: String
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
name |
Да | — | Имя поля GraphQL |
url |
Да | — | URL конечной точки вебхука |
method |
Нет | POST |
HTTP-метод |
timeout_ms |
Нет | 5000 |
Таймаут запроса в миллисекундах |
returns |
Нет | null |
ID зарегистрированной таблицы, или null для встроенного типа |
inline_return_type |
Нет | [] |
Список полей {name, type} для пользовательских форм возвращаемого значения |
arguments |
Нет | [] |
Список определений аргументов {name, type} |
visible_to |
Нет | [] |
Роли, которым разрешено вызывать этот вебхук |
domain_id |
Нет | "" |
Домен, которому принадлежит этот вебхук |
description |
Нет | null |
Описание поля GraphQL |
kind |
Нет | mutation |
"query" или "mutation" |
Аутентификация¶
Аутентификация настраивается под ключом auth.
| Провайдер | Описание |
|---|---|
none |
Без аутентификации; все запросы обрабатываются как default_role |
firebase |
Firebase Authentication; требует project_id и service_account_key |
keycloak |
Keycloak OIDC |
oauth |
Универсальный OAuth 2.0 |
simple |
Имя пользователя/пароль без внешнего провайдера |
auth:
provider: firebase
assignments_source: provisa # "claims" or "provisa"
default_role: analyst
default_assignments:
- role_id: analyst
domain_id: "*"
firebase:
project_id: ${env:FIREBASE_PROJECT_ID}
service_account_key: ${env:FIREBASE_SERVICE_ACCOUNT_KEY}
assignments_source: claims читает назначения ролей из утверждений (claims) JWT. assignments_source: provisa читает их из собственного хранилища назначений Provisa.
Маршрутизация выполнения¶
Прямое выполнение. Запрос, затрагивающий только один реляционный источник, идёт прямо в собственный драйвер этого источника, быстрее 100 мс. Тип источника идёт этим путём, когда Provisa знает и его диалект SQL, и его коннектор.
Федеративное выполнение. Запрос к нескольким источникам или к источнику без прямого драйвера проходит через движок федерации. Provisa поставляется со встроенным движком; для больших развёртываний направьте его на свой совместимый кластер.
Статистика. При регистрации таблицы Provisa выполняет ANALYZE для каждой опубликованной таблицы, чтобы у стоимостного оптимизатора были числа строк, доли NULL, число различных значений и min/max. Если это не удалось, сбой записывается в журнал, а регистрация продолжается.
Графовые и семантические источники¶
Neo4j¶
Зарегистрируйте графовую базу данных Neo4j как запрашиваемый источник. Стюарды пишут запросы Cypher, проецирующие скалярные значения; Provisa кеширует результаты и представляет их как типы GraphQL.
Запросы Cypher должны использовать обращения к свойствам в предложении RETURN (RETURN n.id AS id, n.name AS name). Регистрация отклоняет запрос, возвращающий объекты узлов.
Регистрация через файл конфигурации¶
Объявите источник neo4j и его таблицы в YAML. Каждая таблица требует query_template (Cypher-запрос, порождающий её строки) и типизированные столбцы. Ключ query_template недопустим для любого другого типа источника.
Источнику требуются host, port и database. Строки получаются через POST-запрос {"statements": [{"statement": <cypher>}]} к /db/<database>/tx/commit (HTTP transaction API Neo4j). Ответ с непустым списком errors трактуется как неудавшийся запрос, а не как пустой результат.
Каждый столбец требует data_type. Загрузчик сопоставляет типы конфигурации с типом столбца API, используемым во время запроса:
Тип data_type конфигурации |
Тип API |
|---|---|
varchar, text, string, char |
string |
integer, int, bigint, smallint |
integer |
float, double, real, decimal, numeric, number |
number |
boolean, bool |
boolean |
json, jsonb |
jsonb |
varchar(N) и decimal(10,2) допускаются, и используется базовый тип до скобки.
Регистрация сохраняет строку api_sources и по одной строке api_endpoints на таблицу, поэтому таблицы переживают перезапуск без повторного чтения файла. REST-конечные точки администратора под /admin/sources/neo4j записывают те же строки.
sources:
- id: graph
type: neo4j
host: neo4j
port: 7474
database: neo4j
cache_ttl: 300
tables:
- source_id: graph
schema: neo4j
table: person_skills
query_template: >-
MATCH (p:Person)-[:HAS_SKILL]->(s:Skill)
RETURN p.name AS name, s.skill AS skill, p.experience AS years
columns:
- name: name
data_type: varchar
- name: skill
data_type: varchar
- name: years
data_type: integer
Регистрация таблицы в UI¶
У источника neo4j нет таблиц для перечисления, поэтому форма регистрации таблицы запрашивает саму таблицу вместо того, чтобы предлагать её на выбор.
- Выберите источник neo4j и домен. Селекторы схемы и таблицы, флажок обнаружения и селектор водяного знака не отображаются; источник никогда не интроспектируется.
- Введите имя таблицы и Cypher-запрос. Cypher должен проецировать скаляры (
RETURN a.name AS name); проекция, возвращающая узел или список, сообщается как ошибка. - Нажмите Preview. Форма выполняет Cypher с
LIMIT 5через GraphQL-запросneo4jPreviewи заполняет список столбцов из полученных строк, типизированных какtext,integer,double,booleanилиjson. Неудачный предпросмотр оставляет Cypher в редакторе и показывает сообщение. - Настройте видимость, псевдонимы или маскирование как для любой таблицы, затем зарегистрируйте. Форма отказывается отправляться до тех пор, пока предпросмотр не типизировал столбцы, а сервер отказывается регистрировать таблицу neo4j без Cypher (
schema.neo4j_query_required).
Cypher хранится вместе с таблицей как queryTemplate, отображается в представлении чтения таблицы и сохраняется точно так же, как при регистрации через файл конфигурации: строка api_sources и строка api_endpoints, которые гидратируются при следующем запуске. Редактирование таблицы заново сохраняет отредактированный Cypher.
Регистрация через admin REST¶
# Register via admin API (no YAML config required)
POST /admin/sources/neo4j
{
"source_id": "graph",
"host": "neo4j",
"port": 7474,
"database": "neo4j"
}
# Register a table (preview + validate before persisting)
POST /admin/sources/neo4j/graph/tables
{
"table_name": "person_skills",
"cypher": "MATCH (p:Person)-[:HAS_SKILL]->(s:Skill) RETURN p.name AS name, s.skill AS skill, p.experience AS years",
"ttl": 300
}
Конечная точка предпросмотра (POST /admin/sources/neo4j/{id}/preview) возвращает примеры строк и блокирует регистрацию, если Cypher возвращает объекты узлов.
SPARQL¶
Зарегистрируйте любое совместимое с SPARQL 1.1 хранилище триплетов (Apache Jena Fuseki, Virtuoso, Stardog и т. д.) как запрашиваемый источник.
Запросы должны быть запросами SELECT. Имена переменных в предложении SELECT автоматически становятся именами столбцов.
# Register via admin API
POST /admin/sources/sparql
{
"source_id": "knowledge-graph",
"endpoint_url": "http://fuseki:3030/ds/sparql",
"default_graph_uri": "http://example.org/graph"
}
# Register a table (executes LIMIT 5 probe to validate and infer columns)
POST /admin/sources/sparql/knowledge-graph/tables
{
"table_name": "product_categories",
"sparql_query": "SELECT ?product ?label ?category WHERE { ?product a :Product ; rdfs:label ?label ; :hasCategory ?category . }",
"ttl": 600
}
Оба коннектора используют конвейер кеша API-источника. Результаты хранятся в PostgreSQL с настраиваемым TTL, что делает их доступными для межисточниковых федеративных JOIN.
Регистрация через файл конфигурации и через UI¶
host источника sparql — это URL его конечной точки SPARQL (форма Sources хранит его так же). Каждая таблица под ним содержит query_template — SELECT-запрос, чьи переменные становятся столбцами; каждая привязка (binding) имеет тип text.
sources:
- id: sparql-demo
type: sparql
host: http://localhost:23030/provisa/query
tables:
- source_id: sparql-demo
domain_id: shelter
schema: sparql
table: volunteer
query_template: >-
PREFIX s: <http://provisa.dev/shelter#>
SELECT ?volunteer_id ?name WHERE { ?v a s:Volunteer ; s:id ?volunteer_id ; s:name ?name }
columns:
- { name: volunteer_id, data_type: text, visible_to: [org_admin] }
- { name: name, data_type: text, visible_to: [org_admin] }
Регистрация таблицы работает так же, как для Neo4j: выберите источник, введите имя таблицы и SELECT-запрос, нажмите Preview (запрос sparqlPreview выполняет его с LIMIT 5 и заполняет список столбцов), затем зарегистрируйте. Регистрация сохраняет строку api_sources и строку api_endpoints (POST в форме form-encoded на путь конечной точки, нормализатор sparql_bindings) — те же строки, что записывает регистрация через файл конфигурации, а нативный движок загружает строки через ту же цепочку получения данных, что и Neo4j.
Примеры подключения¶
PostgreSQL¶
- id: sales-pg
type: postgresql
host: postgres
port: 5432
database: provisa
username: provisa
password: ${env:PG_PASSWORD}
Snowflake¶
- id: analytics-sf
type: snowflake
host: org.snowflakecomputing.com
port: 443
database: ANALYTICS
username: svc_provisa
password: ${env:SNOWFLAKE_PASSWORD}
federation_hints:
account: myorg-myaccount
warehouse: COMPUTE_WH
Databricks¶
- id: lakehouse-db
type: databricks
host: my-workspace.azuredatabricks.net
password: ${env:DATABRICKS_TOKEN}
federation_hints:
http_path: /sql/1.0/warehouses/xxxx
MongoDB¶
- id: reviews-mongo
type: mongodb
host: mongodb
port: 27017
database: provisa
username: ""
password: ""
Межисточниковый запрос¶
{
orders(where: {region: {eq: "us"}}) {
id
amount
customers { # PostgreSQL
name
email
}
productReviews { # MongoDB (federated)
rating
comment
}
}
}
Однопоточные части маршрутизируются напрямую. Межисточниковые JOIN федерируются с автоматическим приведением типов.