Демократизатор
Демократизатор

Представьте: Один неоптимизированный запрос от неопытного коллеги - и вот уже 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-мастера отказ мастера приводит к простою всей системы.

Принцип работы

  1. Данные распределяются между сегментами по выбранному ключу

  2. Каждый сегмент обрабатывает свою часть данных параллельно

  3. Результаты агрегируются на мастер-хосте

Архитектура
Архитектура

Выполнение запроса

  1. Клиент шлёт запрос на мастер-хост.

  2. Master разбирает SQL, строит план, отправляет задачи на сегмент-хосты.

  3. Сегменты работают параллельно:

    • Сканируют свои куски данных.

    • Обмениваются данными через Interconnect (если нужно).

  4. Master собирает результаты, применяет финальные операции (сортировка, лимиты), возвращает клиенту.

SPILL как угроза

Если вы работаете с Greenplum, то знаете, что:

  • Один тяжелый запрос может исчерпать общий пул памяти (MEMORY_SHARED_QUOTA) ресурсной группы. Это не оставит остатков для других запросов - они просто встанут в очередь и не будут выполняться, пока память не освободится, что может парализовать все процессы в этой ресурсной группе.

  • SPILL-файлы, представляющие из себя миллионы мелких файлов, могут разрастись до десятков ТБ. Это приводит к исчерпанию свободного места на файловой системе, что вызывает аварийную остановку работы сегмента и интенсивный износ дисков.

  • pg_stat_activity - простыня ужасов, которую неудобно мониторить вручную

  • Пользователь просто нажал на кнопочку в DBeaver, а у тебя падают ETLсегменты. И никакой обратной связи.

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

Основные причины возникновения spill-файлов при исполнении запросов:

  • обработка большого объема данных без фильтров (особенно эффективны фильтры по полю партиционирования)

  • создание хэш-таблицы на основе таблицы большого объема

  • устранение дублей (DISTINCT) на большом количестве полей

  • сортировка в SQL-запросе (ORDER BY) на большом количестве полей

  • группировка в SQL-запросе (GROUP BY)

  • соединение таблиц с разными ключами распределения

  • соединение таблиц по полям, не являющимися ключами распределения

Оптимизация:

  1. Остановить запущенный запрос, текст которого приложен в письме, если он ещё исполняется на кластере.

  2. Проверить его текст на соответствие основным причинам создания spill-файлов (см. выше).

  3. Проверить ключи распределения таблиц, участвующих в операциях соединения запроса. Вместо $schemaname.tablename в текст запроса необходимо подставить названия интересующих таблиц в указанном формате.
    ```

    SELECT attname FROM pg_attribute WHERE attrelid = (SELECT '$schemaname.tablename'::regclass::oid) AND attnum = '1'
  4. Проверить равномерность распределения строк в таблицах по сегментам 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;
  5. Собрать план запроса с помощью 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'ах. Необходима предварительная выборка с этими условиями, затем соединяться по какому-то одному ключу.

  • Неэффективные преобразования дат в запросе (приведение к текстовому виду)

Реакция “Демократизатора”:

  1. Первое письмо - предупреждение

  2. Второе письмо - предупреждение

  3. Третье письмо - уведомление о завершении процесса.

  4. 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, следующая итерация проверки в таком случае не запускается до любого завершения предыдущей таски. 

Алерт 1
Алерт 1
Алерт 2
Алерт 2

Дополнительные фичи и скриншоты

  • Whitelist (УЗ, нарушения которых обрабатываются несколько другим способом)

  • Cooldown на уведомления (чтобы не слать 500 уведомлений о превышении лимита каким-нибудь ETL из WhiteList)

  • Исключения для EXPLAIN/ANALYZE

  • Сервисный режим (например, во время исполнения бэкапа) по API и вручную из интерфейса

  • Вычисление реального виновника  нагрузки с его начальником и подразделением из нарушения технической учётной записи

  • Доска позора по пользователям и их подразделениям

  • Бан пользователей (nologin) в случае неоднократных отстрелов (параметры также задаются)

Алерт об отстреле
Алерт об отстреле
Уведомление о включении сервисного режима. Инициаторы - коллега (вручную) и  AirFlow (API)
Уведомление о включении сервисного режима. Инициаторы - коллега (вручную) и AirFlow (API)
Кнопка включения сервисного режима
Кнопка включения сервисного режима
Ежедневный отчёт по кластеру
Ежедневный отчёт по кластеру

Результаты

Статистика приведена в разделе "интеграция с RT.DV"

Напомню основные цифры: 

  • 10118 нарушений

  • 13170 уведомлений

  • 219 отстрелов

  • 240 аварийных отстрелов

  • 2 выдачи nologin

Отзывы коллег:

Владелец контура Greenplum DEV:

Стало жить лучше.

Нашли сотрудников, которые не умеют писать код, это позволило провести точечные беседы и научить людей стандартам

Сформировалась база анти-паттернов на примере п.1 , которую также удалось внедрить в алгоритмы Демократизатора.

Сформировали BI отчеты на данных по нарушениям, смотрим на тренды (провели обучение, рассказали о статьях и бест практикс), смотрим за динамикой нарушителей. Также помогает делать выводы по работе консалтеров, которые оказывают нам услуги – т.е. насколько коллеги пишут хороший код и следуют нашим правилам или нет.

Прозрачное понимание возможных причин аварий на кластере.

Другой инженер из этой команды:

Демократизатор - это хороший, а главное эффективный инструмент, который помог выявить нужных пользователей, для последующего их обучения при работе с данными.

Что дальше?

Мы не планируем останавливаться на достигнутом. Бэклог развития сформирован по обратной связи от пользователей, администраторов и ответственных за эксплуатацию и внедрение. Среди его пунктов:

  • Создание новых правил на основе накопленной статистики и новых рабочих ситуаций для поиска аномалий.

  • Интеграция с ClickHouse для сбора и анализа логов Greenplum, что откроет возможности для более сложных и глубоких проверок.

  • Более тесная интеграция с системой синхронизации учётных записей и ролей, что отразится на точности определения конечного адресата нарушения.  

  • Расширенная работа с мониторином сегмент-хостов для комплексного слежения за всем кластером.

  • Расширение гибкости Web UI и API для интеграций с другими системами нашей платформы.

  • Автоматический разбор запроса с предложениями по оптимизации прямо в письме при помощи нашего продукта Нейрошлюз

  • Кастомный парсер запросов на предмет выявления запрещённых конструкций.

Вывод

MVP Демократизатора был изначально реализован как скрипт для контроля SPILL-файлов, который запускался в кроне и имел конфиги, которые надо было редактировать через vim на машине.

А вырос в полноценную систему с кучей полезных интеграций, которая реально снижает нагрузку на Greenplum, формирует базу антипаттернов для обучения наших коллег.

Пока что в большей степени наказываем за плохой код. Наша следующая большая задача - сместить фокус на предотвращение ошибок, плотнее интегрируя результаты в процессы обучения и разбора полётов.

Мы автоматизировали рутину, убрали человеческий фактор, упростили себе жизнь, наладили процесс сбора статистики и дали пользователям обратную связь. 

И при желании к проверкам в дальнейшем можно будет подключить любой вид СУБД

Благодарность

Хотел бы выразить благодарность коллегам за поддержку:

  • Кошелькову Николаю

  • Ружейникову Михаилу 

  • Горбачеву Денису

  • Черемных Игорю

  • И всем остальным, принимающим участие в развитии птички

Комментарии (1)


  1. mSnus
    07.10.2025 11:31

    Орёл на логотипе должен держать в когтях резиновую полицейскую дубинку