F.51. pg_trace#

F.51. pg_trace

F.51. pg_trace #

Отслеживание запросов в реальном времени для Tantor SE-1C

F.51.1. Обзор #

pg_trace - это библиотека трассировки, которая позволяет отслеживать выполняемые запросы в реальном времени, включая их ресурсы, время выполнения и план запроса.

Особенности

  • Фильтрация запросов с использованием пользовательских условий

  • Встроенный тайм-аут трассировки, после которого она будет отключена

  • Поддержка нескольких одновременных клиентов, прослушивающих трассировки

  • Несколько собранных статистик о запросе: ресурсы, время, план запроса

F.51.2. Начало работы #

Добавьте расширение в shared_preload_libraries в postgresql.conf:

shared_preload_libraries = 'pg_trace'

Создайте расширение и начните трассировку

CREATE EXTENSION pg_trace;
SELECT pg_trace_start();

Подключиться к сокету. Т.е. используя nc:

nc -U /tmp/pg_trace.sock

Выполните несколько запросов:

SELECT 1;

Вы увидите вывод, подобный этому:

{"traceid":0,"queryid":6865378226349601843,"dbid":5,"userid":10,"tuplescount":1,"start_time":1733221222489,"end_time":1733221222489,"total_time":0.00739,"startup_time":0,"sys_time":0,"user_time":0,"rows":1,"shared_blks_hit":0,"shared_blks_read":0,"shared_blks_written":0,"shared_blks_dirtied":0,"local_blks_hit":0,"local_blks_read":0,"local_blks_written":0,"local_blks_dirtied":0,"shared_blk_read_time":0,"shared_blk_write_time":0,"local_blk_read_time":0,"local_blk_write_time":0,"temp_blk_read_time":0,"temp_blk_write_time":0,"wal_records":0,"wal_fpi":0,"wal_bytes":0,"query":"select 1;"}

F.51.3. Функциональность #

F.51.3.1. Управление трассировкой #

Чтобы начать трассировку, используйте функцию pg_trace_start().

CREATE FUNCTION pg_trace_start(
    backend_pid int DEFAULT NULL,
    user_id oid DEFAULT NULL,
    database_id oid DEFAULT NULL,
    duration interval DEFAULT NULL,
    query_like text DEFAULT NULL,
    plan boolean DEFAULT FALSE,
    timeout interval DEFAULT NULL
)
RETURNS boolean;

-- example
SELECT pg_trace_start(
    user_id := 123,
    query_like := 'SELECT *%',
    plan := TRUE,
    timeout := interval '1 hour'
);

Эта функция начинает новую сессию трассировки. Она принимает аргументы:

  • Фильтрация атрибутов, если NULL фильтр не применяется:

    • backend_pid - запрос выполнен на этом сервере

    • user_id - запрос выполнен этим эффективным пользователем

    • database_id - запрос выполнен в этой базе данных

    • duration - запрос выполнялся дольше, чем указано

    • query_like - запрос должен соответствовать шаблону (используя LIKE)

  • plan - включить план запроса в вывод трассировки

  • timeout - переопределить тайм-аут трассировки по умолчанию с указанным интервалом

Возвращает boolean - трассировка была включена, когда выполнялась функция.

Чтобы остановить трассировку сессии, выполните pg_trace_stop():

CREATE FUNCTION pg_trace_stop()
RETURNS boolean;

SELECT pg_trace_stop();

Эта функция принудительно отключает трассировку. Возвращает boolean - трассировка была включена при запуске функции.

Чтобы проверить, включена ли трассировка, используйте функцию pg_trace_enabled:

CREATE FUNCTION pg_trace_enabled()
RETURN boolean;

SELECT pg_trace_enabled();

F.51.3.2. Наблюдаемость #

Для отправки трассировок расширение использует фоновый рабочий процесс, который здесь называется сервером. Сервер собирает некоторые метрики и предоставляет их с помощью SQL функций:

  • Общее количество клиентов, подключенных к серверу

    CREATE FUNCTION @extschema@.pg_trace_metrics_clients_accepted()
    RETURNS bigint;
    
  • Общее количество клиентов, отключенных от сервера

    CREATE FUNCTION @extschema@.pg_trace_metrics_clients_disconnected()
    RETURNS bigint;
    
  • Общее количество раз, когда WaitEventSet был воссоздан

    CREATE FUNCTION @extschema@.pg_trace_metrics_wes_recreations()
    RETURNS bigint;
    
  • Общее количество собранных трассировок

    CREATE FUNCTION @extschema@.pg_trace_metrics_traces_collected()
    RETURNS bigint;
    
  • Общее количество трассировок было отклонено из-за нехватки свободного места

    CREATE FUNCTION @extschema@.pg_trace_metrics_traces_declined()
    RETURNS bigint;
    
  • Общее количество байт, отправленных всем клиентам через сокет

    CREATE FUNCTION @extschema@.pg_trace_metrics_send_bytes()
    RETURNS bigint;
    
  • Общее количество случаев, когда отправка данных через сокет вернула ошибку

    CREATE FUNCTION @extschema@.pg_trace_metrics_send_errors()
    RETURNS bigint;
    

Описание pg_trace_metrics_wes_recreations см. ниже

F.51.4. Клиент-серверный протокол #

Взаимодействие между сервером (фоновым рабочим процессом) и клиентом (приложением, читающим трассировки) настроено в клиент/серверной манере.

Клиент подключается к серверному UNIX-сокету (он должен знать его расположение). После подключения клиент начнет получать сообщения. Каждое сообщение представляет собой один trace и содержит 2 поля:

Длина Трассировка
4 байта N байт

Length - длина поля Trace. Это хранится как сериализованное целое число в формате little-endian.

Trace - ASCII кодированное JSON представление объекта Trace.

Схема JSON сообщения:

Ключ Тип Описание
queryid number ID запроса
dbid number OID базы данных
userid number OID пользователя
tuplescount number Общее количество кортежей, обработанных запросом
start_time number UNIX метка времени начала выполнения запроса
end_time number UNIX метка времени окончания выполнения запроса
total_time number количество времени, в течение которого выполнялся запрос, в секундах
startup_time number время запуска запроса
startup_time number общее время выполнения запроса
sys_time number количество времени, которое запрос провел в пространстве ядра
user_time number время, проведенное запросом в пользовательском пространстве
rows number Общее количество строк, извлеченных или затронутых запросом
shared_blks_hit number Общее количество попаданий в кэш общих блоков для данного выражения
shared_blks_read number Общее количество общих блоков, прочитанных оператором
shared_blks_written number Общее количество общих блоков, записанных оператором
shared_blks_dirtied number Общее количество общих блоков, измененных оператором
local_blks_hit number Общее количество попаданий в локальный кэш блоков по оператору
local_blks_read number Общее количество локальных блоков, прочитанных оператором
local_blks_written number Общее количество локальных блоков, записанных оператором
local_blks_dirtied number Общее количество локальных блоков, измененных оператором
local_blks_dirtied number Общее количество локальных блоков, измененных оператором
shared_blk_read_time number Общее время, затраченное оператором на чтение общих блоков данных файла, в миллисекундах
shared_blk_write_time число Общее время, затраченное оператором на запись общих блоков данных файла, в миллисекундах
local_blk_read_time number Общее время, затраченное оператором на чтение локальных блоков данных файла, в миллисекундах
local_blk_write_time number Общее время, затраченное оператором на запись локальных блоков данных в файл, в миллисекундах
temp_blk_read_time number Общее время, затраченное оператором на чтение блоков временного файла, в миллисекундах
temp_blk_write_time number Общее время, затраченное оператором на запись блоков временного файла, в миллисекундах
wal_records number Общее количество записей WAL, созданных оператором
wal_fpi number Общее количество полных изображений страниц WAL, сгенерированных оператором
wal_bytes number Общее количество WAL, сгенерированного оператором, в байтах
query string Строка запроса
plan plan object План запроса в формате EXPLAIN JSON
jit_functions number Количество сгенерированных JIT функций
jit_generation_time number Накопленное время на генерацию кода с использованием JIT
jit_optimization_time number Накопленное время для генерации кода с использованием JIT
jit_inlining_time number Накопленное время, использованное для инлайнинга JIT-кода
jit_emission_time number Накопленное время для генерации JIT-кода

Многие поля содержат информацию о времени. В этой схеме вся информация о времени предоставляется в виде UNIX временной метки.

План предоставляется в виде JSON-объекта, сгенерированного с помощью EXPLAIN.

F.51.5. Конфигурация #

pg_trace.max_clients - максимальное количество клиентов, которые могут обрабатываться одновременно.

pg_trace.trace_plan_mem - объем памяти, используемой для хранения плана запроса в текстовом представлении. Указывается как объем памяти с суффиксом размера. Например, 8 kB. По умолчанию установлено на 16 kB.

Примечание

Этот параметр повлияет на размер результата одной записи Trace. Таким образом, изменение этого параметра повлияет на другие.

pg_trace.traces_mem - объем памяти для выделения под трассировки в серверном фоновом процессе. Указывается как объем памяти с суффиксом размера. Например, 128 MB. По умолчанию устанавливается объем памяти для хранения ровно 5000 трассировок.

pg_trace.shm_queue_mem - объем памяти, выделяемой в общей памяти для буфера трассировок. Указывается как объем памяти с суффиксом размера. Например, 32 MB. По умолчанию установлено на объем памяти, достаточный для хранения ровно 32 трассировок.

Примечание

Если вы укажете объем памяти меньше, чем размер 1 Trace, вы получите ошибку. traces_mem и shm_queue_mem зависят от настройки trace_plan_mem.

pg_trace.unix_socket_path - путь к unix сокету для сервера. Если сокет уже существует, он будет удален и будет создан новый сокет. По умолчанию, /tmp/pg_trace.sock.

pg_trace.trace_timeout - тайм-аут по умолчанию для сеанса трассировки, после которого сеанс трассировки будет автоматически отключен. Это значение используется, если аргумент timeout в pg_trace_start не установлен. Указывается как время с суффиксом времени. Например, 1h. По умолчанию, 10 минут.

pg_trace.update_delay - время ожидания перед началом обработки трассировки из очереди общей памяти фоновым рабочим процессом. Это время может дать бэкэндам шанс записать больше трассировок в очередь, прежде чем фоновый рабочий процесс возьмет эксклюзивную блокировку. По умолчанию, 0 - без задержки.

pg_trace.plan_format - формат плана, который передается, когда 'plan' был запрошен при запуске трассировки сессии. Допустимые значения: text, json, yaml, xml. Если запрашивается json, будет использован объект плана в формате JSON, в противном случае (для других форматов) он будет представлен как строка плана (возможно, экранированная для JSON). По умолчанию используется формат text.

Параметры trace_timeout, update_delay и plan_format могут быть изменены с помощью обновления файла конфигурации с отправкой SIGHUP. Все остальные не могут быть изменены после запуска postgres - необходимо перезапустить сервер.

F.51.6. Как это работает #

Терминология

  • Сервер - фоновый рабочий процесс

  • Клиент - пользователь, который подключается к сокету и прослушивает трассировки

  • Backend - сервер, который выполняет запросы и передает трассировки фоновому рабочему процессу

Общая архитектура и принципы работы:

        ----------------- Trace format ----------------

                    +---------------------+
                    |   Instrumentation   |
                    |       Timings       |
                    |      Resources      |
                    +---------------------+
                    |      Query plan     |  `pg_trace.plan_max`
                    +---------------------+  



        ---------------- Architecture ----------------
                        
    +---------+           +---------+           +---------+
    | backend |           | backend |           | backend |
    +---------+           +---------+           +---------+
         \                     |                     /
          +--------------------+--------------------+
                               |
                           filtering
                               |
                               |
                               V                         `pg_trace.shm_queue_max`
                    +---------------------+     buffer         +---+---+---+          
                    | shared memory queue |--------------------| X |   |   |          
                    +---------------------+                    +---+---+---+
                               |
                               |
                               |
                               V                          `pg_trace.traces_mem`
                    +---------------------+  ring buffer  +---+---+---+---+---+
                    |  background worker  |---------------| X | X | X | X |   |
                    +---------------------+               +---+---+---+---+---+
                               |
                               |
                               |
            +------------------+------------------+
           /                   |                   \
          |                    |                    |
          V                    V                    V
          O                    O                    O
         /|\                  /|\                  /|\
         / \                  / \                  / \

                     `pg_trace.max_clients`      

Когда запрос выполняется и проходит все переданные фильтры, он отправляется в фоновый рабочий процесс, используя очередь общей памяти. Он содержит буфер размером pg_trace.shm_queue_max.

После этого фоновый рабочий копирует все трассировки из буфера очереди shm в свой собственный буфер. Трассировки хранятся в кольцевом буфере. Он выделяется при запуске сервера с размером pg_trace.traces_mem. Текущая реализация позволяет всем операциям (освобождение, поиск, вставка и т.д.) иметь сложность O(1), так что не стесняйтесь увеличивать этот параметр.

Когда добавляются новые трассировки, они отправляются клиентам. Максимальное количество клиентов, которые могут обрабатываться одновременно, устанавливается с помощью параметра pg_trace.max_clients.

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

F.51.7. Известные проблемы #

F.51.7.1. Воссоздание WaitEventSet #

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

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

Чтобы справиться с этой ситуацией, этот WaitEventSet пересоздается. Эта операция не бесплатна, потому что нам нужно закрыть старый файловый дескриптор, создать новый и пройти через всех клиентов и отфильтровать активных.

Количество раз, когда нам нужно воссоздать WaitEventSet, можно уменьшить, увеличив настройку pg_trace.max_clients.

Метрика функция pg_trace_metrics_wes_recreations возвращает количество раз, когда этот WaitEventSet был воссоздан.

F.51.7.2. Состояние гонки #

Функции pg_trace_start и pg_trace_stop могут использоваться одновременно.

Состояние обновляется атомарно (в блокировке), но эти функции могут работать одновременно. Пользователь должен сам отслеживать их использование.

F.51.7.3. Привилегии #

По умолчанию, pg_trace_start и pg_trace_stop создаются без какой-либо проверки привилегий. Разрешение использования этих функций всеми может привести к утечкам безопасности и ухудшению производительности.

Чтобы исправить это, измените привилегии доступа:

REVOKE EXECUTE ON FUNCTION pg_trace_start FROM public;
REVOKE EXECUTE ON FUNCTION pg_trace_stop FROM public;

GRANT EXECUTE ON FUNCTION pg_trace_start TO superuser;
GRANT EXECUTE ON FUNCTION pg_trace_stop TO superuser;

F.51.7.4. Память плана #

Объем памяти для хранения плана запроса устанавливается с помощью pg_trace.trace_plan_mem и используется для хранения сериализованного представления плана запроса для передачи на сервер. Он фиксирован, поэтому иногда это может быть меньше размера результата сериализованного плана. В таких случаях план не устанавливается в объекте JSON трассировки.

Т.е. pg_trace.trace_plan_mem установлено в 1 кБ, но общий сериализованный размер плана запроса составляет 1,5 кБ. В этом случае план не устанавливается в выходном JSON.