Resource groups#
Resource groups накладывают ограничения на использование ресурсов и могут применять политики очередей для запросов, выполняющихся внутри них, либо распределять ресурсы между подгруппами. Запрос принадлежит одной resource group и потребляет ресурсы из этой группы (и её родительских групп). За исключением ограничения на количество запросов в очереди, когда resource group исчерпывает ресурс, это не приводит к падению выполняющихся запросов; вместо этого новые запросы помещаются в очередь. Resource group может либо иметь подгруппы, либо принимать запросы, но не может делать и то, и другое одновременно.
Resource groups и связанные с ними правила выбора (selection rules) настраиваются через менеджер, который является подключаемым (pluggable).
Вы можете использовать файловый или основанный на базе данных менеджер resource group:
Добавьте файл
etc/resource-groups.propertiesУстановите свойство
resource-groups.configuration-managerвfileилиdbДобавьте дополнительные параметры конфигурации для выбранного менеджера.
File resource group manager#
Файловый менеджер resource group читает JSON конфигурационный файл,
указанный в resource-groups.config-file:
resource-groups.configuration-manager=file
resource-groups.config-file=etc/resource-groups.json
Путь к JSON файлу может быть абсолютным или относительным относительно директории данных Trino. JSON файл должен присутствовать только на coordinator.
Database resource group manager#
Менеджер resource group на базе базы данных загружает конфигурацию из реляционной базы данных. Поддерживаемые базы данных: MySQL, PostgreSQL и Oracle.
resource-groups.configuration-manager=db
resource-groups.config-db-url=jdbc:mysql://localhost:3306/resource_groups
resource-groups.config-db-user=username
resource-groups.config-db-password=password
Конфигурация resource group должна быть заполнена через таблицы
resource_groups_global_properties, resource_groups и selectors.
Если какие-либо из этих таблиц отсутствуют при запуске Trino, они будут
созданы автоматически.
Правила в таблице selectors обрабатываются в порядке убывания значений
в поле priority.
Таблица resource_groups также содержит поле environment, которое
сопоставляется со значением свойства node.environment в
Node properties (Свойства узла). Это позволяет хранить конфигурации resource group
для разных кластеров Trino в одной базе данных.
Конфигурация перечитывается из базы данных каждую секунду, и изменения автоматически применяются к входящим запросам.
Имя свойства |
Описание |
Значение по умолчанию |
|---|---|---|
|
URL базы данных для загрузки конфигурации. |
|
|
Пользователь базы данных для подключения. |
|
|
Пароль пользователя базы данных. |
|
|
Максимальный период времени, в течение которого кластер продолжает принимать запросы после ошибок обновления, из-за которых конфигурация устаревает. |
|
|
Как часто кластер перечитывает конфигурацию из базы данных. |
|
|
Включение этого флага позволяет использовать дополнительную таблицу
|
|
Свойства resource group#
name(обязательно): имя группы. Может быть шаблоном (см. ниже).maxQueued(обязательно): максимальное количество запросов в очереди. При достижении этого лимита новые запросы отклоняются.softConcurrencyLimit(необязательно): количество одновременно выполняющихся запросов, после которого новые запросы будут запускаться только если все соседние resource groups ниже своих soft-лимитов недоступны, либо если все доступные группы превышают свои soft-лимиты.hardConcurrencyLimit(обязательно): максимальное количество выполняющихся запросов.softMemoryLimit(необязательно): максимальный объём распределённой памяти, который может использовать эта группа, прежде чем новые запросы начнут помещаться в очередь. Может задаваться как абсолютное значение (например,1GB) или как процент (например,10%) от общей памяти кластера.softCpuLimit(необязательно): максимальное количество CPU-времени, которое эта группа может использовать за период (см.cpuQuotaPeriod), прежде чем будет применён штраф к максимальному количеству выполняющихся запросов. Также должен быть заданhardCpuLimit.hardCpuLimit(необязательно): максимальное количество CPU-времени, которое эта группа может использовать за период.hardPhysicalDataScanLimit(необязательно): максимальный объём данных, который эта группа может сканировать за период, прежде чем новые запросы начнут помещаться в очередь. Должен задаваться как абсолютное значение (например,1GB).schedulingPolicy(необязательно): определяет, как выбираются запросы из очереди для выполнения и как подгруппы получают право запускать свои запросы. Может принимать одно из следующих значений:fair(по умолчанию): запросы из очереди обрабатываются по принципу FIFO, а подгруппы по очереди запускают новые запросы, если у них есть очередь.weighted_fair: подгруппы выбираются на основе ихschedulingWeightи количества уже выполняющихся запросов. Ожидаемая доля выполняющихся запросов для подгруппы вычисляется на основе весов всех доступных подгрупп. Подгруппа с наименьшей относительной загрузкой запускает следующий запрос.weighted: запросы из очереди выбираются стохастически пропорционально их приоритету, заданному черезquery_prioritysession property. Подгруппы выбираются для запуска новых запросов пропорционально ихschedulingWeight.query_priority: все подгруппы также должны быть настроены сquery_priority. Запросы из очереди выбираются строго по их приоритету.
schedulingWeight(необязательно): вес подгруппы, используемый в политикахweightedиweighted_fair. По умолчанию1. См. Пример scheduling weight.jmxExport(необязательно): еслиtrue, статистика группы экспортируется в JMX для мониторинга. По умолчаниюfalse.subGroups(необязательно): список подгрупп.
Пример scheduling weight#
Scheduling weight — это способ назначения приоритета ресурсу. Подгруппы с более высоким значением scheduling weight получают более высокий приоритет. Например, чтобы обеспечить своевременное выполнение запросов пайплайнов, задайте для них более высокий вес, чем для adhoc-запросов.
В следующем примере запросы пайплайнов имеют вес 350, что выше, чем у
adhoc-запросов с весом 150. Это означает, что примерно 70% (350 из 500)
запросов поступают из подгруппы pipeline, и 30% (150 из 500) — из подгруппы
adhoc в заданный период времени. В качестве альтернативы, если задать для
каждой подгруппы значение 1, нагрузка будет распределяться равномерно —
по 50% на каждую подгруппу.
{
{
"name": "pipeline",
"schedulingWeight": 350,
},
{
"name": "adhoc",
"schedulingWeight": 150
}
}
Правила селекторов (Selector rules)#
Правила сопоставления используют возможности регулярных выражений Java.
Java реализует регулярные выражения через пакет java.util.regex.
Подробнее см. документацию Java.
user(необязательно): Java regex для сопоставления с именем пользователя.originalUser(необязательно): Java regex для сопоставления с исходным именем пользователя, т.е. до изменений session user. Например, если пользователь “foo” выполняетSET SESSION AUTHORIZATION 'bar', тоoriginalUser— это “foo”, аuser— “bar”.authenticatedUser(необязательно): Java regex для сопоставления с аутентифицированным пользователем, который всегда соответствует пользователю, прошедшему аутентификацию, независимо от изменений session user.userGroup(необязательно): Java regex для сопоставления с каждой группой, к которой принадлежит пользователь.source(необязательно): Java regex для сопоставления со строкой источника.queryText(необязательно): regex для сопоставления со строкой SQL-запроса.queryType(необязательно): строка для сопоставления с типом отправленного запроса:SELECT: запросы SELECT.EXPLAIN: запросы EXPLAIN, но не EXPLAIN ANALYZE.DESCRIBE: запросы DESCRIBE, DESCRIBE INPUT, DESCRIBE OUTPUT, а такжеSHOW-запросы, такие как SHOW CATALOGS, SHOW SCHEMAS и SHOW TABLES.INSERT: запросы INSERT, CREATE TABLE AS и REFRESH MATERIALIZED VIEW.UPDATE: запросы UPDATE.MERGE: запросы MERGE.DELETE: запросы DELETE.ANALYZE: запросы ANALYZE.DATA_DEFINITION: запросы, влияющие на определение данных. Включают операторыCREATE,ALTERиDROPдля схем, таблиц, представлений и materialized views, а также команды управления prepared statements, привилегиями, сессиями и транзакциями. Если внешним клиентам требуется доступ к процедуреsystem.runtime.kill_query()для остановки выполняющихся или ожидающих запросов, необходимо использовать этотqueryType, чтобыkill_query()выполнялся напрямую, а не ставился в очередь ожидания завершения исходного запроса.ALTER_TABLE_EXECUTE: запросы, выполняющие табличные процедуры через ALTER TABLE EXECUTE.
clientTags(необязательно): список тегов. Для совпадения каждый тег из этого списка должен присутствовать в списке тегов клиента, связанных с запросом.group(обязательно): группа, в которой будут выполняться эти запросы.
Все правила внутри одного selector объединяются логическим AND. Следовательно,
все условия должны совпасть, чтобы selector был применён.
Selectors обрабатываются последовательно, и используется первый совпавший.
Глобальные свойства#
cpuQuotaPeriod(необязательно): период, в течение которого применяются CPU-квоты.physicalDataScanQuotaPeriod(необязательно): период, в течение которого применяются квоты на сканирование физических данных.
Передача свойств selector#
Имя source можно задать следующим образом:
CLI: используйте опцию
--source.JDBC драйвер при использовании в клиентских приложениях: добавьте свойство
sourceв конфигурацию подключения и задайте значение при использовании Java-приложения, которое использует JDBC Driver.JDBC драйвер в Java-программах: добавьте свойство с ключом
sourceи значением в объектConnection, как показано в примере.
Client tags можно задать следующим образом:
CLI: используйте опцию
--client-tags.JDBC драйвер при использовании в клиентских приложениях: добавьте свойство
clientTagsв конфигурацию подключения и задайте значение при использовании Java-приложения.JDBC драйвер в Java-программах: добавьте свойство с ключом
clientTagsи значением в объектConnection, как показано в примере.
Пример#
В приведённой ниже конфигурации есть несколько resource groups, некоторые из которых являются шаблонами.
Шаблоны позволяют администраторам динамически формировать дерево resource groups. Например, в группе
pipeline_${USER} переменная ${USER} заменяется на имя пользователя, отправившего запрос.
Также поддерживается ${SOURCE}, которая заменяется на источник запроса.
Вы также можете использовать пользовательские именованные переменные в регулярных выражениях для
user, source, originalUser, authenticatedUser и queryText.
Существует шесть selectors, определяющих, какие запросы выполняются в каких resource groups:
Первый selector сопоставляет запросы от пользователя
bobи помещает их в группу admin.Следующий selector сопоставляет запросы с original пользователем
bobи помещает их в группу admin.Следующий selector сопоставляет запросы с authenticated пользователем
bobи помещает их в группу admin.Следующий selector сопоставляет запросы от группы пользователей
adminи помещает их в группу admin.Следующий selector сопоставляет все DDL-запросы (data definition) с source, содержащим
pipeline, и помещает их в группуglobal.data_definition. Это может уменьшить время ожидания, так как такие запросы обычно быстрые.Следующий selector сопоставляет запросы с source, содержащим
pipeline, и помещает их в динамически создаваемую пользовательскую pipeline-группу внутриglobal.pipeline.Следующий selector сопоставляет запросы от BI-инструментов, у которых source соответствует регулярному выражению
jdbc#(?<toolname>.*)и client tags включаютhipri. Такие запросы помещаются в динамически создаваемую подгруппу внутриglobal.adhoc. Подгруппы формируются на основе значений переменныхtoolnameиuser, полученных из source и пользователя запроса соответственно. Например, запрос с sourcejdbc#powerfulbi, пользователемkaylaи тегамиhipriиfastбудет направлен в resource groupglobal.adhoc.bi-powerfulbi.kayla.Последний selector является универсальным (catch-all) и помещает все не сопоставленные ранее запросы в пользовательскую adhoc-группу.
В совокупности эти selectors реализуют следующую политику:
Пользователь
bobи любой пользователь из группыadminсчитается администратором и может выполнять до 50 параллельных запросов.bobбудет считаться администратором, даже если изменит session user (например, черезSET SESSION AUTHORIZATIONили заголовокX-Trino-User). Запросы выполняются с учётом приоритета, заданного пользователем.
Для остальных пользователей:
Одновременно может выполняться не более 100 запросов.
До 5 параллельных DDL-запросов с source
pipeline. Запросы выполняются по FIFO.Не-DDL запросы выполняются в группе
global.pipelineс общей параллельностью 45 и ограничением 5 на пользователя. Выполнение по FIFO.Для BI-инструментов: каждый инструмент может выполнять до 10 запросов, а каждый пользователь — до 3. Если общий спрос превышает 10, следующий слот получает пользователь с наименьшим числом выполняющихся запросов, обеспечивая справедливость при конкуренции.
Все остальные запросы помещаются в пользовательскую группу внутри
global.adhoc.otherс аналогичным поведением.
File resource group manager#
{
"rootGroups": [
{
"name": "global",
"softMemoryLimit": "80%",
"hardPhysicalDataScanLimit": "50TB",
"hardConcurrencyLimit": 100,
"maxQueued": 1000,
"schedulingPolicy": "weighted",
"jmxExport": true,
"subGroups": [
{
"name": "data_definition",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 5,
"maxQueued": 100,
"schedulingWeight": 1
},
{
"name": "adhoc",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 50,
"maxQueued": 1,
"schedulingWeight": 10,
"subGroups": [
{
"name": "other",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 2,
"maxQueued": 1,
"schedulingWeight": 10,
"schedulingPolicy": "weighted_fair",
"subGroups": [
{
"name": "${USER}",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 1,
"maxQueued": 100,
"hardPhysicalDataScanLimit": "10GB"
}
]
},
{
"name": "bi-${toolname}",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 10,
"maxQueued": 100,
"schedulingWeight": 10,
"schedulingPolicy": "weighted_fair",
"subGroups": [
{
"name": "${USER}",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 3,
"maxQueued": 10
}
]
}
]
},
{
"name": "pipeline",
"softMemoryLimit": "80%",
"hardConcurrencyLimit": 45,
"maxQueued": 100,
"schedulingWeight": 1,
"jmxExport": true,
"subGroups": [
{
"name": "pipeline_${USER}",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 5,
"maxQueued": 100
}
]
}
]
},
{
"name": "admin",
"softMemoryLimit": "100%",
"hardConcurrencyLimit": 50,
"maxQueued": 100,
"schedulingPolicy": "query_priority",
"jmxExport": true
}
],
"selectors": [
{
"user": "bob",
"group": "admin"
},
{
"originalUser": "bob",
"group": "admin"
},
{
"authenticatedUser": "bob",
"group": "admin"
},
{
"userGroup": "admin",
"group": "admin"
},
{
"source": ".*pipeline.*",
"queryType": "DATA_DEFINITION",
"group": "global.data_definition"
},
{
"source": ".*pipeline.*",
"group": "global.pipeline.pipeline_${USER}"
},
{
"source": "jdbc#(?<toolname>.*)",
"clientTags": ["hipri"],
"group": "global.adhoc.bi-${toolname}.${USER}"
},
{
"group": "global.adhoc.other.${USER}"
}
],
"cpuQuotaPeriod": "1h",
"physicalDataScanQuotaPeriod": "1h"
}
Database resource group manager#
Этот пример предназначен для базы данных MySQL.
-- global properties
INSERT INTO resource_groups_global_properties (name, value) VALUES ('cpu_quota_period', '1h');
-- Every row in resource_groups table indicates a resource group.
-- The enviroment name is 'test_environment', make sure it matches `node.environment` in your cluster.
-- The parent-child relationship is indicated by the ID in 'parent' column.
-- create a root group 'global' with NULL parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_physical_data_scan_limit, hard_concurrency_limit, max_queued, scheduling_policy, jmx_export, environment) VALUES ('global', '80%', '50TB', 100, 1000, 'weighted', true, 'test_environment');
-- get ID of 'global' group
SELECT resource_group_id FROM resource_groups WHERE name = 'global'; -- 1
-- create two new groups with 'global' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, environment, parent) VALUES ('data_definition', '10%', 5, 100, 1, 'test_environment', 1);
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, environment, parent) VALUES ('adhoc', '10%', 50, 1, 10, 'test_environment', 1);
-- get ID of 'adhoc' group
SELECT resource_group_id FROM resource_groups WHERE name = 'adhoc'; -- 3
-- create 'other' group with 'adhoc' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, scheduling_policy, environment, parent) VALUES ('other', '10%', 2, 1, 10, 'weighted_fair', 'test_environment', 3);
-- get ID of 'other' group
SELECT resource_group_id FROM resource_groups WHERE name = 'other'; -- 4
-- create '${USER}' group with 'other' as parent.
INSERT INTO resource_groups (name, soft_memory_limit, hard_physical_data_scan_limit, hard_concurrency_limit, max_queued, environment, parent) VALUES ('${USER}', '10%', '10GB', 1, 100, 'test_environment', 4);
-- create 'bi-${toolname}' group with 'adhoc' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, scheduling_policy, environment, parent) VALUES ('bi-${toolname}', '10%', 10, 100, 10, 'weighted_fair', 'test_environment', 3);
-- get ID of 'bi-${toolname}' group
SELECT resource_group_id FROM resource_groups WHERE name = 'bi-${toolname}'; -- 6
-- create '${USER}' group with 'bi-${toolname}' as parent. This indicates
-- nested group 'global.adhoc.bi-${toolname}.${USER}', and will have a
-- different ID than 'global.adhoc.other.${USER}' created above.
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, environment, parent) VALUES ('${USER}', '10%', 3, 10, 'test_environment', 6);
-- create 'pipeline' group with 'global' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, jmx_export, environment, parent) VALUES ('pipeline', '80%', 45, 100, 1, true, 'test_environment', 1);
-- get ID of 'pipeline' group
SELECT resource_group_id FROM resource_groups WHERE name = 'pipeline'; -- 8
-- create 'pipeline_${USER}' group with 'pipeline' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, environment, parent) VALUES ('pipeline_${USER}', '50%', 5, 100, 'test_environment', 8);
-- create a root group 'admin' with NULL parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_policy, environment, jmx_export) VALUES ('admin', '100%', 50, 100, 'query_priority', 'test_environment', true);
-- Selectors
-- use ID of 'admin' resource group for selector
INSERT INTO selectors (resource_group_id, user_regex, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'admin'), 'bob', 6);
-- use ID of 'admin' resource group for selector
INSERT INTO selectors (resource_group_id, user_group_regex, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'admin'), 'admin', 5);
-- use ID of 'global.data_definition' resource group for selector
INSERT INTO selectors (resource_group_id, source_regex, query_type, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'data_definition'), '.*pipeline.*', 'DATA_DEFINITION', 4);
-- use ID of 'global.pipeline.pipeline_${USER}' resource group for selector
INSERT INTO selectors (resource_group_id, source_regex, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'pipeline_${USER}'), '.*pipeline.*', 3);
-- get ID of 'global.adhoc.bi-${toolname}.${USER}' resource group by disambiguating group name using parent ID
SELECT A.resource_group_id self_id, B.resource_group_id parent_id, concat(B.name, '.', A.name) name_with_parent
FROM resource_groups A JOIN resource_groups B ON A.parent = B.resource_group_id
WHERE A.name = '${USER}' AND B.name = 'bi-${toolname}';
-- 7 | 6 | bi-${toolname}.${USER}
INSERT INTO selectors (resource_group_id, source_regex, client_tags, priority) VALUES (7, 'jdbc#(?<toolname>.*)', '["hipri"]', 2);
-- get ID of 'global.adhoc.other.${USER}' resource group for by disambiguating group name using parent ID
SELECT A.resource_group_id self_id, B.resource_group_id parent_id, concat(B.name, '.', A.name) name_with_parent
FROM resource_groups A JOIN resource_groups B ON A.parent = B.resource_group_id
WHERE A.name = '${USER}' AND B.name = 'other';
-- | 5 | 4 | other.${USER} |
INSERT INTO selectors (resource_group_id, priority) VALUES (5, 1);