Learning Platform
Глоссарий Troubleshooting
Урок 04.02 · 21 мин
Средний
connectorSPIpluginarchitecture

Что такое коннектор: реализация SPI

В прошлом уроке мы выяснили, что Trino не хранит данные и адресует их через имя catalog.schema.table. Но как именно движок умеет читать из PostgreSQL и из Parquet-файла на S3, не зная заранее ничего ни о PostgreSQL, ни о Parquet? Ответ — коннекторы. Коннектор это компонент, который переводит язык конкретного источника на язык, понятный ядру Trino.

Этот урок отвечает на вопрос «что такое коннектор» через ключевое понятие — Service Provider Interface (SPI). Понять SPI важно, потому что именно эта архитектура объясняет, почему Trino расширяется новыми источниками без переписывания ядра.


Проблема: ядро не должно знать про источники

Представьте, что ядро Trino — парсер SQL, планировщик, распределённое исполнение — содержит код для работы с PostgreSQL: JDBC-вызовы, маппинг типов, специфику диалекта. Затем добавили поддержку MySQL — ещё код в ядре. Потом Iceberg, Kafka, MongoDB, Cassandra. Ядро превращается в свалку, где логика движка перемешана с логикой каждого источника. Добавить новый источник нельзя, не тронув ядро. Сторонний разработчик не может добавить свой источник вообще.

Решение — провести жёсткую границу. Ядро движка знает только абстрактный «источник данных», описанный набором интерфейсов. Конкретику каждого источника реализует отдельный модуль — коннектор. Эта граница и называется SPI — Service Provider Interface.

SPI как граница между ядром и источниками
Trino engineЯдро: парсер SQL, анализатор, планировщик, оптимизатор, распределённое исполнение. Не знает ничего о конкретных источниках.
вызывает через SPI
Trino SPIService Provider Interface: набор Java-интерфейсов, описывающих абстрактный источник данных. Стабильный контракт.
реализуется коннекторами
PostgreSQLКоннектор реализует SPI поверх JDBC: переводит вызовы движка в SQL-запросы к PostgreSQL.
IcebergКоннектор реализует SPI поверх табличного формата Iceberg и object storage.
KafkaКоннектор реализует SPI поверх Kafka: топик становится таблицей.

API (Application Programming Interface) и SPI смотрят в разные стороны. API — это то, что движок предоставляет наружу клиентам: вы посылаете SQL, получаете результат. SPI — то, что движок требует от плагина: «реализуй вот эти интерфейсы, и я смогу с тобой работать». SPI это контракт «provider» (поставщика возможностей) с движком.


Что такое SPI технически

Технически Trino SPI — это набор Java-интерфейсов и базовых классов в отдельном Maven-модуле trino-spi. Этот модуль намеренно изолирован: он почти не зависит от внутренностей движка. Так сделано, чтобы внутреннее устройство ядра можно было менять, не ломая существующие коннекторы.

Коннектор — это плагин, реализующий интерфейсы SPI. Главные точки входа:

  • Plugin — точка входа плагина. Метод getConnectorFactories() возвращает список фабрик коннекторов, которые плагин предоставляет.
  • ConnectorFactory — фабрика. Метод getName() возвращает имя (то самое, что вы пишете в connector.name), а create() строит экземпляр коннектора по переданным свойствам catalog.
  • Connector — собственно коннектор. Отдаёт движку набор сервисов: как читать метаданные, как разбивать данные на части, как читать и писать.

Цепочка простая: при старте Trino загружает плагины, у каждого спрашивает фабрики, запоминает их по имени. Когда вы добавляете catalog с connector.name=postgresql, движок находит фабрику с этим именем и просит её создать коннектор, передавая свойства из properties-файла.

NOTE

Один плагин может содержать несколько фабрик коннекторов. Например, плагин для систем object storage может поставлять и Hive-, и Iceberg-, и Delta Lake-коннекторы. Поэтому “плагин” и “коннектор” — не синонимы: плагин это единица поставки и загрузки, коннектор это единица подключения источника.


Где коннекторы лежат и как загружаются

Коннекторы поставляются как директории в каталоге plugin/ установки Trino. Каждая поддиректория — один плагин со своими JAR-файлами:

trino-server-481/
  plugin/
    postgresql/      <- JAR-ы коннектора PostgreSQL
    iceberg/         <- JAR-ы коннектора Iceberg
    kafka/           <- JAR-ы коннектора Kafka
    tpch/            <- JAR-ы коннектора TPC-H
    ...

Каждый плагин загружается своим изолированным classloader. Это решает реальную проблему: коннектору PostgreSQL может быть нужна одна версия библиотеки, коннектору Iceberg — другая, несовместимая. Без изоляции classloader-ов эти версии конфликтовали бы. С изоляцией каждый плагин живёт в своём «пузыре зависимостей», и конфликта нет.

Загрузка происходит один раз — при старте сервера Trino. Плагины обнаруживаются в plugin/, classloader-ы создаются, фабрики регистрируются. Сами catalog при этом ещё не создаются — это отдельный шаг.

Важно различать два уровня:

УровеньЧто этоКогда происходит
ПлагинКод коннектора в plugin/имя/Загружается при старте сервера
CatalogКонфигурация подключения в etc/catalog/имя.propertiesСоздаётся из properties-файла

Плагин это потенциальная возможность («Trino умеет PostgreSQL»). Catalog это конкретное использование («есть подключение prod_postgres к такому-то серверу»). Один плагин может обслуживать много catalog: десять PostgreSQL-серверов это один плагин postgresql и десять properties-файлов.


Что коннектор скрывает от движка

Главная ценность SPI-архитектуры в том, что движок работает со всеми источниками одинаково, а вся разница спрятана внутри коннектора. Что именно коннектор берёт на себя:

Что коннектор транслирует
Запрос движкаДвижок оперирует абстракциями SPI: запросить метаданные, получить части данных, прочитать строки.
коннектор переводит
Язык источникаКоннектор превращает абстрактные вызовы в конкретные действия: SQL для JDBC, чтение Parquet, опрос Kafka.
  • Маппинг типов. Родные типы источника (int4 в PostgreSQL, INT96-таймстемп в Parquet) коннектор переводит в типы Trino и обратно.
  • Доступ к метаданным. Какие схемы и таблицы есть, какие у них столбцы — коннектор отвечает на это, обращаясь к источнику (system-каталоги PostgreSQL, Hive Metastore для Iceberg).
  • Чтение данных. Коннектор знает, как физически достать строки: выполнить SQL через JDBC, распарсить ORC-файл, вычитать сообщения из топика.
  • Запись данных — если источник её поддерживает и коннектор реализует соответствующую часть SPI.
  • Поддержка pushdown. Коннектор сообщает движку, какие операции (фильтрация, проекция, агрегация) он умеет выполнить сам, на стороне источника, вместо того чтобы движок тянул всё к себе.

Последний пункт особенно важен. SPI не «всё или ничего». Коннектор может реализовать SPI частично: например, уметь читать, но не писать; поддерживать predicate pushdown, но не aggregation pushdown. Поэтому возможности у коннекторов разные, и это нормально — каждый реализует ровно то, что имеет смысл для его источника.

TIP

Когда вы оцениваете незнакомый коннектор, первым делом смотрите его страницу в документации Trino: там перечислено, что он умеет — чтение, запись, какие виды pushdown, какие SQL-операции (CREATE TABLE, INSERT, MERGE) поддержаны. Два коннектора к разным источникам почти никогда не равны по возможностям.


Коннектор работает на каждой ноде

Важная деталь, вытекающая из распределённой природы Trino: коннектор — это не что-то, что живёт только на coordinator. Экземпляр коннектора создаётся на каждой ноде кластера — и на coordinator, и на каждом worker.

Причина — в разделении ролей, которое мы вводили в архитектуре. Coordinator планирует запрос: ему коннектор нужен, чтобы получить метаданные — структуру таблиц, статистику. Worker-ы исполняют запрос: им коннектор нужен, чтобы физически читать данные — открывать соединения, парсить файлы. Раз обе роли работают с источником, коннектор должен быть доступен на всех нодах.

Экземпляр коннектора на каждой ноде
CoordinatorКоннектор здесь нужен для планирования: получить метаданные и статистику таблиц.
тот же коннектор, та же конфигурация
Worker AКоннектор здесь нужен для исполнения: физически читать данные из источника.
Worker BТот же коннектор, тоже читает данные источника — параллельно.

Отсюда практическое следствие, к которому мы вернёмся в уроке про конфигурацию: catalog properties file должен лежать на каждой ноде. Если конфигурацию положить только на coordinator, он сможет спланировать запрос (метаданные получены), но worker-ы при попытке прочитать данные не найдут конфигурацию источника — и запрос упадёт.

Ещё одно свойство — коннектор сам по себе не хранит состояние данных между запросами. Он не кэширует строки таблиц у себя (хотя может кэшировать метаданные на короткое время ради скорости планирования). Каждый запрос идёт в источник заново. Это согласуется с природой Trino: движок не хранилище, и коннектор — лишь адаптер доступа, а не буфер данных.

NOTE

Не путайте экземпляр коннектора с подключениями к источнику. Экземпляр коннектора создаётся на каждой ноде при создании catalog. А вот соединения с источником (например, JDBC-соединения в пуле) коннектор открывает по мере надобности, когда задачам этой ноды реально нужно читать данные. Экземпляр коннектора есть всегда; соединения — приходят и уходят.


Почему это даёт расширяемость

Сложим картину. Ядро Trino знает только SPI — абстрактный контракт источника данных. Любой источник, для которого кто-то написал коннектор (реализацию SPI), становится доступен через тот же ANSI SQL и то же распределённое исполнение. Добавление нового источника не требует изменения ядра — нужен лишь новый плагин в plugin/.

Именно поэтому экосистема коннекторов Trino насчитывает десятки источников: реляционные СУБД, object storage с табличными форматами, NoSQL, поисковые движки, streaming. И поэтому компания или сообщество могут написать собственный коннектор к внутренней системе, не имея доступа к исходникам ядра и не дожидаясь релиза Trino. SPI это и есть механизм, превращающий Trino из «движка для конкретных источников» в универсальную точку доступа к данным.


Попробуй сам

На работающем кластере Trino исследуйте, какие плагины загружены, и как они соотносятся с catalog:

  1. Зайдите на сервер Trino и посмотрите содержимое директории plugin/ (в Docker-образе это путь вроде /usr/lib/trino/plugin/). Сколько там поддиректорий? Каждая — отдельный плагин.
  2. Посмотрите содержимое etc/catalog/. Сколько там .properties-файлов? Откройте один и найдите строку connector.name — это имя фабрики коннектора.
  3. Сопоставьте: для каждого connector.name из properties-файлов найдите соответствующую поддиректорию в plugin/.
  4. Найдите плагин, для которого нет ни одного catalog. Это иллюстрирует разницу: плагин загружен (возможность есть), но catalog не создан (источник не подключён).

Сформулируйте письменно разницу между «плагин загружен» и «catalog создан» — это разделение объясняет всю модель расширяемости Trino.


Проверка знанийKnowledge check
Что такое SPI в Trino, и почему именно эта архитектура позволяет добавлять новые источники данных, не меняя ядро движка?
ОтветAnswer
SPI (Service Provider Interface) — это набор Java-интерфейсов в изолированном модуле trino-spi, описывающий абстрактный источник данных: как читать метаданные, как разбивать данные на части, как читать и писать. Ядро Trino (парсер, планировщик, исполнение) знает только этот абстрактный контракт и ничего не знает о конкретных источниках. Коннектор — это плагин, реализующий интерфейсы SPI для конкретного источника: он переводит абстрактные вызовы движка в родной протокол источника (SQL по JDBC, чтение Parquet, опрос Kafka), занимается маппингом типов и сообщает движку, какие операции умеет выполнить сам. Поскольку граница между ядром и источником формализована через SPI, добавление нового источника сводится к написанию нового плагина и помещению его в директорию plugin/ — ядро при этом не меняется. Именно так Trino получает экосистему из десятков коннекторов и возможность для сторонних разработчиков писать свои коннекторы без доступа к исходникам ядра.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. В чём разница между API и SPI применительно к Trino?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6