ClickHouse: подключение¶
hare работает с ClickHouse — колоночной базой для аналитики — через любой из двух интерфейсов сервера; у каждого своя библиотека:
engine / схема адреса базы |
Драйвер | Установка |
|---|---|---|
clickhouse+clickhouse-connect |
асинхронный клиент clickhouse-connect, HTTP-интерфейс (порт 8123) | pip install "hare-orm[clickhouse-connect]" |
clickhouse+clickhouse-driver |
clickhouse-driver, родной протокол поверх TCP (порт 9000) | pip install "hare-orm[clickhouse-driver]" |
await Hare.init(
config={
"connections": {"analytics": "clickhouse+clickhouse-connect://default:secret@clickhouse.local:8123/analytics"},
"apps": {"analytics": {"models": ["library.analytics"], "default_connection": "analytics"}},
}
)
Для родного протокола меняются только схема и порт — модели, запросы и SQL остаются теми же:
config = {"connections": {"analytics": "clickhouse+clickhouse-driver://default:secret@clickhouse.local:9000/analytics"}}
Диалект — один из встроенных диалектов hare, построенный только на хуках диалектов и Features, как был
бы построен диалект стороннего пакета; как и другие встроенные, он импортируется, только когда его
называет соединение или поиск по имени. Он работает с ClickHouse 25.8 и новее.
Настройки¶
Путь URL называет базу данных; порт — порт интерфейса драйвера: по умолчанию 8123 для
clickhouse+clickhouse-connect и 9000 для clickhouse+clickhouse-driver.
| Настройка | Что задаёт |
|---|---|
secure |
TLS: HTTPS вместо HTTP, защищённый порт родного протокола (на сервере с настройками по умолчанию — 9440, его нужно указать в URL). По умолчанию false. |
compress |
Сжатие данных: ответов HTTP, блоков родного протокола (LZ4). По умолчанию true. |
connect_timeout |
Секунды ожидания соединения, больше 0 и не больше 3600. По умолчанию 10. |
send_receive_timeout |
Сколько секунд может выполняться команда, больше 0 и не больше 86400. По умолчанию 300. Более долгую команду останавливает сервер (max_execution_time, OperationalError с кодом 159) на любом драйвере — HTTP-клиент ждёт его ответа на секунду дольше; собственный settings(max_execution_time=...) команды главнее. |
native_port |
Порт родного протокола, на котором hare dbshell запускает clickhouse-client, от 1 до 65535. По умолчанию: для clickhouse+clickhouse-connect — порт самого клиента (9000, 9440 с secure), для clickhouse+clickhouse-driver — порт самого подключения. |
max_size |
Только clickhouse+clickhouse-driver: наибольшее число соединений клиента — столько команд он выполняет одновременно, от 1 до 1024. По умолчанию 16. |
cluster |
Имя кластера, в который входит сервер: каждая команда схемы выполняется ON CLUSTER на нём, а журнал миграций реплицируется — см. Кластер. По умолчанию нет. |
async_insert |
Сервер собирает вставки многих команд и пишет их вместе (async_insert=1) — для множества мелких вставок. По умолчанию false. |
wait_for_async_insert |
С async_insert: вставка возвращается, когда её строки записаны, а ошибка поднята (wait_for_async_insert=1); false возвращается сразу — до записи строк и без ошибки. По умолчанию true. |
transactions |
atomic() выполняет транзакцию ClickHouse — на сервере с ClickHouse Keeper и allow_experimental_transactions; проверяется при открытии соединения. См. Транзакции и блокировки. По умолчанию false. |
keeper_hosts |
Серверы ClickHouse Keeper, в которых hare берёт блокировку миграций migrate, — host[:port] через запятую (порт по умолчанию 9181). Сессия открывается при открытии соединения — ConfigurationError, если ни один сервер её не открыл. По умолчанию нет. |
С чем выполняется каждая команда¶
Каждая команда выполняется с такими настройками:
| Настройка | Зачем |
|---|---|
join_use_nulls=1 |
LEFT JOIN без подходящей строки читает NULL, как в SQL, а не значения колонок по умолчанию. |
mutations_sync=2, lightweight_deletes_sync=2 |
UPDATE или DELETE завершён, когда команда вернулась. |
session_timezone='UTC' |
Моменты читаются и записываются в UTC. |
max_query_size=1 ГиБ, max_ast_elements и max_expanded_ast_elements=100 000 000 |
Почти каждое значение команды вписывается в её текст (длинный список __in при чтении уходит внешней таблицей — см. Длинные списки __in); ограничения по умолчанию (256 КиБ текста) не пропускают длинный список __in при записи или UPDATE, записывающий большой JSON-документ. |
allow_simdjson=0 |
Текст JSON читает разборщик, который принимает целое больше 64 бит за число, — разборщик по умолчанию не читает ни одного пути документа с таким числом. |
input_format_json_read_bools_as_numbers=0 |
Логическое значение JSON остаётся логическим — массив чисел и логических значений хранил бы true как 1. |
input_format_try_infer_dates=0, input_format_try_infer_datetimes=0 |
Строка JSON, похожая на дату или момент, остаётся строкой — колонка JSON хранила бы её как дату, потеряв смещение. |
cast_string_to_dynamic_use_inference=0, cast_string_to_variant_use_inference=0 |
Текст, приводимый к значению Dynamic или Variant, остаётся текстом — сервер прочитал бы '5' как число. |
json_type_escape_dots_in_keys=1 |
Точка в ключе значения JSON — часть ключа, а не вложенный путь. |
allow_suspicious_types_in_order_by=1 |
По значению Dynamic (пути JSON) можно сортировать. |
allow_suspicious_types_in_group_by=1 |
Группировка может взять такую сортировку — values("data__n").annotate(...).order_by("data__n"). |
calculate_text_stack_trace=0 |
Текст ошибки — сообщение сервера без его стека вызовов, который присылает собственный протокол. |
Неверный пароль или пользователь и отсутствующая база подключения дают ConfigurationError на обоих
драйверах, как в PostgreSQL: повтор этого не исправит; сервер, который отказывает в соединении или
разрывает его, даёт DBConnectionError.
Значения и строки¶
Дата и момент времени хранятся с 1900-01-01 по 2299-12-31 (Date32, DateTime64); любое другое значение
сервер без ошибки заменяет ближайшим из них. hare вместо этого отклоняет такое значение — и при записи, и
в фильтре — с ValidationError. ClickhouseDatetimeField(precision=9) считает наносекунды в 64
битах и хранит моменты до 2262-04-11 23:47:16.854775 UTC — более поздний отклоняется так же.
Диапазон задан в UTC: момент в пределах суток от его краёв, прочитанный в
поясе, где местное время выходит за диапазон (1900-01-01 00:00 UTC в America/New_York), сервер
переносит в Extract(), Trunc() и фильтре по части даты в этом поясе — держите такие моменты на сутки
внутри диапазона.
bulk_create() загружает партию одной двоичной вставкой (Features.copies_bulk_inserts) — см.
Массовая загрузка, — а строки моделей и values() читаются из кортежей,
которые отдаёт библиотека, без словаря на каждую строку (Features.supports_positional_rows).
HTTP-интерфейс: clickhouse-connect¶
Каждая команда — это HTTP-запрос; clickhouse-connect сам держит HTTP-соединения и выполняет команды
параллельных задач одновременно. Свои соединения он никому не показывает: get_pool_status() даёт
UnSupportedError (Features.supports_pool_status выключен), а метрики пула это подключение не
учитывают.
Родной протокол: clickhouse-driver¶
clickhouse-driver — синхронная библиотека, а соединение родного протокола выполняет одну команду за раз.
hare выполняет каждую команду в рабочем потоке, а цикл событий ждёт её, не блокируясь; у каждого рабочего
потока своё соединение, которое открывает его первая команда. max_size — число потоков: столько команд
выполняется одновременно, остальные ждут очереди.
- Состояние пула.
get_pool_status()показывает открытые, занятые и ожидаемые соединения, их же считают метрики пула (Features.supports_pool_status). - Отменённая команда. Отмена задачи снимает её команду, если та ещё не началась. Уже отправленная команда выполняется на сервере до конца — её результат отбрасывается.
- Закрытие.
close()ждёт выполняющиеся команды до 10 секунд. После этого не начатые команды завершаются ошибкойDBConnectionError, а ещё выполняющаяся доходит до конца сама — тогда её поток закрывает своё соединение. - Команда с ошибкой. Библиотека закрывает соединение команды, завершившейся ошибкой; следующая команда этого потока открывает новое.
- Пинг. Перед каждой командой библиотека спрашивает сервер, живо ли соединение, — это ещё один обмен с сервером на каждую команду. hare отправляет этот пинг только по соединению, которое простояло больше 10 секунд: занятое соединение начинает команду сразу, а закрытое сервером во время простоя незаметно открывается заново.
- Моменты времени. Двоичная вставка записывает каждый момент целым числом тиков колонки сервера
(
DateTime64любой точности,DateTime), иDateTime64читается из тиков так же — точно, и до 1970 года, и до 2299-го. Сама библиотека переводит момент через число секунд с плавающей точкой, которое после 2242 года ошибается на микросекунду, а на Windows её перевод любого момента до 1970 года не работает. compressнужны пакетыlz4иclickhouse-cityhash— их ставитhare-orm[clickhouse-driver]. Для сервера на той же машинеcompress=falseизбавляет от работы по сжатию.
Выбор драйвера¶
Команда занимает на обоих драйверах примерно одинаковое время — его определяют работа сервера и разбор ответа самой библиотекой, а не транспорт (см. бенчмарк). Родной протокол открывает по соединению на рабочий поток и сообщает состояние пула; HTTP-интерфейс проходит через прокси и балансировщики, которые понимают только HTTP.