
Представьте: Один неоптимизированный запрос от неопытного коллеги - и вот уже 40 ТБ SPILL-файлов парализуют систему.
Срабатывает лимит на уровне Greenplum, запрос завершён. Никто ничего не знает.
Создаются заявки, пишутся письма, пользователь недоволен.
Это не какая-то выдуманная история, а обычный будний день в большом Greenplum. Вернее, так было раньше.
Мы устали от этой боли и, вдохновившись докладом Дмитрия Немчина и Андрея Башлыкова, написали “Демократизатор” - систему автоматического контроля пользовательской нагрузки.
Рассказываю, как это и с чем это едят, но для начала необходимо немного рассказать немного о себе и о самой СУБД.
Меня зовут Выборнов Николай, я занимаю должность ведущего разработчика в отделе поддержки инфраструктуры единой технологической платформы. Инженер по автоматизации и управлению инфраструктурой, автор «Демократизатора», а также ряда других сервисов - от LDAP-синхронизации с несколькими системами до платформы управления виртуальной инфраструктурой.
Как устроен Greenplum
Основные компоненты
Greenplum - это массивно-параллельная СУБД (MPP), где данные и вычисления распределены между несколькими узлами.
Мастер-хост:
Принимает SQL-запросы
Генерирует оптимальный план выполнения (Query Planner)
Распределяет задачи через Query Executor
Агрегирует результаты
Сегмент-хосты:
Хранят шардированные данные
Выполняют параллельную обработку
Обмениваются данными через Interconnect
Interconnect:
Высокоскоростная внутренняя сеть (10/25 Gigabit Ethernet)
Обеспечивает передачу промежуточных данных
Отказоустойчивость:
Сегмент-хосты: Каждый первичный сегмент имеет своего зеркального двойка (mirror) на другом физическом хосте. При сбое первичного сегмента обработка запросов автоматически переключается на его зеркало. Восстановление исходного состояния требует административного вмешательства (gprecoverseg).
Мастер-хост: Для обеспечения доступности требуется настройка отдельного Standby Master. Переключение при сбое активного мастера на standby выполняется вручную администратором с помощью утилиты gpactivatestandby. Без standby-мастера отказ мастера приводит к простою всей системы.
Принцип работы
Данные распределяются между сегментами по выбранному ключу
Каждый сегмент обрабатывает свою часть данных параллельно
Результаты агрегируются на мастер-хосте

Выполнение запроса
Клиент шлёт запрос на мастер-хост.
Master разбирает SQL, строит план, отправляет задачи на сегмент-хосты.
-
Сегменты работают параллельно:
Сканируют свои куски данных.
Обмениваются данными через Interconnect (если нужно).
Master собирает результаты, применяет финальные операции (сортировка, лимиты), возвращает клиенту.
SPILL как угроза
Если вы работаете с Greenplum, то знаете, что:
Один тяжелый запрос может исчерпать общий пул памяти (MEMORY_SHARED_QUOTA) ресурсной группы. Это не оставит остатков для других запросов - они просто встанут в очередь и не будут выполняться, пока память не освободится, что может парализовать все процессы в этой ресурсной группе.
SPILL-файлы, представляющие из себя миллионы мелких файлов, могут разрастись до десятков ТБ. Это приводит к исчерпанию свободного места на файловой системе, что вызывает аварийную остановку работы сегмента и интенсивный износ дисков.
pg_stat_activity - простыня ужасов, которую неудобно мониторить вручную
Пользователь просто нажал на кнопочку в DBeaver, а у тебя падают ETL, сегменты. И никакой обратной связи.
Пара профессиональных джоинов с фильтрами - и вот уже в пятницу/субботу вечером сыпятся алерты: кластер в нокауте, администратору надо срочно бежать к компьютеру.
Основные причины возникновения spill-файлов при исполнении запросов:
обработка большого объема данных без фильтров (особенно эффективны фильтры по полю партиционирования)
создание хэш-таблицы на основе таблицы большого объема
устранение дублей (DISTINCT) на большом количестве полей
сортировка в SQL-запросе (ORDER BY) на большом количестве полей
группировка в SQL-запросе (GROUP BY)
соединение таблиц с разными ключами распределения
соединение таблиц по полям, не являющимися ключами распределения
Оптимизация:
Остановить запущенный запрос, текст которого приложен в письме, если он ещё исполняется на кластере.
Проверить его текст на соответствие основным причинам создания spill-файлов (см. выше).
-
Проверить ключи распределения таблиц, участвующих в операциях соединения запроса. Вместо $schemaname.tablename в текст запроса необходимо подставить названия интересующих таблиц в указанном формате.
```SELECT attname FROM pg_attribute WHERE attrelid = (SELECT '$schemaname.tablename'::regclass::oid) AND attnum = '1'
-
Проверить равномерность распределения строк в таблицах по сегментам Greenplum. Вместо $schemaname.tablename в текст запроса необходимо подставить названия интересующих таблиц в указанном формате.
```SELECT '$schemaname.tablename' AS "Table Name", max(c) AS "Max Seg Rows", min(c) AS "Min Seg Rows", (max(c)-min(c))*100.0/max(c) AS "Percentage Difference Between Max & Min" FROM (SELECT count(*) c, gp_segment_id FROM $schemaname.tablename GROUP BY 2) AS a;
Собрать план запроса с помощью EXPLAIN или EXPLAIN ANALYZE. Проанализировать его в planchecker.
Почему мы решили сделать свой инструмент
Накопленные проблемы:
Даже при настроенных лимитах периодически возникают критические нагрузки, которые нарушают работу всей системы
Для аналитика или разработчика его запрос - это запрос в чёрный ящик. Он просто отправляет его и ждёт результат, не понимая, что происходит внутри кластера
Администраторы видят алерты или аномалии в мониторинге когда кластер уже испытывает проблемы: растут показатели нагрузки, появляются блокировки, заполняются диски
Надо было навести порядок в зоопарке. Разработать единые правила. Обозначить, какие операции опасны для кластера
Не просто ограничить плохие запросы, но и объяснять пользователям, какие именно правила они нарушают и как это исправить
Greenplum - мощная система, но:
средств для тонкой автоматизации и реакции на события нет
можно настроить лимиты по тем же спилам, но не будет обратной связи, не будет собираться статистика, нет гибкости
мониторить вручную - это очень дорого и неудобно
пользователи не понимают, что их запросы неоптимальные, что они что-то ломают
Нам нужен был инструмент, который:
работает как watchdog
умеет реагировать на разные типы нарушений
даёт прозрачную обратную связь пользователям, администраторам и ответственным за кластер
имеет человеческий интерфейс
и не требует постоянного ручного контроля от администратора
Так появился Демократизатор. Он:
24/7 следит за БД
находит плохие запросы по заданным условиям
предупреждает пользователей
даёт обратную связь
убивает тяжёлые процессы
банит злостных нарушителей
и молчит, когда всё хорошо
Название и логотип
Почему именно Демократизатор?
Нам надо было навести порядок в наших владениях. Естественно, сначала внедрение подобного рода инструментов встречается без оваций со стороны пользователей.
Мы вспомнили, что резиновую дубинку называют «демократизатором» - как сатиру на методы «наведения демократии».
А логотип решили выбрать такой:

Это птичка, которая держит сливы и защищает их мечом.
Кстати, между собой мы называем его птицей, птичкой, и пташкой ?
Архитектура Демократизатора
Используемые технологии
RedOS 7.3.2 - базовый образ
Python 3.12 - язык программирования
Django - веб фрейморк
Celery - распределенная очередь задач (воркеры)
Valkey (выбран из-за Open-Source, для удобства далее буду называть Redis) - key-value хранилище для кеша и Celery
Celery beat - планировщик задач
Flower - мониторинг Celery
PostgreSQL - мета БД
Такая связка была выбрана для фокусирования на самом алгоритме Демократизатора и для быстрого создания интерфейса.
Компоненты

Используется стандартная архитектура Django приложения, только мы добавили еще Celery Beat для контроля тасок по расписанию и Flower для мониторинга.
Воркеры подключаются к целевым БД - нашим Greenplum

Для лёгкого динамического управления расписанием мы использовали RedBeat https://github.com/sibson/redbeat , хотя есть альтернативы https://github.com/celery/django-celery-beat .
Мета

Основа меты Демократизатора
Database - источник данных (Поддерживается Greenplum и PostgreSQL через Psycopg2)
-
Task - правило, по которому происходит проверка (SQL + лимиты + правила)
Существует 2 типа тасок:notify_only - Простое уведомление о нарушении правила, никаких действий не предпринимается.
-
notify_and_kill - уведомления о нарушении с отменой запроса пользователя. Для данного типа дополнительно устанавливаются 2 значения:
count_to_kill - число раз, при котором УЗ будет выслано сообщение - предупреждение в случае нарушения правила перед отстрелом запроса.
kill_value - значение, при котором запрос УЗ будет убит сразу же без предупреждения (аварийный механизм сброса нагрузки с системы).
Violation - зафиксированное нарушение (SQL запрос, usename, procpid)
Notification - уведомление (Значение, слепок времени, дополнительная информация)
WhiteList - белый список УЗ, который может подключаться к таске (может подключаться не один список)
Надёжность
На текущем этапе мы сознательно пошли на упрощения для скорости разработки и внедрения:
Celery. Встроенный механизм блокировок не позволяет одной проверке запуститься повторно, пока предыдущая не окончена. Целостность данных дополнительно защищена флагами на уровне БД Демократизатора.
Redis (Valkey) работает в standalone-режиме, так как отказ Демократизатора не останавливает работу Greenplum, а лишь временно отключает автоматический контроль. Состояние очереди задач не является критически важным данных - воркер просто начинает работу над новыми задачами по расписанию после перезапуска, в процессе которого незавершённые задачи принудительно помечаются как неактивные в БД Демократизатора и в самом Redis (Valkey).
Как это работает
Организационные моменты
Администратор
Делает техническую учётную запись в БД
Наделяет её необходимыми правами
Заказывает сетевую связность Демократизатор ↔ Greenplum DB
Добавляет данные для подключения в Демократизатор
Убеждается, что БД подключена к Демократизатору (в web ui)
Выбирает тип таски (notify_only / notify_and_kill)
Заполняет лимиты, частоту запуска и остальные параметры
При создании таски идёт проверка возвращаемых данных SQL запроса. Если необходимая информация отсутствует, таска не добавится.
Обычный пользователь (не администратор) получает доступ в интерфейс с правами Read only.
Проверка
Основа любой таски - это SQL запрос, который должен вернуть:
value - значение метрики (время, объём, блокировка и т.д.)
usename - имя УЗ
procpid - pid процесса
current_query - SQL запрос, который будет записан в нарушение
additional_info (опционально) - например, кто кого блокирует, записывается потом в Notification
ip_address (опционально в случае отсутствия) - ip адрес клиента
Пример проверки на блокировки:
select
blocked_query AS current_query,
blocked_pid as procpid,
blocked_user as usename,
string_agg(E'Инфо о блокировщике: \nUser: ' || blocking_user||E'\npid: ' || blocking_pid ||E'\nquery (первые 100 символов):\n'||substring(blocking_query, 1,100), E'\n\n') as additional_info,
extract (epoch from to_timestamp(age, 'HH24h:MIm:SSs')::time)::int as value,
ri.client_addr ip_address
from public.v_blocked_queries_allusers sa
JOIN xxx.real_ip ri ON ri.procpid=sa.blocked_pid
group by 1,2,3,5,6
IP Адрес
В какой-то момент мы столкнулись с необходимостью сохранять IP адрес клиента-нарушителя.
Но вот незадача: если УЗ ходит через pgbouncer, то стандартным запросом мы будем возвращать только адрес хоста, где находится баунсер, а никак не адрес конечного клиента.
Вот как мы решили эту проблему:
Это обычная fdw, которая запускает команду к 2 таблицам в постгресе и реализуется это как внешняя таблица в GP.
(Мастер должен иметь возможность подключаться к БД pgbouncer)
--WEB таблица возвращает SHOW SERVERS из pgbouncer
CREATE EXTERNAL WEB TABLE xxx.pgbouncer_servers (
type char(1),
user_name char(40),
database_name char(20),
state char(15),
addr char(15),
port int,
local_addr char(15),
local_port int,
connect_time char(40),
request_time char(40),
wait int,
wait_us int,
close_needed int,
ptr char(15),
link char(15),
remote_pid int,
tls int
)
EXECUTE 'psql -p 6432 -h localhost -U pgbouncer -qtAX -F "," -c "SHOW SERVERS"' ON MASTER
FORMAT 'CSV' ( delimiter ',' null '' escape '"' quote '"' )
ENCODING 'UTF8';
--WEB таблица возвращает SHOW CLIENTS из pgbouncer
CREATE EXTERNAL WEB TABLE xxx.pgbouncer_clients (
type char(1),
user_name char(40),
database_name char(20),
state char(15),
addr char(15),
port int,
local_addr char(15),
local_port int,
connect_time char(40),
request_time char(40),
wait int,
wait_us int,
close_needed int,
ptr char(15),
link char(15),
remote_pid int,
tls int
)
EXECUTE 'psql -p 6432 -h localhost -U pgbouncer -qtAX -F "," -c "SHOW CLIENTS"' ON MASTER
FORMAT 'CSV' ( delimiter ',' null '' escape '"' quote '"' )
ENCODING 'UTF8';
-- Представление возвращает соответствие PID и IP. Если соединение идет напрямую, то IP берется из pg_stat_activity, если через pgbouncer, то IP берется из pgbouncer.
create or replace view xxx.real_ip
as
select
procpid,
case when cl.addr is not null then 'pgbouncer - '||cl.addr
else regexp_replace(client_addr::text,'/32','')
end client_addr
from pg_stat_activity() sa
left join xxx.pgbouncer_servers sr on sr.remote_pid=sa.procpid
left join xxx.pgbouncer_clients cl on cl.ptr=sr.link
На все три объекта выдаются права на SELECT для технической учётной записи Демократизатора.
Реакция
Если значение превышает порог - запускается сценарий, который приводит к реакции в зависимости от вида таски:
notify - превышение лимита (таска-родитель notify_and_kill). Простое уведомление. Пример темы письма - ❗️ Предупреждение 1/3.
notify_only - превышение лимита (таска-родитель notify_only). Простое уведомление. Пример темы письма - ⚠️ Превышение лимита.
whitelist_notify_only - уведомление о превышении пользователя, находящегося в WhiteList. (таска-родитель ТОЛЬКО notify_and_kill). Пример темы письма - ⚠️ Превышение лимита. WhiteList.
explain_analyze_notify_only - уведомление о превышении лимита запросом, в котором содержится EXPLAIN ANALYZE (таска-родитель ТОЛЬКО notify and kill, отсылается темплейт notify_only). Пример темы письма - ⚠️ Explain Analyze/Превышение лимита.
kill - убийство (таска-родитель notify_and_kill). Пример темы письма - ? Принудительное завершение.
emergency - убийство (таска-родитель notify_and_kill). Убийство в случае превышения аварийного лимита. Пример темы письма - ? Аварийное завершение.
Отстрел запроса
Для гарантированного завершения проблемных процессов мы используем pg_terminate_backend, которая посылает процессу сигнал SIGTERM. Мы выбрали эту функцию вместо pg_cancel_backend (которая посылает SIGINT), потому что SIGTERM приводит к немедленному разрыву соединения и откату транзакции, даже если запрос завис. Обратите внимание: SIGKILL (-9) мы не используем - он убивает процесс на уровне ОС, что может привести к нестабильности кластера. PostgreSQL сам корректно обрабатывает SIGTERM от pg_terminate_backend.
Сам вызов производится через другую функцию для безопасности и логирования.
Для ограничения злостных нарушителей мы выдаём им nologin.
Всё реализовано через функции чтобы дополнительно логировать информацию о действиях в БД.
Визуализация и отчёты
Всё отображается в Web UI
Детальные отчёты по нарушениям
Поддержка выгрузки в Excel
Интеграция с RT DataVision
Утренние summary на почту по каждому кластеру
API для внешних систем
Нарушения и детали нарушений


Отчёт в Excel за выбранные даты

Формируется отчёт по usename, date, procpid, notification vaule, notification type, task name, notification id, который можно использовать в отчётах.
RT.Datavision
С помощью коллег визуализировали наши данные, это позволяет удобно мониторить список злостных нарушителей, получать статистику по подразделениям и.т.д.
Статистика по типу уведомления

Статистика по плановым отстрелам и отстрелам в результате превышения аварийного лимита

Распределение нарушений в разрезе отделов/департаментов

Сравнительный анализ PROD и DEV

Разборы полётов

В соответствии с параметрами таски пользователю направлено 2 предупреждения и затем на 3 раз был произведён отстрел.
Давайте разберёмся, почему так произошло
SELECT t1.appl_key, t1.appl_emp_src_lgn
FROM table_a t1
LEFT JOIN table_b t2
ON t1.appl_key = t2.appl_key
AND t2.deleted_ind = 0
AND t2.active_ind = 'Y'
AND t2.exp_dttm::DATE = TO_DATE('29991231', 'YYYYMMDD')
LEFT JOIN table_c t3
ON t2.emp_src_key = t3.emp_src_key
AND t3.deleted_ind = 0
AND t3.active_ind = 'Y'
JOIN table_d t4
ON t3.appl_emp_src_key = t4.emp_src_key
AND to_char(t1.appl_opn_dttm, 'YYYY-MM') = to_char(t4.appl_emp_src_prd, 'YYYY-MM')
AND t4.deleted_ind = 0
WHERE
1 = 1
AND t1.deleted_ind = 0
AND t1.exp_dttm::DATE = TO_DATE('29991231', 'YYYYMMDD')
AND t1.APPL_OPN_DTTM::DATE BETWEEN TO_DATE('20241201', 'YYYYMMDD') AND TO_DATE('20250331', 'YYYYMMDD') + INTERVAL '1 day -1 second'

Неоптимальный запрос привёл к нескольким тысячам ГБ SPILL.
PlanChecker показал 5 перекосов. Это означает, что в результате работы запроса данные были распределены по сегментам неравномерно, что привело к росту Spill файлов.
Данный запрос неоптимален:
Множество условий в JOIN'ах. Необходима предварительная выборка с этими условиями, затем соединяться по какому-то одному ключу.
Неэффективные преобразования дат в запросе (приведение к текстовому виду)
Реакция “Демократизатора”:
Первое письмо - предупреждение
Второе письмо - предупреждение
Третье письмо - уведомление о завершении процесса.
Kill запроса. Логирование. Сообщение в мессенджере.
Разбор полётов помог сотруднику сделать анализ и исправление ошибок, а администраторам не заниматься поиском проблем с производительностью:
AND to_char(t1.appl_opn_dttm, 'YYYY-MM') = to_char(t4.appl_emp_src_prd, 'YYYY-MM')
заменено на
AND date_trunc('month', t1.appl_opn_dttm) = date_trunc('month', t4.appl_emp_src_prd)
Сделана предварительная выборка с условиями из JOIN'ов:
WITH filtered_t2 AS (
SELECT *
FROM table_b
WHERE deleted_ind = 0
AND active_ind = 'Y'
AND exp_dttm::DATE = TO_DATE('29991231', 'YYYYMMDD')
),
filtered_t3 AS (
SELECT *
FROM table_c
WHERE deleted_ind = 0
AND active_ind = 'Y'
),
filtered_t4 AS (
SELECT *
FROM table_d
WHERE deleted_ind = 0
)
SELECT t1.appl_key, t1.appl_emp_src_lgn
FROM table_a t1
LEFT JOIN filtered_t2 ft2
ON t1.appl_key = ft2.appl_key
LEFT JOIN filtered_t3 ft3
ON ft2.emp_src_key = ft3.emp_src_key
JOIN filtered_t4 ft4
ON ft3.appl_emp_src_key = ft4.emp_src_key
AND date_trunc('month', t1.appl_opn_dttm) = date_trunc('month', ft4.appl_emp_src_prd)
WHERE
t1.deleted_ind = 0
AND t1.exp_dttm::DATE = TO_DATE('29991231', 'YYYYMMDD')
AND t1.APPL_OPN_DTTM::DATE BETWEEN TO_DATE('20241201', 'YYYYMMDD') AND TO_DATE('20250331', 'YYYYMMDD') + INTERVAL '1 day -1 second';
Работа с пользователями
Пример 1: Реакция на блокировку
Письмо

Ответ

Вывод: Андрей и Павел узнали о проблеме и решили её.
Пример 2: SPILL от пользователя через ETL
УЗ по определённым признакам находится WhiteList (отстрел запроса не применяется), но значения фиксируются и уведомления с информацией пересылаются.

Письмо пользователю:

Ответ пользователя:

Коллеге была предоставлена инфомрация по отмене и оптимизации запроса.
Пример 3: Результаты проработки инцидента на проде:

Коллеги смогли оптимизировать запрос и сняли метрики, подтверждающие это.
Как устроены проверки

Проверки - это просто SELECT запросы, которые возвращают определённые значения.
Можно мониторить достаточно широкий спектр острых моментов:
SPILL-файлы
длительность запросов
повторные сессии
IDLE-процессы
шаблоны SQL (anti-pattern)
нарушения resource group
и тому подобное
Производительность и тюнинг
Запросы в БД
Greenplum - это не самая лёгкая СУБД. Особенно если у вас 72 сегмент-хостов, на каждом хосте 6 primary и 6 mirror сегментов => 432 сегмента и примерно 1730 личных и технических УЗ в одном только проде.
Мы подключаем Демократизатор через PgBouncer в качестве исключения в режиме pool_mode = session - сознательно, чтобы избежать transaction-пулинга, где PgBouncer сам оборачивает каждый запрос в транзакцию. Это первый и необходимый шаг.
Но даже в session-режиме одного этого недостаточно.
Дело в том, что psycopg2 по умолчанию работает в режиме autocommit=False, и даже простой SELECT к pg_stat_activity превращается в:
BEGIN;
SELECT ...;
COMMIT;
А в Greenplum открытие транзакции - это:
чтение системных каталогов на всех сегментах
создание согласованного snapshot’а
удержание shared-блокировок.
Если pg_catalog не обслужен (не проанализирован, фрагментирован), такие операции могут занимать 200–250 секунд. А у нас запросы к pg_stat_activity идут постоянно - ждать по несколько минут неприемлемо.
А нам просто необходимо делать селекты, и даже такие настройки PgBouncer не помогут - всё равно от этого никуда не денемся.... если не настроить клиент правильно!
Поэксперементировав, мы нашли решение, которое работает на Python 3.8/3.12 + psycopg2 2.9.9. Опыт прямиком из production:
conn.set_session(autocommit=True, readonly=True)
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
Хотя set_session(autocommit=True) технически достаточно, в psycopg2 2.9.9 почему-то всё заработало правильно только при явном указании уровня изоляции.
Ссылка на Issue https://github.com/psycopg/psycopg2/issues/1742
А вот такое работает на Python 2.7.5 + psycopg2 2.5.1 (поставлена не через pip)
https://github.com/psycopg/psycopg2/issues/941
Результат:
Запросы к системным таблицам перестали оборачиваться в транзакции
Время выполнения упало с порой 200+ секунд до десятков.
Нагрузка на
pg_catalog
и shared memory значительно снизилась.
Было:


Стало:

Обработка результатов
Любая таска обёрнута в механизм обработки исключений. Если проверочный запрос падает с ошибкой или не возвращает ответ за установленный таймаут, это не приводит к падению всего «Демократизатора». В первом случае задача завершается с ошибкой, а информация об этом дублируется в наш чат и иногда это первый признак проблемы с каким-либо сегментом, о которой который мы иногда узнаём даже раньше, чем срабатывает Zabbix. А во втором - запрос висит в пределах установленного connection limit, следующая итерация проверки в таком случае не запускается до любого завершения предыдущей таски.


Дополнительные фичи и скриншоты
Whitelist (УЗ, нарушения которых обрабатываются несколько другим способом)
Cooldown на уведомления (чтобы не слать 500 уведомлений о превышении лимита каким-нибудь ETL из WhiteList)
Исключения для EXPLAIN/ANALYZE
Сервисный режим (например, во время исполнения бэкапа) по API и вручную из интерфейса
Вычисление реального виновника нагрузки с его начальником и подразделением из нарушения технической учётной записи
Доска позора по пользователям и их подразделениям
Бан пользователей (nologin) в случае неоднократных отстрелов (параметры также задаются)




Результаты
Статистика приведена в разделе "интеграция с RT.DV"
Напомню основные цифры:
10118 нарушений
13170 уведомлений
219 отстрелов
240 аварийных отстрелов
2 выдачи nologin
Отзывы коллег:
Владелец контура Greenplum DEV:
Стало жить лучше.
Нашли сотрудников, которые не умеют писать код, это позволило провести точечные беседы и научить людей стандартам
Сформировалась база анти-паттернов на примере п.1 , которую также удалось внедрить в алгоритмы Демократизатора.
Сформировали BI отчеты на данных по нарушениям, смотрим на тренды (провели обучение, рассказали о статьях и бест практикс), смотрим за динамикой нарушителей. Также помогает делать выводы по работе консалтеров, которые оказывают нам услуги – т.е. насколько коллеги пишут хороший код и следуют нашим правилам или нет.
Прозрачное понимание возможных причин аварий на кластере.
Другой инженер из этой команды:
Демократизатор - это хороший, а главное эффективный инструмент, который помог выявить нужных пользователей, для последующего их обучения при работе с данными.
Что дальше?
Мы не планируем останавливаться на достигнутом. Бэклог развития сформирован по обратной связи от пользователей, администраторов и ответственных за эксплуатацию и внедрение. Среди его пунктов:
Создание новых правил на основе накопленной статистики и новых рабочих ситуаций для поиска аномалий.
Интеграция с ClickHouse для сбора и анализа логов Greenplum, что откроет возможности для более сложных и глубоких проверок.
Более тесная интеграция с системой синхронизации учётных записей и ролей, что отразится на точности определения конечного адресата нарушения.
Расширенная работа с мониторином сегмент-хостов для комплексного слежения за всем кластером.
Расширение гибкости Web UI и API для интеграций с другими системами нашей платформы.
Автоматический разбор запроса с предложениями по оптимизации прямо в письме при помощи нашего продукта Нейрошлюз
Кастомный парсер запросов на предмет выявления запрещённых конструкций.

Вывод
MVP Демократизатора был изначально реализован как скрипт для контроля SPILL-файлов, который запускался в кроне и имел конфиги, которые надо было редактировать через vim на машине.
А вырос в полноценную систему с кучей полезных интеграций, которая реально снижает нагрузку на Greenplum, формирует базу антипаттернов для обучения наших коллег.
Пока что в большей степени наказываем за плохой код. Наша следующая большая задача - сместить фокус на предотвращение ошибок, плотнее интегрируя результаты в процессы обучения и разбора полётов.
Мы автоматизировали рутину, убрали человеческий фактор, упростили себе жизнь, наладили процесс сбора статистики и дали пользователям обратную связь.
И при желании к проверкам в дальнейшем можно будет подключить любой вид СУБД
Благодарность
Хотел бы выразить благодарность коллегам за поддержку:
Кошелькову Николаю
Ружейникову Михаилу
Горбачеву Денису
Черемных Игорю
И всем остальным, принимающим участие в развитии птички
mSnus
Орёл на логотипе должен держать в когтях резиновую полицейскую дубинку