F.3. auto_dump — расширение с хуком ProcessInterrupts#
F.3. auto_dump — расширение с хуком ProcessInterrupts #
Версия: 1.1
F.3.1. Обзор #
auto_dump помогает вам
создать самодостаточную
репродукцию проблемных запросов, которые выполняются
в рабочей среде (например, из 1С).
Когда условие триггера выполняется, расширение записывает:
DDL для таблиц, используемых в запросе (временных и/или постоянных — настраиваемо)
Необязательные дампы данных (операторы INSERT)
Необязательные команды создания индексов
Необязательный текст запроса
Необязательный план(ы) выполнения:
EXPLAINиEXPLAIN (ANALYZE, BUFFERS, WAL, TIMING, SUMMARY)
Дампы записываются в каталог, создаваемый для каждого запроса и называемый:
<PID>-YYYY_MM_DD_hh_mm_ss-<counter>/
Дампы содержат (в зависимости от конфигурации):
query.sql- исходный текст запросаcreate_persistent.sql- DDL для постоянных таблиц и индексов (если dump_persistent_tables = on)create_temporary.sql- DDL для временных таблиц и индексов (если dump_temporary_tables = on)insert_persistent.sql- вставка данных для постоянных таблиц (если dump_data = on и dump_persistent_tables = on)insert_temporary.sql- вставка данных для временных таблиц (если dump_data = on и dump_temporary_tables = on)table-<table>.txt- файл(ы) данных таблицы дляauto_dump.data_format = 'copy-file'(один файл на каждую выгруженную таблицу)plan_explain.txt- вывод EXPLAIN (если dump_plan = on)plan_analyze.txt- вывод EXPLAIN (ANALYZE, BUFFERS, WAL, TIMING, SUMMARY) (если dump_plan = on)readme.txt- инструкции по использованию для воспроизведения дампа
Примечание
Планирование дампа и триггер “dump_on_cancel” требуют небольшего патча ядра, который добавляет ProcessInterrupts_hook. Для работы этих функций необходимо применить этот патч и собрать Tantor SE-1C с ним.
F.3.2. Требования #
Tantor SE-1C 18.1. PostgreSQL 18.1.
C-инструментарий для сборки расширений Tantor SE-1C.
Основной патч (добавляет
ProcessInterrupts_hook), если хотите:dump-on-cancel (
auto_dump.dump_on_cancel)сбор плана с инструментированием (
auto_dump.dump_plan = onилиauto_dump.dump_on_bad_plan = on)
F.3.3. Включение расширения #
Добавьте в
postgresql.conf(для всего кластера):shared_preload_libraries = 'auto_dump' auto_dump.enable = on auto_dump.output_directory = '/var/lib/postgresql/auto_dump_files/'
Перезапустите Tantor SE-1C:
systemctl restart tantor-se-1c-server-18
В целевой базе данных:
CREATE EXTENSION auto_dump;
F.3.4. Конфигурация (GUCs) #
Все параметры GUC имеют значение SUSET (могут быть установлены суперпользователем во время выполнения, если не указано иное ограничение). Значения по умолчанию соответствуют текущей реализации в коде.
F.3.4.1. Основные параметры (GUC) #
| GUC | Тип | Значение по умолчанию | Описание |
|---|---|---|---|
auto_dump.enable
| bool |
off
| Главный GUC для включения дампирования. |
auto_dump.output_directory
| string |
''
| Каталог, в котором создаются папки дампов (обязательно). |
auto_dump.dump_query
| bool |
on
| Сохранять исходный текст запроса в отдельный файл query.sql (не как блок комментариев). |
auto_dump.dump_create
| bool |
on
| Включает операторы создания таблиц. |
auto_dump.dump_indexes
| bool |
on
| Включает операторы создания индексов. |
auto_dump.dump_data | bool | on | Включить данные таблицы (INSERT/COPY; при auto_dump.data_format = 'copy-file' создаёт table-<table>.txt). |
auto_dump.data_format | enum | copy-stdin | Формат дампа данных: insert, copy-file, copy-stdin (см. подробности ниже). |
auto_dump.dump_plan | bool | on | Сохранять вывод EXPLAIN и EXPLAIN (ANALYZE, BUFFERS, WAL, TIMING, SUMMARY) в отдельные файлы plan_explain.txt и plan_analyze.txt (не в виде блоков комментариев). Требуется инструментирование. |
auto_dump.dump_readme | bool | on | Генерировать файл readme.txt с инструкциями по воспроизведению в каждом каталоге дампа. |
F.3.4.2. Параметры сохранения таблиц #
| GUC | Тип | Значение по умолчанию | Описание |
|---|---|---|---|
auto_dump.dump_temporary_tables
| bool |
true
| Сохраняет временные таблицы, на которые ссылается запрос. |
auto_dump.dump_persistent_tables
| bool |
false
| Сохраняет постоянные таблицы, на которые ссылается запрос. |
auto_dump.dump_all_temp_tables
| bool |
false
| Сохраняет все временные таблицы в текущей сессии backend, а не только те, которые были использованы в запросе, вызвавшем триггер. |
F.3.4.3. Триггеры #
| GUC | Тип | По умолчанию | Диапазон | Описание |
|---|---|---|---|---|
auto_dump.dump_on_query_string | строка | '' | — | Если не пусто, выполняет дамп, когда текст запроса содержит эту подстроку или подстроки. |
auto_dump.dump_on_cancel | bool | false | true/false | Выгружать дамп при отмене текущего запроса. Требует патча с core hook. |
auto_dump.dump_on_bad_plan | bool | false | true/false | Выполнять дамп, когда выполненный план считается «плохим» (см. пороги ниже). Требуется инструментирование. |
auto_dump.dump_on_duration | int | -1 | -1 … INT_MAX | Выполнять дамп, если время выполнения запроса (в миллисекундах) превышает это значение. -1 отключает срабатывание; 0 — дамп для каждого запроса. |
F.3.4.3.1. dump_on_query_string: несколько
подстрок с | #
Вы можете указать несколько альтернатив для подстроки-триггера, разделяя их вертикальной чертой (|):
CREATE TABLE AAA(BBB int, CCC int); SET auto_dump.dump_on_query_string = 'AAA'; -- Triggers (contains 'AAA') - default work SELECT 1 FROM AAA; SET auto_dump.dump_on_query_string = 'AAA|BBB|CCC'; -- Triggers (contains 'AAA' or 'BBB' or 'CCC') SELECT 1 FROM AAA; SELECT BBB FROM AAA; -- Does not trigger (different case): SELECT ccc FROM aaa; -- To match case variants, list them explicitly: SET auto_dump.dump_on_query_string = 'AAA|BBB|CCC|aaa|bbb|ccc';
Примечание
Сопоставление подстрок чувствительно к регистру и нормализует пробелы/разделители (последовательные символы пробелов/разделителей в шаблоне соответствуют любому пробелу в запросе). Для более быстрой работы используйте в этом параметре GUC более чем 10 символов.
F.3.4.4. “Пороговые значения "плохого плана"” #
Узел плана считается “плохим” только если
оба включённых порога превышены (логическое И). Вы можете отключить порог, установив его
в значение 0 или ниже.
| GUC | Тип | По умолчанию | Диапазон | Значение |
|---|---|---|---|---|
auto_dump.bad_plan_count_threshold | int | 0 | 0 … INT_MAX | Устанавливает разницу между оценочным и фактическим количеством строк, при которой будет выполнен дамп плана. > 0 включает; = 0 отключает. |
auto_dump.bad_plan_percent_threshold | int | 0 | 0 … INT_MAX | Устанавливает процентное различие между оценочным и фактическим количеством строк, при котором происходит дамп плана. Значение > 0 включает проверку порога; = 0 отключает этот порог (всегда проходит). |
auto_dump.bad_plan_min_expected_rows | int | 100 | 0 … INT_MAX | Минимальный порог оценочного количества строк. Узлы, у которых как оценочное, так и фактическое количество строк ниже порога, игнорируются (подавление шума). |
auto_dump.bad_plan_min_actual_rows | int | 100 | 0 … INT_MAX | Минимальный порог фактического количества строк (используется вместе с порогом оценочного количества строк). |
Примечание
На загруженных рабочих нагрузках 1С, начните с:
auto_dump.dump_on_bad_plan = on auto_dump.bad_plan_min_expected_rows = 100 auto_dump.bad_plan_min_actual_rows = 100 auto_dump.bad_plan_count_threshold = 1000 auto_dump.bad_plan_percent_threshold = 300
Если в запросах часто встречается estimate = 0, и вы всё равно хотите реагировать на большие абсолютные несоответствия, отключите порог в процентах:
auto_dump.bad_plan_percent_threshold = 0
F.3.5. Режимы срабатывания #
По подстроке: если
auto_dump.dump_on_query_stringявляется непустой строкой и текст запроса содержит её, создаётся дамп.При отмене: если
auto_dump.dump_on_cancel = on, дамп создаётся, когдаpg_cancel_backend()прерывает выполняющийся запрос (требуется патч ядра с ProcessInterrupts_hook).При “плохом плане”: если
auto_dump.dump_on_bad_plan = on, исполнитель проверяет счетчики инструментирования после выполнения и срабатывает, если пороги превышены.
F.3.6. Ручной запуск дампа (SQL-функции) #
Три SQL-функции позволяют инициировать дампы для других серверных процессов (требуются привилегии pg_write_server_files):
| Функция | Описание |
|---|---|
auto_dump_immediate(pid int) | Немедленно создать дамп текущего запроса процесса с pid и отменить его выполнение |
auto_dump_current(pid int) | Выгружает текущий запрос процесса с идентификатором pid после его завершения |
auto_dump_next(pid int) | Выгрузить следующий запрос, выполняемый серверным процессом с pid |
Пример:
SELECT auto_dump_current(12345); -- Dump current query of backend with PID 12345
F.3.7. Что выгружается #
Дампы записываются в отдельный каталог для каждого запроса (<PID>-YYYY_MM_DD_hh_mm_ss-<counter>/) и содержат следующие файлы в зависимости от настроек GUC:
| Файл | Создается когда | Поведение по умолчанию |
|---|---|---|
query.sql | auto_dump.dump_query = on | Включено (по умолчанию: on) |
create_temporary.sql | (auto_dump.dump_create = on ИЛИ auto_dump.dump_indexes = on) И (auto_dump.dump_temporary_tables = on ИЛИ auto_dump.dump_all_temp_tables = on) | Включено (временные таблицы выгружаются по умолчанию) |
create_persistent.sql | (auto_dump.dump_create = on ИЛИ auto_dump.dump_indexes = on) И auto_dump.dump_persistent_tables = on | Не включено (постоянные таблицы отключены по умолчанию) |
insert_temporary.sql | auto_dump.dump_data = on И (auto_dump.dump_temporary_tables = on ИЛИ auto_dump.dump_all_temp_tables = on) | Включено (данные для временных таблиц выгружаются по умолчанию) |
insert_persistent.sql | auto_dump.dump_data = on И auto_dump.dump_persistent_tables = on | Не включено (данные постоянных таблиц по умолчанию отключены) |
table-<table>.txt | auto_dump.dump_data = on И auto_dump.data_format = 'copy-file' | Не включено по умолчанию. Один файл на каждую выгруженную таблицу; содержит необработанные данные таблицы, используемые для воспроизведения. |
plan_explain.txt | auto_dump.dump_plan = on | Включено (по умолчанию: on) |
plan_analyze.txt | auto_dump.dump_plan = on (требуется инструментирование) | Включено (по умолчанию: on) |
readme.txt | auto_dump.dump_readme = on | Включено (по умолчанию: on) |
Состав дампа по умолчанию (с стандартными настройками):
Схемы временных таблиц (
create_temporary.sql)Данные временной таблицы в формате
COPY ... FROM stdin(insert_temporary.sql)Определения индексов для выгруженных таблиц (
auto_dump.dump_indexes = on)Исходный текст запроса (
query.sql)Планы выполнения (
plan_explain.txt,plan_analyze.txt)Инструкции по воспроизведению (
readme.txt)
Данные таблицы (insert_temporary.sql / insert_persistent.sql) — формат зависит от auto_dump.data_format:
insert: операторы INSERTcopy-stdin: COPY ... FROM stdin (встроенные данные)copy-file: отдельные файлыtable-<table>.txtдля каждой таблицы + команды загрузки вinsert_*.sql
Постоянные таблицы по умолчанию исключены. Чтобы включить их, установите auto_dump.dump_persistent_tables = on.
Примечание
В отличие от устаревшей версии, текст запроса и планы выполнения никогда не вставляются в виде комментариев в файлы схемы/данных. Каждый артефакт записывается в свой собственный отдельный файл для более чистых рабочих процессов воспроизведения.
F.3.8. Пример использования (из реальной нагрузки, похожей на 1С) #
У нас есть SQL-запрос, который нам нужно воспроизвести и отладить:
SELECT
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef,
CASE WHEN (T1._Q_000_F_003RRef IN ('\\261\\321\\276\\257\\272\\331\\300wM\\263\\336*q\\031I\\316'::bytea, '\\247\\202\\266$\\366\\301r\\343N\\300\\333\\\\m1\\221\\330'::bytea, '\\235\\352S\\336\\242/$\\372L\\011\\237\\202K\\246\\332n'::bytea)) THEN SUM(T2._Fld33213) WHEN (T1._Q_000_F_003RRef IN ('\\200\\357\\030o\\256\\333\\332W@\\263\\231\\3626\\013\\334t'::bytea)) THEN SUM(T4._Fld33309) END,
T1._Q_000_F_000RRef
FROM pg_temp.tt7 T1
LEFT OUTER JOIN _Document1328_VT33208X1 T2
LEFT OUTER JOIN _Document1328X1 T3
ON (T2._Document1328_IDRRef = T3._IDRRef) AND (T3._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T3._Fld33135RRef) AND (T1._Q_000_F_001RRef = T2._Fld33210RRef) AND (T1._Q_000_F_002RRef = T2._Fld33211RRef) AND (T2._Fld33240 = FALSE)) AND (T2._Fld2488 = CAST(0 AS NUMERIC))
LEFT OUTER JOIN _Document1328_VT33302X1 T4
LEFT OUTER JOIN _Document1328X1 T5
ON (T4._Document1328_IDRRef = T5._IDRRef) AND (T5._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T5._Fld33135RRef) AND (T1._Q_000_F_001RRef = T4._Fld33304RRef) AND (T1._Q_000_F_002RRef = T4._Fld33305RRef) AND (T4._Fld77517 = FALSE)) AND (T4._Fld2488 = CAST(0 AS NUMERIC))
GROUP BY T1._Q_000_F_000RRef,
T1._Q_000_F_003RRef,
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef;
В postgresql.conf включите расширение, укажите каталог для дампов и задайте подстроку, которая должна вызывать дамп для этого запроса:
shared_preload_libraries = 'auto_dump' auto_dump.enable = on auto_dump.output_directory = '/var/lib/postgresql/auto_dump_files/' auto_dump.dump_on_query_string = 'LEFT OUTER JOIN _Document1328_VT33208X1 T2'
Перезапустите службу Tantor SE-1C:
systemctl restart tantor-se-1c-server-18
Создайте расширение в базе данных, в которой выполняется целевой запрос:
-- psql \c erp CREATE EXTENSION auto_dump;
После того как пользователь в 1С инициирует бизнес-действие, запускающее запрос, в каталоге /var/lib/postgresql/auto_dump_files/ появляется новый подкаталог.
По умолчанию выгружаются только временные таблицы (auto_dump.dump_temporary_tables = on).
CREATE TEMPORARY TABLE tt7 (
_q_000_f_000rref bytea,
_q_000_f_001rref bytea,
_q_000_f_002rref bytea,
_q_000_f_003rref bytea,
_q_000_f_004 numeric(10,0),
_q_000_f_005 timestamp without time zone,
_q_000_f_006 timestamp without time zone,
_q_000_f_007 timestamp without time zone
);
CREATE INDEX tmpind_0 ON pg_temp.tt7 USING btree (_q_000_f_000rref);INSERT INTO tt7 (_q_000_f_000rref, _q_000_f_001rref, _q_000_f_002rref, _q_000_f_003rref, _q_000_f_004, _q_000_f_005, _q_000_f_006, _q_000_f_007) VALUES (E'\\x98891866dab152db11eedf71f5f2a486',E'\\x83b11866dab152db11ee95becd03e9a6',E'\\x00000000000000000000000000000000',E'\\xb1d1beafbad9c0774db3de2a711949ce',0,'2024-03-12 00:00:00','2024-03-13 00:00:00','2024-03-12 00:00:00');
ANALYZE tt7;
/*
SELECT
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef,
CASE WHEN (T1._Q_000_F_003RRef IN ('\261\321\276\257\272\331\300wM\263\336*q\031I\316'::bytea, '\247\202\266$\366\301r\343N\300\333\\m1\221\330'::bytea, '\235\352S\336\242/$\372L\011\237\202K\246\332n'::bytea)) THEN SUM(T2._Fld33213) WHEN (T1._Q_000_F_003RRef IN ('\200\357\030o\256\333\332W@\263\231\3626\013\334t'::bytea)) THEN SUM(T4._Fld33309) END,
T1._Q_000_F_000RRef
FROM pg_temp.tt7 T1
LEFT OUTER JOIN _Document1328_VT33208X1 T2
LEFT OUTER JOIN _Document1328X1 T3
ON (T2._Document1328_IDRRef = T3._IDRRef) AND (T3._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T3._Fld33135RRef) AND (T1._Q_000_F_001RRef = T2._Fld33210RRef) AND (T1._Q_000_F_002RRef = T2._Fld33211RRef) AND (T2._Fld33240 = FALSE)) AND (T2._Fld2488 = CAST(0 AS NUMERIC))
LEFT OUTER JOIN _Document1328_VT33302X1 T4
LEFT OUTER JOIN _Document1328X1 T5
ON (T4._Document1328_IDRRef = T5._IDRRef) AND (T5._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T5._Fld33135RRef) AND (T1._Q_000_F_001RRef = T4._Fld33304RRef) AND (T1._Q_000_F_002RRef = T4._Fld33305RRef) AND (T4._Fld77517 = FALSE)) AND (T4._Fld2488 = CAST(0 AS NUMERIC))
GROUP BY T1._Q_000_F_000RRef,
T1._Q_000_F_003RRef,
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef;*/
/*
Query Text: SELECT
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef,
CASE WHEN (T1._Q_000_F_003RRef IN ('\\261\\321\\276\\257\\272\\331\\300wM\\263\\336*q\\031I\\316'::bytea, '\\247\\202\\266$\\366\\301r\\343N\\300\\333\\\\m1\\221\\330'::bytea, '\\235\\352S\\336\\242/$\\372L\\011\\237\\202K\\246\\332n'::bytea)) THEN SUM(T2._Fld33213) WHEN (T1._Q_000_F_003RRef IN ('\\200\\357\\030o\\256\\333\\332W@\\263\\231\\3626\\013\\334t'::bytea)) THEN SUM(T4._Fld33309) END,
T1._Q_000_F_000RRef
FROM pg_temp.tt7 T1
LEFT OUTER JOIN _Document1328_VT33208X1 T2
LEFT OUTER JOIN _Document1328X1 T3
ON (T2._Document1328_IDRRef = T3._IDRRef) AND (T3._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T3._Fld33135RRef) AND (T1._Q_000_F_001RRef = T2._Fld33210RRef) AND (T1._Q_000_F_002RRef = T2._Fld33211RRef) AND (T2._Fld33240 = FALSE)) AND (T2._Fld2488 = CAST(0 AS NUMERIC))
LEFT OUTER JOIN _Document1328_VT33302X1 T4
LEFT OUTER JOIN _Document1328X1 T5
ON (T4._Document1328_IDRRef = T5._IDRRef) AND (T5._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T5._Fld33135RRef) AND (T1._Q_000_F_001RRef = T4._Fld33304RRef) AND (T1._Q_000_F_002RRef = T4._Fld33305RRef) AND (T4._Fld77517 = FALSE)) AND (T4._Fld2488 = CAST(0 AS NUMERIC))
GROUP BY T1._Q_000_F_000RRef,
T1._Q_000_F_003RRef,
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef
GroupAggregate (cost=5.02..5.04 rows=1 width=100)
Group Key: t1._q_000_f_000rref, t1._q_000_f_003rref, t1._q_000_f_001rref, t1._q_000_f_002rref
-> Sort (cost=5.02..5.02 rows=1 width=78)
Sort Key: t1._q_000_f_000rref, t1._q_000_f_003rref, t1._q_000_f_001rref, t1._q_000_f_002rref
-> Nested Loop Left Join (cost=0.73..5.00 rows=1 width=78)
-> Nested Loop Left Join (cost=0.39..3.41 rows=1 width=73)
-> Seq Scan on tt7 t1 (cost=0.00..1.01 rows=1 width=68)
-> Nested Loop (cost=0.39..2.39 rows=1 width=56)
Join Filter: ((t1._q_000_f_001rref = t4._fld33304rref) AND (t1._q_000_f_002rref = t4._fld33305rref))
-> Index Only Scan using _document1328_4x1 on _document1328x1 t5 (cost=0.17..1.29 rows=1 width=34)
Index Cond: ((_fld2488 = '0'::numeric) AND (_fld33135rref = t1._q_000_f_000rref))
-> Index Scan using _document1328_vt33302_skx1 on _document1328_vt33302x1 t4 (cost=0.22..0.93 rows=14 width=56)
Index Cond: ((_fld2488 = '0'::numeric) AND (_document1328_idrref = t5._idrref))
Filter: (NOT _fld77517)
-> Nested Loop (cost=0.34..1.58 rows=1 width=56)
Join Filter: ((t1._q_000_f_001rref = t2._fld33210rref) AND (t1._q_000_f_002rref = t2._fld33211rref))
-> Index Only Scan using _document1328_4x1 on _document1328x1 t3 (cost=0.17..1.29 rows=1 width=34)
Index Cond: ((_fld2488 = '0'::numeric) AND (_fld33135rref = t1._q_000_f_000rref))
-> Index Scan using _document1328_vt33208_skx1 on _document1328_vt33208x1 t2 (cost=0.17..0.28 rows=1 width=56)
Index Cond: ((_fld2488 = '0'::numeric) AND (_document1328_idrref = t3._idrref))
Filter: (NOT _fld33240)
*/
/*
Query Text: SELECT
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef,
CASE WHEN (T1._Q_000_F_003RRef IN ('\\261\\321\\276\\257\\272\\331\\300wM\\263\\336*q\\031I\\316'::bytea, '\\247\\202\\266$\\366\\301r\\343N\\300\\333\\\\m1\\221\\330'::bytea, '\\235\\352S\\336\\242/$\\372L\\011\\237\\202K\\246\\332n'::bytea)) THEN SUM(T2._Fld33213) WHEN (T1._Q_000_F_003RRef IN ('\\200\\357\\030o\\256\\333\\332W@\\263\\231\\3626\\013\\334t'::bytea)) THEN SUM(T4._Fld33309) END,
T1._Q_000_F_000RRef
FROM pg_temp.tt7 T1
LEFT OUTER JOIN _Document1328_VT33208X1 T2
LEFT OUTER JOIN _Document1328X1 T3
ON (T2._Document1328_IDRRef = T3._IDRRef) AND (T3._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T3._Fld33135RRef) AND (T1._Q_000_F_001RRef = T2._Fld33210RRef) AND (T1._Q_000_F_002RRef = T2._Fld33211RRef) AND (T2._Fld33240 = FALSE)) AND (T2._Fld2488 = CAST(0 AS NUMERIC))
LEFT OUTER JOIN _Document1328_VT33302X1 T4
LEFT OUTER JOIN _Document1328X1 T5
ON (T4._Document1328_IDRRef = T5._IDRRef) AND (T5._Fld2488 = CAST(0 AS NUMERIC))
ON ((T1._Q_000_F_000RRef = T5._Fld33135RRef) AND (T1._Q_000_F_001RRef = T4._Fld33304RRef) AND (T1._Q_000_F_002RRef = T4._Fld33305RRef) AND (T4._Fld77517 = FALSE)) AND (T4._Fld2488 = CAST(0 AS NUMERIC))
GROUP BY T1._Q_000_F_000RRef,
T1._Q_000_F_003RRef,
T1._Q_000_F_001RRef,
T1._Q_000_F_002RRef
GroupAggregate (cost=5.02..5.04 rows=1 width=100) (actual time=0.125..0.127 rows=1 loops=1)
Output: t1._q_000_f_001rref, t1._q_000_f_002rref, CASE WHEN (t1._q_000_f_003rref = ANY ('{"\\\\xb1d1beafbad9c0774db3de2a711949ce","\\\\xa782b624f6c172e34ec0db5c6d3191d8","\\\\x9dea53dea22f24fa4c099f824ba6da6e"}'::bytea[])) THEN sum(t2._fld33213) WHEN (t1._q_000_f_003rref = '\\x80ef186faedbda5740b399f2360bdc74'::bytea) THEN sum(t4._fld33309) ELSE NULL::numeric END, t1._q_000_f_000rref, t1._q_000_f_003rref
Group Key: t1._q_000_f_000rref, t1._q_000_f_003rref, t1._q_000_f_001rref, t1._q_000_f_002rref
Buffers: shared hit=20, local hit=1
-> Sort (cost=5.02..5.02 rows=1 width=78) (actual time=0.113..0.114 rows=1 loops=1)
Output: t1._q_000_f_001rref, t1._q_000_f_002rref, t1._q_000_f_000rref, t1._q_000_f_003rref, t2._fld33213, t4._fld33309
Sort Key: t1._q_000_f_000rref, t1._q_000_f_003rref, t1._q_000_f_001rref, t1._q_000_f_002rref
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=20, local hit=1
-> Nested Loop Left Join (cost=0.73..5.00 rows=1 width=78) (actual time=0.097..0.099 rows=1 loops=1)
Output: t1._q_000_f_001rref, t1._q_000_f_002rref, t1._q_000_f_000rref, t1._q_000_f_003rref, t2._fld33213, t4._fld33309
Buffers: shared hit=20, local hit=1
-> Nested Loop Left Join (cost=0.39..3.41 rows=1 width=73) (actual time=0.074..0.075 rows=1 loops=1)
Output: t1._q_000_f_001rref, t1._q_000_f_002rref, t1._q_000_f_003rref, t1._q_000_f_000rref, t4._fld33309
Buffers: shared hit=12, local hit=1
-> Seq Scan on pg_temp.tt7 t1 (cost=0.00..1.01 rows=1 width=68) (actual time=0.008..0.009 rows=1 loops=1)
Output: t1._q_000_f_000rref, t1._q_000_f_001rref, t1._q_000_f_002rref, t1._q_000_f_003rref, t1._q_000_f_004, t1._q_000_f_005, t1._q_000_f_006, t1._q_000_f_007
Buffers: local hit=1
-> Nested Loop (cost=0.39..2.39 rows=1 width=56) (actual time=0.063..0.064 rows=0 loops=1)
Output: t4._fld33309, t4._fld33304rref, t4._fld33305rref, t5._fld33135rref
Join Filter: ((t1._q_000_f_001rref = t4._fld33304rref) AND (t1._q_000_f_002rref = t4._fld33305rref))
Rows Removed by Join Filter: 7
Buffers: shared hit=12
-> Index Only Scan using _document1328_4x1 on public._document1328x1 t5 (cost=0.17..1.29 rows=1 width=34) (actual time=0.030..0.030 rows=1 loops=1)
Output: t5._fld2488, t5._fld33135rref, t5._idrref
Index Cond: ((t5._fld2488 = '0'::numeric) AND (t5._fld33135rref = t1._q_000_f_000rref))
Heap Fetches: 0
Buffers: shared hit=4
-> Index Scan using _document1328_vt33302_skx1 on public._document1328_vt33302x1 t4 (cost=0.22..0.93 rows=14 width=56) (actual time=0.022..0.030 rows=7 loops=1)
Output: t4._document1328_idrref, t4._fld2488, t4._keyfield, t4._lineno33303, t4._fld33304rref, t4._fld33305rref, t4._fld33306rref, t4._fld33307rref, t4._fld33308, t4._fld33309, t4._fld33310rref, t4._fld33311, t4._fld33312rref, t4._fld33313, t4._fld33314rref, t4._fld33315rref, t4._fld112251rref, t4._fld33316, t4._fld33317, t4._fld33318, t4._fld33319, t4._fld33320, t4._fld33321, t4._fld33322, t4._fld33323, t4._fld33325rref, t4._fld101275, t4._fld77517, t4._fld77518rref, t4._fld77519, t4._fld33324rref, t4._fld77520rref, t4._fld101276rref, t4._fld81551, t4._fld88569rref, t4._fld112252, t4._fld112253
Index Cond: ((t4._fld2488 = '0'::numeric) AND (t4._document1328_idrref = t5._idrref))
Filter: (NOT t4._fld77517)
Buffers: shared hit=8
-> Nested Loop (cost=0.34..1.58 rows=1 width=56) (actual time=0.021..0.022 rows=1 loops=1)
Output: t2._fld33213, t2._fld33210rref, t2._fld33211rref, t3._fld33135rref
Join Filter: ((t1._q_000_f_001rref = t2._fld33210rref) AND (t1._q_000_f_002rref = t2._fld33211rref))
Buffers: shared hit=8
-> Index Only Scan using _document1328_4x1 on public._document1328x1 t3 (cost=0.17..1.29 rows=1 width=34) (actual time=0.005..0.005 rows=1 loops=1)
Output: t3._fld2488, t3._fld33135rref, t3._idrref
Index Cond: ((t3._fld2488 = '0'::numeric) AND (t3._fld33135rref = t1._q_000_f_000rref))
Heap Fetches: 0
Buffers: shared hit=4
-> Index Scan using _document1328_vt33208_skx1 on public._document1328_vt33208x1 t2 (cost=0.17..0.28 rows=1 width=56) (actual time=0.014..0.014 rows=1 loops=1)
Output: t2._document1328_idrref, t2._fld2488, t2._keyfield, t2._lineno33209, t2._fld33210rref, t2._fld33211rref, t2._fld33212, t2._fld33213, t2._fld33214rref, t2._fld33215_type, t2._fld33215_rtref, t2._fld33215_rrref, t2._fld33216rref, t2._fld33217rref, t2._fld33218, t2._fld33219, t2._fld33220, t2._fld33221, t2._fld33222, t2._fld33223, t2._fld33224, t2._fld33225, t2._fld33226_type, t2._fld33226_rtref, t2._fld33226_rrref, t2._fld33227_type, t2._fld33227_rtref, t2._fld33227_rrref, t2._fld33236_type, t2._fld33236_rtref, t2._fld33236_rrref, t2._fld33229, t2._fld33230rref, t2._fld33231, t2._fld33232rref, t2._fld33233_type, t2._fld33233_rtref, t2._fld33233_rrref, t2._fld33234_type, t2._fld33234_rtref, t2._fld33234_rrref, t2._fld33235_type, t2._fld33235_rtref, t2._fld33235_rrref, t2._fld33237, t2._fld33238, t2._fld33239rref, t2._fld33240, t2._fld33241rref, t2._fld33242rref, t2._fld77511, t2._fld77512, t2._fld77513, t2._fld88563rref, t2._fld88564rref, t2._fld104367, t2._fld104368, t2._fld112249, t2._fld81859
Index Cond: ((t2._fld2488 = '0'::numeric) AND (t2._document1328_idrref = t3._idrref))
Filter: (NOT t2._fld33240)
Buffers: shared hit=4
Settings: max_parallel_workers_per_gather = '0', effective_io_concurrency = '128', maintenance_io_concurrency = '128', random_page_cost = '1.1', from_collapse_limit = '20', join_collapse_limit = '20', max_parallel_workers = '13', hash_mem_multiplier = '8', temp_buffers = '189MB', work_mem = '378MB', effective_cache_size = '279GB', selectivity_model = '1c', enable_mergejoin = 'off', cpu_operator_cost = '0.001'
*/
F.3.9. Настройка и рекомендации #
Шумные среды (1С): используйте пороги для подавления мелких узлов:
auto_dump.dump_on_bad_plan = on auto_dump.bad_plan_min_expected_rows = 100 auto_dump.bad_plan_min_actual_rows = 100 auto_dump.bad_plan_count_threshold = 1000 auto_dump.bad_plan_percent_threshold = 300
Если у многих планов
estimate = 0и вы всё же хотите отлавливать их по абсолютной ошибке, отключите порог в процентах:auto_dump.bad_plan_percent_threshold = 0
Чтобы ограничить объем дампа во время диагностики:
auto_dump.dump_plan = off auto_dump.dump_persistent_tables = off auto_dump.dump_all_temp_tables = off
F.3.10. Устранение неполадок #
Дампы не отображаются:
Проверьте, что
auto_dump.enable = on, а также чтоauto_dump.output_directoryустановлен и доступен для записи.Убедитесь, что
shared_preload_librariesвключаетauto_dump, и сервер был перезапущен.Проверьте, действительно ли выполнено условие триггера (подстрока / отмена / неверный план).
Сбой сервера при отмене или дампе плана:
Убедитесь, что вы запускаете сервер, собранный с патчем
ProcessInterrupts_hook.
Слишком много дампов:
Увеличьте пороги и пределы (смотрите раздел конфигурации).
Используйте триггер по подстроке для точечного срабатывания при анализе проблемы (
auto_dump.dump_on_query_string).
Постоянные таблицы отсутствуют в дампах:
Установите
auto_dump.dump_persistent_tables = on.