Skip to content
 
 

Repository files navigation

fasttrun

English version: README_EN.md

fasttrun — расширение для PostgreSQL 16, PostgreSQL 17 и PostgreSQL 18. Оно ускоряет повторяющийся цикл работы с временными таблицами:

  1. очистить таблицу;
  2. заполнить её заново;
  3. обновить статистику для планировщика;
  4. выполнить расчёт.

Основные функции расширения:

  • fasttruncate() очищает локальную временную таблицу;
  • fasttrun_analyze() обновляет оценки планировщика после её заполнения.

Обычные TRUNCATE и ANALYZE уведомляют другие серверные процессы PostgreSQL об изменениях системного каталога и файлов отношений. При высокой конкуренции такие уведомления могут создавать заметную нагрузку на CPU. fasttrun выполняет основную работу только в текущем серверном процессе и в стандартном режиме не отправляет общих сообщений инвалидации.

Установка расширения сама по себе ничего не ускоряет. Приложение должно явно вызывать функции fasttrun вместо штатных команд в поддерживаемом сценарии.

Быстрые ссылки: проверка перед внедрением, установка, работа через пулер, восстановление после ошибки, обновление.

Кому подходит

fasttrun стоит рассматривать, если:

  • приложение многократно переиспользует локальные временные таблицы;
  • одновременно работают десятки длительно живущих серверных соединений;
  • обычные TRUNCATE и ANALYZE создают измеримую нагрузку;
  • приложение можно изменить так, чтобы оно явно вызывало функции fasttrun;
  • данные временной таблицы не требуется восстанавливать после ROLLBACK.

Типичный сценарий — крупные PL/pgSQL-расчёты через пулер соединений. Backend PostgreSQL, то есть серверный процесс одного физического соединения, живёт долго, обслуживает много клиентов и постепенно накапливает временные таблицы для повторного использования.

fasttrun не нужен, если обычные TRUNCATE и ANALYZE не создают заметной нагрузки. Сначала подтвердите проблему измерениями на своей системе.

Что проверить до внедрения

Важно: fasttruncate() не является транзакционной заменой TRUNCATE. После начала физической очистки ROLLBACK не восстановит прежние данные.

Перед использованием проверьте следующее:

  1. Целевые таблицы являются самостоятельными локальными временными heap-таблицами, то есть используют стандартный метод хранения PostgreSQL. Это основной поддерживаемый сценарий.

  2. Для секционирования и наследования не требуется обход дочерних отношений.

  3. Для очистки не нужны внешние ключи, CASCADE, TRUNCATE-триггеры и RESTART IDENTITY.

  4. В PostgreSQL включён track_counts:

    SHOW track_counts;

    Ожидаемое значение — on.

  5. После каждого повторного заполнения код вызывает fasttrun_analyze().

  6. Опечатки в именах контролируются приложением: отсутствующая таблица для основных функций считается допустимым случаем и не вызывает ошибку.

  7. Для каждого серверного соединения предусмотрен бюджет памяти. По умолчанию кеш статистики fasttrun не ограничен.

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

  9. У приложения есть возможность вернуться к обычным TRUNCATE и ANALYZE.

  10. Право EXECUTE выдано только доверенным ролям. Функции работают с правами вызывающего пользователя, но не повторяют все проверки владения и ACL, которые выполняют штатные TRUNCATE и ANALYZE.

Не начинайте внедрение с учёта и предварительного создания таблиц. Основные функции работают без shared_preload_libraries.

Быстрый старт

Сборка и установка

Нужны исходные заголовки PostgreSQL и PGXS для той версии сервера, на которую устанавливается расширение. Используйте pg_config именно этого сервера:

PG_CONFIG=/path/to/postgresql/bin/pg_config

"$PG_CONFIG" --version
make PG_CONFIG="$PG_CONFIG"
make install PG_CONFIG="$PG_CONFIG"

make install должен выполняться пользователем, которому разрешена запись в каталоги библиотек и расширений PostgreSQL.

Для PostgreSQL 16, 17 и 18 нужны отдельные сборки. При последовательной сборке из одного каталога выполняйте make clean перед сменой PG_CONFIG, иначе make может повторно использовать объектный файл от другой основной версии. Установите собранные файлы на все узлы кластера, которые могут обслуживать базу или стать основным сервером.

Создайте расширение в каждой базе, где оно будет использоваться:

SHOW track_counts;

CREATE EXTENSION fasttrun;

SELECT extversion
FROM pg_extension
WHERE extname = 'fasttrun';

Версия по умолчанию — 2.4.0.

Вместо обычного CREATE EXTENSION расширение можно установить в отдельную схему. Схема с API не должна разрешать недоверенным ролям создавать объекты:

CREATE SCHEMA fasttrun_api;
REVOKE CREATE ON SCHEMA fasttrun_api FROM PUBLIC;
CREATE EXTENSION fasttrun WITH SCHEMA fasttrun_api;

При таком варианте квалифицируйте вызовы, например fasttrun_api.fasttruncate(...). Пример из examples/ жёстко вызывает public.fasttruncate() и требует отдельной адаптации.

Все последующие SQL-примеры предполагают обычную установку в public. Если вы выбрали отдельную схему, добавляйте к функциям префикс fasttrun_api. либо настройте для роли безопасный search_path.

Для fasttruncate(), fasttrun_analyze() и остальных основных функций не нужны ни рестарт PostgreSQL, ни shared_preload_libraries, ни session_preload_libraries. Библиотека загрузится при первом вызове функции.

shared_preload_libraries требуется только для общего реестра учёта и fasttrun_prewarm(). Специального положения fasttrun в списке библиотек нет.

Права

Все 10 SQL-функций после установки имеют стандартное для PostgreSQL право EXECUTE для PUBLIC и работают как SECURITY INVOKER. Внутренние проверки владения и ACL таблицы не выполняются. Это особенно важно в transaction pooling, если один backend работает под разными ролями через SET ROLE.

Настройку прав должен выполнять суперпользователь или владелец функций. При установке trusted extension (trusted=true) непривилегированная роль владеет объектом расширения, но созданные C-функции принадлежат начальному суперпользователю кластера. Для рекомендованной отдельной схемы закройте весь API, а затем выдайте минимальные права нужным ролям. Повторяйте проверку после ALTER EXTENSION ... UPDATE, поскольку новая версия может добавить функцию:

REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA fasttrun_api FROM PUBLIC;

GRANT USAGE ON SCHEMA fasttrun_api TO app_role;
GRANT EXECUTE ON FUNCTION fasttrun_api.fasttruncate(text) TO app_role;
GRANT EXECUTE ON FUNCTION fasttrun_api.fasttrun_analyze(text) TO app_role;

Ограничьте fasttrun_inspect_stats() отдельно: MCV и гистограммы могут содержать значения из временных данных.

Для обычной установки в public используйте полный блок:

REVOKE EXECUTE ON FUNCTION public.fasttruncate(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_analyze(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_analyze_bulk(text[]) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_relstats(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_collect_stats(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_inspect_stats(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_cache_stats() FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_hot_temp_tables(integer) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_prewarm() FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.fasttrun_reset_temp_stats() FROM PUBLIC;

GRANT EXECUTE ON FUNCTION public.fasttruncate(text) TO app_role;
GRANT EXECUTE ON FUNCTION public.fasttrun_analyze(text) TO app_role;

Остальные права выдайте отдельно ролям приложения, пулера, мониторинга и DBA. После обновления старой установки проверьте также права у старой функции fasttruncate_c(text), если она осталась.

trusted=true также позволяет роли с правом CREATE на базу самостоятельно установить расширение. Реестр учёта общий для всего экземпляра PostgreSQL, поэтому ACL в одной базе не защищает его от функций, установленных в другой базе. Если общий учёт включён, контролируйте возможность установки fasttrun во всех базах экземпляра. Не используйте эту подсистему между недоверенными арендаторами.

Проверка установки

Следующий пример можно выполнить непосредственно в psql:

CREATE TEMP TABLE fasttrun_demo (
    id bigint,
    category integer
);

INSERT INTO fasttrun_demo
SELECT n, n % 10
FROM generate_series(1, 10000) AS g(n);

SELECT fasttrun_analyze('pg_temp.fasttrun_demo');
SELECT * FROM fasttrun_relstats('pg_temp.fasttrun_demo');

SELECT fasttruncate('pg_temp.fasttrun_demo');

SELECT count(*) = 0 AS table_is_empty
FROM fasttrun_demo;

DROP TABLE fasttrun_demo;

Последняя проверка должна вернуть true.

Основной рабочий цикл

Очищайте таблицу перед началом нового расчёта. Не полагайтесь только на очистку в конце: клиент может завершиться с ошибкой до последнего шага.

Шаблон для обычного SQL

Это пример рабочего порядка, а не готовый запрос: замените source_data и последующий SELECT объектами своего приложения.

BEGIN;

CREATE TEMP TABLE IF NOT EXISTS temp_work (
    id bigint,
    value numeric
);

-- Удаляем данные, оставшиеся от предыдущего использования backend.
SELECT fasttruncate('pg_temp.temp_work');

INSERT INTO temp_work
SELECT id, amount
FROM source_data
WHERE processing_date = CURRENT_DATE;

-- Обязательно после каждого повторного заполнения.
SELECT fasttrun_analyze('pg_temp.temp_work');

SELECT ...
FROM temp_work
JOIN ...;

COMMIT;

ROLLBACK отменит обычные изменения транзакции, но не вернёт данные, которые уже были удалены успешным fasttruncate().

Шаблон для PL/pgSQL

Внутри PL/pgSQL функции с результатом void вызываются через PERFORM:

DO $plpgsql$
BEGIN
    CREATE TEMP TABLE IF NOT EXISTS temp_work (
        id bigint,
        value numeric
    );

    PERFORM fasttruncate('pg_temp.temp_work');

    INSERT INTO temp_work
    SELECT id, amount
    FROM source_data
    WHERE processing_date = CURRENT_DATE;

    PERFORM fasttrun_analyze('pg_temp.temp_work');

    -- Дальнейшие запросы к temp_work.
END;
$plpgsql$;

PERFORM нельзя выполнять как отдельную команду в psql. В обычном SQL используйте SELECT fasttruncate(...) и SELECT fasttrun_analyze(...).

Пример более сложной create_temp_table() лежит в examples/create_temp_table.sql. Это пример интеграции, а не часть API расширения. Он зависит от pg_variables, схемы шаблонов и роли _client. В нём текстовые имена подставляются в динамический SQL без %I, поэтому не передавайте в него недоверенные значения и перед внедрением в рабочую среду адаптируйте функцию под свой проект.

CREATE TEMP TABLE IF NOT EXISTS не обновляет структуру уже существующей таблицы. При изменении шаблона после деплоя проверяйте версию структуры и при необходимости удаляйте и создавайте таблицу заново.

Работа через пулер

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

Соблюдайте следующие правила:

  1. Очищайте рабочую таблицу в начале каждого расчёта.
  2. После заполнения всегда вызывайте fasttrun_analyze().
  3. В transaction pooling выполняйте весь цикл в одной явной транзакции.
  4. Statement pooling для такого цикла не подходит.
  5. Не считайте fasttrun границей безопасности между клиентами. Очистка при передаче backend должна обеспечиваться приложением или настройкой пулера.
  6. После SQLSTATE 55000 backend нельзя возвращать в пул, пока таблица не восстановлена. Если результат восстановления неочевиден, закройте физическое соединение и позвольте пулеру создать новое.
  7. DISCARD TEMP и DISCARD ALL удаляют временные таблицы. Это безопасно, но устраняет выгоду от их повторного использования.

При SQLSTATE 55000 сразу перейдите к разделу «Восстановление после ошибки».

Учёт и предварительное создание — отдельная необязательная возможность. fasttrun_prewarm() имеет смысл вызывать при создании нового физического соединения, а не при каждой выдаче соединения клиенту.

Функции

Публичный API содержит 10 SQL-функций.

Основные операции

Функция Назначение Общие сообщения инвалидации
fasttruncate(text) Очищает одну локальную временную heap-таблицу, её индексы, TOAST и индексы TOAST Нет при zero_sinval_truncate=on; при off отправляется служебное сообщение для каждого очищенного отношения
fasttrun_analyze(text) Обновляет локальные оценки размера и при необходимости статистику столбцов Нет
fasttrun_analyze_bulk(VARIADIC text[]) Последовательно выполняет fasttrun_analyze для массива таблиц Нет
fasttrun_collect_stats(text) Явно пересобирает локальную статистику столбцов Нет

fasttruncate() берёт AccessExclusiveLock. fasttrun_analyze() и fasttrun_collect_stats() берут AccessShareLock на время работы. Открытый курсор или активный запрос к очищаемой таблице приведёт к обычной SQL-ошибке до изменения файлов.

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

Пакетный вызов не объединяет локальные инвалидации планов. Они выполняются только для отношений, где статистика действительно изменилась. В худшем случае для N таблиц будет N полных проходов по локальному списку планов.

Диагностика

Функция Назначение
fasttrun_relstats(text) Возвращает текущие локальные relpages и reltuples
fasttrun_inspect_stats(text) Показывает сохранённые кандидаты статистики в формате pg_statistic
fasttrun_cache_stats() Показывает количество записей и память локальных кешей

Для отсутствующей таблицы fasttrun_relstats() возвращает NULL, а fasttrun_inspect_stats() — пустой набор. Пустой набор также означает, что локальная статистика столбцов ещё не собиралась.

fasttrun_inspect_stats() предназначена для диагностики. Наличие строки в её результате не гарантирует, что планировщик использует эту статистику прямо сейчас: проверка свежести может временно скрыть её.

fasttrun_cache_stats() всегда возвращает одну строку со следующими полями:

  • analyze_entries;
  • column_stats_relid_entries;
  • column_stats_entries;
  • analyze_bytes;
  • column_stats_bytes;
  • total_bytes.

total_bytes равен сумме analyze_bytes и column_stats_bytes. Все значения относятся только к текущему физическому backend.

Учёт и предварительное создание таблиц

Функция Назначение
fasttrun_hot_temp_tables(n) Возвращает самые часто встречавшиеся имена временных таблиц
fasttrun_prewarm() Вызывает внешнюю create_temp_table(text) для выбранных имён
fasttrun_reset_temp_stats() Очищает общий реестр и сохранённый файл учёта

Без shared_preload_libraries первая функция возвращает пустой набор, вторая — 0, а сброс ничего не делает.

Как обновляется статистика

Размер таблицы

fasttrun_analyze() публикует в памяти текущего backend значения, которые планировщик использует для оценки размера таблицы и индексов.

Внутри одной транзакции повторный вызов обычно вычисляет число строк по счётчикам изменений и не сканирует таблицу. При COMMIT это дельта-состояние очищается. Локальные оценки и статистика столбцов сохраняются, но первый вызов в следующей транзакции может снова просканировать таблицу.

Если таблица не превышает fasttrun.max_analyze_pages, сканирование даёт точное число строк. Для более крупной таблицы используется ограниченная выборка блоков и получается оценка, как при обычном ANALYZE.

Статистика столбцов

По умолчанию fasttrun использует тот же механизм анализа обычных столбцов, что и PostgreSQL. Рассчитываются наиболее частые значения (MCV), гистограмма, корреляция, доля NULL, средняя ширина и число различных значений.

Размер выборки по умолчанию — 3000 строк. Это осознанный компромисс между скоростью и качеством. Обычный ANALYZE часто использует заметно большую выборку. Для наиболее близкого поведения установите:

BEGIN;
SET LOCAL fasttrun.sample_rows = -1;
SET LOCAL fasttrun.stats_refresh_threshold = 0;
-- Заполнение, fasttrun_analyze() и рабочие запросы.
COMMIT;

Даже в этом режиме полное совпадение результатов не гарантируется: случайные выборки могут различаться. Сбор будет дороже, чем с настройками по умолчанию. SET LOCAL не оставляет изменённые параметры следующему клиенту пулера.

После небольшого DML кешированная статистика может оставаться видимой, как и между обычными запусками ANALYZE. При достижении fasttrun.stats_refresh_threshold она пересобирается, если автоматический сбор включён, sample_rows не равен нулю и лимит памяти разрешает сбор. В противном случае fasttrun скрывает устаревшую статистику, чтобы планировщик не использовал заведомо старое распределение.

track_counts=on требуется для проверки свежести статистики столбцов и быстрого дельта-пути. При off fasttrun продолжает обновлять размер таблицы через сканирование или выборку блоков, но не сохраняет и не публикует локальную статистику столбцов. При попытке собрать статистику непустой таблицы расширение один раз за время жизни backend пишет WARNING. После включения track_counts снова вызовите fasttrun_analyze() или fasttrun_collect_stats().

Если сбор не отключён через sample_rows=0 и не остановлен лимитом памяти, явный fasttrun_collect_stats() запускает полный проход по таблице. Ограничение fasttrun.max_analyze_pages относится к сканированиям внутри fasttrun_analyze() и не ограничивает этот явный вызов.

Обычный полный ANALYZE возвращает управление статистикой ядру PostgreSQL для всей таблицы. ANALYZE table (col1, ...) передаёт ядру только перечисленные столбцы; локальная статистика остальных столбцов сохраняется. VACUUM без ANALYZE ничего не меняет. Команды, переписывающие таблицу, скрывают старую локальную статистику до следующего сбора.

Настройки

Контекст «пользователь» означает, что параметр можно менять через SET. Контекст «администратор» означает superuser-контекст или отдельно выданное право SET ON PARAMETER. Для изменения этих GUC рестарт не нужен.

Основные параметры

Параметр По умолчанию Диапазон Контекст Назначение
fasttrun.auto_collect_stats on boolean пользователь Собирать статистику столбцов внутри fasttrun_analyze()
fasttrun.sample_rows 3000 -1..1000000 пользователь 0 отключает сбор столбцов; при use_typanalyze=on значение -1 использует размер выборки, запрошенный PostgreSQL, а при off — 3000 строк
fasttrun.stats_refresh_threshold 0.2 0..1 пользователь Доля DML для автоматического пересбора; 0 — при любом DML, 1 — автопересбор отключён
fasttrun.invalidate_threshold 0.2 0..1 пользователь Допустимое накопленное изменение размера до локального сброса планов
fasttrun.use_typanalyze on boolean пользователь Использовать ядерные обработчики статистики; off оставляет только базовые показатели
fasttrun.zero_sinval_truncate on boolean пользователь on очищает файлы без общих сообщений; off использует ядерный путь очистки
fasttrun.max_analyze_pages 100000 0..2147483647 страниц пользователь Выше порога fasttrun_analyze() переходит к выборке блоков; 0 требует полный скан при необходимости сканирования
fasttrun.max_stats_memory 0 КБ пользователь Мягкий предел памяти статистики столбцов; 0 означает отсутствие предела

При размере страницы 8 КБ значение max_analyze_pages=100000 соответствует примерно 800 МБ heap.

Оставляйте fasttrun.zero_sinval_truncate=on в рабочей среде. Значение off предназначено для диагностики и проверки совместимости. Оно не делает очистку более безопасной и возвращает общие сообщения инвалидации. Ошибка внутри ядерного критического участка в этом режиме может привести к PANIC всего экземпляра PostgreSQL.

max_stats_memory проверяется только перед первым сбором статистики для новой таблицы. Уже сохранённая статистика продолжает обновляться. Равенство текущего размера пределу разрешено, поэтому первая новая таблица может превысить лимит на полный объём статистики всех своих столбцов. Размер этого превышения заранее не ограничен. В предел входит только column_stats_bytes; память analyze_bytes не учитывается. Автоматического вытеснения и LRU нет. Если лимит остановил автоматический сбор, backend выдаёт WARNING один раз. Явный fasttrun_collect_stats() выдаёт NOTICE при каждой такой попытке.

Параметры учёта и предварительного создания

Параметр По умолчанию Диапазон Контекст Назначение
fasttrun.track_temp_creates on boolean администратор Включает учёт подходящих CREATE TEMP TABLE
fasttrun.prewarm_count 1000 0..8192 пользователь Максимальное число вызовов вспомогательной функции; 0 отключает prewarm
fasttrun.prewarm_schema dummy_tmp имя схемы администратор Схема таблиц-шаблонов
fasttrun.track_schedule mon-fri 08:00-18:00 строка администратор Временные окна, когда учитываются попытки создания таблиц

Эти параметры имеют практический смысл только при загрузке fasttrun через shared_preload_libraries.

Память

Кеши статистики принадлежат физическому backend и не разделяются между соединениями. При 300 таблицах по 300 столбцов объём может составлять десятки или сотни мегабайт на backend. Широкие значения и увеличенный default_statistics_target повышают расход.

Для измерения используйте фактические данные:

SELECT pg_backend_pid(), *
FROM fasttrun_cache_stats();

Запрос показывает только backend, на котором он выполнен. В transaction pooling разные вызовы могут попасть на разные серверные соединения.

Периодический ресайкл соединений освобождает локальные кеши. Для предсказуемой деградации можно задать fasttrun.max_stats_memory. После достижения лимита новые таблицы продолжат получать оценки размера, но останутся без локальной статистики столбцов. Уже принятая статистика не вытесняется.

Учёт и предварительное создание таблиц

Развернуть необязательный раздел

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

Включение

Добавьте fasttrun в shared_preload_libraries и перезапустите PostgreSQL:

shared_preload_libraries = 'fasttrun'

Если в списке уже есть другие расширения, сохраните их. Специального требования ставить fasttrun последним нет.

session_preload_libraries не заменяет shared_preload_libraries: оно не создаёт общий реестр учёта.

Что именно учитывается

Учитываются только попытки команд вида:

CREATE TEMP TABLE temp_work
(LIKE dummy_tmp.temp_work INCLUDING ALL);

Имя схемы в LIKE должно явно совпадать с fasttrun.prewarm_schema. Создание временной таблицы без такого LIKE не учитывается. INCLUDING ALL показан только для примера и не является обязательным условием учёта.

Также не учитываются CREATE TEMP TABLE AS, SELECT INTO TEMP, определения столбцов без LIKE, неквалифицированный LIKE template и шаблоны из другой схемы. Для последующего предварительного создания имя временной таблицы должно совпадать с именем шаблона. Сам реестр это равенство не проверяет: попытка CREATE TEMP TABLE foo (LIKE dummy_tmp.bar) запишет имя foo, но при предварительном создании будет искаться dummy_tmp.foo.

Счётчик обновляется до выполнения CREATE. Поэтому он отражает попытки, а не только успешно созданные таблицы. Его увеличивают также неуспешные команды, CREATE ... IF NOT EXISTS без фактического создания и команды, которые позже были отменены транзакцией.

Реестр:

  • общий для всего экземпляра PostgreSQL;
  • содержит не более 8192 имён;
  • использует только имя таблицы, без OID базы, схемы и пользователя;
  • суммирует одинаковые имена из разных баз;
  • после заполнения молча не принимает новые имена.

Реестр не является источником аудита или безопасности. Сессия, способная отправлять подходящие команды CREATE TEMP TABLE, может увеличить счётчики даже неуспешными попытками и заполнить реестр новыми именами. Используйте эту возможность только для доверенной нагрузки и контролируйте заполнение.

fasttrun_reset_temp_stats() очищает весь общий реестр, а не только данные текущей базы.

fasttrun_hot_temp_tables(0) возвращает все записи. Сортировка выполняется по числу попыток, затем по времени последней попытки и имени.

Расписание

По умолчанию попытки учитываются по рабочим дням с 08:00 до 18:00:

fasttrun.track_schedule = 'mon-fri 08:00-18:00'

Пустая строка включает учёт постоянно. Поддерживается до восьми окон:

fasttrun.track_schedule = 'mon-fri 08:00-18:00; sat 10:00-14:00'

Дни задаются как mon, tue, wed, thu, fri, sat, sun. Время окончания не входит в окно. Значение 24:00 допустимо только как время окончания. Окно через полночь нужно разделить:

fasttrun.track_schedule = 'fri 22:00-24:00; sat 00:00-02:00'

Используется log_timezone сервера. Ошибка формата записывается как WARNING, после чего учёт остаётся постоянно включённым.

Использование prewarm

fasttrun_prewarm() работает в текущей базе и текущем соединении с правами вызывающей роли. Он выбирает до fasttrun.prewarm_count имён. fasttrun проверяет только наличие любого отношения с таким именем в fasttrun.prewarm_schema. Если отношения нет, имя пропускается. Проверить, что это действительно подходящий шаблон, должна пользовательская create_temp_table(). Функция не дополняет результат менее популярными именами вместо пропущенных.

Расширение не устанавливает create_temp_table(text). Оно выполняет неквалифицированный вызов:

SELECT create_temp_table($1);

Функция с совместимой сигнатурой должна быть доступна через текущий search_path. Размещайте её в доверенной схеме и не допускайте, чтобы раньше неё в search_path находились схемы, куда обычные пользователи могут создавать функции.

Возвращаемое число — количество выполненных вызовов create_temp_table(), а не обязательно количество новых таблиц. Создание отсутствующей таблицы идёт через обычный DDL и создаёт стандартные каталожные инвалидации. Поведение для уже существующей таблицы полностью определяется пользовательской вспомогательной функцией.

Одна ошибка create_temp_table() прерывает весь fasttrun_prewarm(). Ранее обработанные таблицы уже могли быть физически очищены пользовательской функцией, и ROLLBACK не обязан вернуть их данные.

Перед первым включением задайте небольшое fasttrun.prewarm_count, измерьте время и число создаваемых таблиц, затем увеличивайте значение. Значение по умолчанию 1000 не является рекомендацией для любой системы.

Пример:

SELECT * FROM fasttrun_hot_temp_tables(20);
SELECT fasttrun_prewarm();

Сохранение реестра

При штатной остановке PostgreSQL реестр атомарно сохраняется в $PGDATA/pg_stat/fasttrun_temp_stats и загружается при следующем старте.

При аварийном завершении актуальное состояние не записывается. Может быть загружена более старая успешно сохранённая копия. Ошибки записи фиксируются в журнале PostgreSQL; недописанный временный файл удаляется.

Файл не пишется в WAL, не реплицируется и не входит в pg_dump. fasttrun_reset_temp_stats() очищает реестр и удаляет файл немедленно; ROLLBACK этот сброс не отменяет.

Восстановление после ошибки

Ошибка до изменения файлов

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

SQLSTATE 55000

Если ошибка возникла после начала очистки, прежние файлы уже нельзя безопасно восстановить. fasttrun блокирует таблицу в текущем backend. SELECT, DML, COPY, планирование и функции расширения для неё возвращают SQLSTATE 55000. Обычный ROLLBACK и откат к точке сохранения блокировку не снимают.

Сначала завершите ошибочную транзакцию:

ROLLBACK;

Затем выберите один способ восстановления:

  1. Повторите fasttruncate(). После успешного вызова таблица снова доступна, пуста и должна быть заполнена заново.
  2. Удалите таблицу и создайте её заново.
  3. Выполните DISCARD TEMP или DISCARD ALL вне транзакции. Это удалит все временные таблицы текущего backend.
  4. Закройте физическое соединение. Для backend под управлением пулера это самый простой безопасный вариант.

Пример повторной очистки:

BEGIN;
SELECT fasttruncate('pg_temp.temp_work');
COMMIT;

DISCARD ALL дополнительно сбрасывает подготовленные планы, параметры сессии и другое состояние соединения. Если нужно удалить только временные таблицы, используйте DISCARD TEMP.

Для новых операций приложение можно временно вернуть к штатным командам:

TRUNCATE pg_temp.temp_work;
ANALYZE pg_temp.temp_work;

Это не восстанавливает уже заблокированную fasttrun таблицу. Для неё используйте один из четырёх способов выше.

Ошибка в запасном режиме

При fasttrun.zero_sinval_truncate=off расширение вызывает ядерный RelationTruncate для каждого отношения. Ошибка в критическом участке физической очистки PostgreSQL приводит к PANIC. Это завершает все серверные сеансы данного экземпляра, а не только текущий backend. При restart_after_crash=on postmaster заново запускает процессы и выполняет аварийное восстановление. Пулер увидит массовый обрыв соединений.

При restart_after_crash=off PostgreSQL не запускает процессы автоматически; перезапуск и восстановление должен выполнить оператор или система управления кластером.

Проверьте журнал и состояние всего экземпляра PostgreSQL. Продолжать работу старых соединений после такого события невозможно.

Обновление и удаление

Поддерживается обновление с версий 2.0, 2.1, 2.1.1, 2.1.2, 2.2.0, 2.3.0, 2.3.1, 2.3.2, 2.3.3 и 2.3.4.

Обновление C-библиотеки планируйте как обслуживание серверных процессов. Новая сборка и SQL-файлы должны быть установлены на всех узлах кластера.

  • Если fasttrun загружен через shared_preload_libraries, остановите PostgreSQL, установите новую сборку и снова запустите сервер.
  • При ленивой загрузке или session_preload_libraries остановите приём нового трафика и закройте все backend, которые могли загрузить старую библиотеку. Для пулера это означает полный ресайкл серверных соединений. После этого установите новую сборку.

Команды установки:

make PG_CONFIG=/path/to/pg_config
make install PG_CONFIG=/path/to/pg_config

После запуска или открытия нового соединения выполните в каждой базе от имени владельца расширения или суперпользователя:

ALTER EXTENSION fasttrun UPDATE TO '2.4.0';

При физической репликации установите файлы и на резервные узлы, но выполняйте ALTER EXTENSION только на доступном для записи основном сервере. Изменения системного каталога попадут на реплики через обычную репликацию.

Это важно для обновления 2.3.4 → 2.4.0: SQL-миграция добавляет функцию, которой нет в старой C-библиотеке.

При переходе на новую основную версию через pg_upgrade заранее соберите и установите fasttrun против нового pg_config. Каталог расширений переносится вместе с базой; повторный CREATE EXTENSION не требуется. Файл общего реестра учёта не является частью каталога расширения и должен считаться восстанавливаемым служебным состоянием, а не данными, которые обязан переносить pg_upgrade.

Удаление SQL-объектов:

-- Выполните перед DROP, если нужно удалить и общий реестр учёта.
SELECT fasttrun_reset_temp_stats();

DROP EXTENSION fasttrun;

DROP EXTENSION не выгружает уже загруженную библиотеку из backend. Для полного отключения удалите fasttrun из shared_preload_libraries, если он там есть, и перезапустите PostgreSQL. При ленивой или сессионной загрузке переработайте старые соединения. Сам DROP EXTENSION и удаление из preload не удаляют $PGDATA/pg_stat/fasttrun_temp_stats. Без предварительного сброса старые счётчики могут загрузиться при следующем включении расширения.

Вызывайте fasttrun_reset_temp_stats() до удаления fasttrun из shared_preload_libraries, пока общий реестр доступен. Если preload уже отключён, остановите PostgreSQL и удалите файл вручную.

Ограничения

  • Основной поддерживаемый контракт — самостоятельная локальная временная heap-таблица текущего backend.
  • Секционированный родитель и родитель наследования отклоняются. Листовой раздел, который сам является heap-таблицей, или таблица-наследник без собственных потомков может быть принят, но fasttrun обработает только это отношение и не станет обходить иерархию.
  • Внешние ключи не проверяются, TRUNCATE ... CASCADE не выполняется.
  • BEFORE TRUNCATE и AFTER TRUNCATE триггеры не вызываются.
  • DELETE-триггеры также не вызываются.
  • Последовательности SERIAL/IDENTITY не сбрасываются.
  • Физическая очистка не откатывается транзакцией.
  • fasttruncate() публикует reltuples=0. Ядерный TRUNCATE использует служебное значение -1, поэтому после повторного заполнения особенно важно вызвать fasttrun_analyze().
  • Расширенная статистика CREATE STATISTICS, статистика выражений индексов и статистика наследования не собираются.
  • Старая каталожная статистика выражений индексов скрывается; до обычного ANALYZE планировщик использует оценки по умолчанию.
  • ACL, RLS и security-barrier поведение обычного ANALYZE не воспроизводится.
  • Функции не проверяют владение таблицей и привилегии штатных TRUNCATE и ANALYZE; доступ нужно ограничивать правом EXECUTE.
  • Локальная статистика сохраняется между транзакциями одного backend, но исчезает при завершении соединения.
  • После DML, зафиксированного без fasttrun_analyze(), изменение распределения той же величины может остаться незамеченным. После каждого заполнения вызывайте fasttrun_analyze().
  • Локальные планы сбрасываются только в текущем backend. Глобальный ResetPlanCache не вызывается.
  • Отсутствующая таблица для основных функций считается допустимым случаем: функция ничего не делает.
  • fasttrun не является границей безопасности между клиентами пулера.

Производительность

Результат зависит от размера таблиц, числа столбцов и индексов, скорости хранилища, настроек PostgreSQL и размера кеша планов.

Основные ожидаемые эффекты:

  • fasttruncate() избегает общих сообщений об изменении файлов в стандартном режиме;
  • fasttrun_analyze() не пишет pg_class и pg_statistic;
  • повторный analyze внутри одной транзакции обычно не сканирует таблицу;
  • invalidate_threshold сокращает лишние обходы локального кеша планов;
  • запросы без временных таблиц не меняют планы и результаты.

Проверка check-no-temp-impact сравнивает планы, результаты, повторное планирование и память для запросов без временных таблиц. После активации локальных кешей вход в хук планировщика имеет небольшую, но не нулевую стоимость. В измеренном сценарии она составляла около 0,26 мкс на планирование. Это результат конкретного теста, а не универсальная гарантия.

Синтетический тест находится в sql/fasttrun_bench.sql. Для своей системы используйте реальные планы и нагрузочные тесты.

Пример наблюдаемого эффекта на одном из рабочих кластеров:

CPU до fasttrun

CPU после fasttrun

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

Тестирование

Развернуть команды и устройство тестов

Базовый набор:

make installcheck PG_CONFIG=/path/to/pg_config

Он содержит 13 наборов pg_regress. На поддерживаемых PostgreSQL 16, PostgreSQL 17 и PostgreSQL 18 ожидается результат 13/13.

Расширенный локальный набор:

make check-deep-local PG_CONFIG=/path/to/pg_config

Он не заменяет полный обязательный pre-release прогон. Перед релизом и в ночном задании используется:

FT_CASSERT_TARGETS="16:/path/pg16/bin/pg_config:port:log,..." \
  scripts/check_fasttrun_prerelease.sh

Обязательный набор включает cassert-сборки PostgreSQL 16/17/18, 13/13 pg_regress, BRIN stress, cache-init faults, fault matrix, проверки памяти, планировщика и локальных инвалидаций.

Fault matrix выполняет 50 сценариев на 25 точках отказа. Короткий scripts/check_fasttrun_brin_stress.sh делает 96 обычных и 48 принудительных проверок планирования. Полный pre-release режим выполняет 1000 обычных и 200 принудительных проверок BRIN-пути на каждой версии PostgreSQL.

Полезные отдельные проверки:

make check-parity PG_CONFIG=/path/to/pg_config
make check-no-temp-impact PG_CONFIG=/path/to/pg_config
make check-zero-sinval PG_CONFIG=/path/to/pg_config
make check-fault-matrix PG_CONFIG=/path/to/pg_config
make check-brin-stress PG_CONFIG=/path/to/cassert/pg_config
make check-tracking-persistence PG_CONFIG=/path/to/pg_config
make check-required-suite
make check-docs

Сохранение файла учёта проверяет scripts/check_fasttrun_tracking_persistence.sh.

make check-zero-sinval — отдельная Linux-проверка через gdb. Она считает вызовы отправки общих сообщений и подтверждает, что основной путь fasttrun их не создаёт. Каталожный тест из pg_regress не заменяет эту send-side проверку. Она не входит в scripts/check_fasttrun_prerelease.sh, поэтому перед релизом её нужно запускать отдельно на подходящем Linux-сервере.

Внутреннее устройство

В стандартном режиме fasttruncate() удаляет файлы локальной временной таблицы и создаёт пустые заново. Локальные буферы принадлежат только текущему backend, поэтому другим процессам не нужно сообщать об изменении этих файлов. Индексы, TOAST и их метаданные перестраиваются в том же backend.

fasttrun_analyze() хранит оценки размера и статистику столбцов в памяти backend. Хуки планировщика подставляют эти значения вместо старых каталожных значений. Общий ResetPlanCache не используется; при необходимости планы помечаются устаревшими только в текущем backend через PlanCacheRelCallback. Хуки статистики устанавливаются лениво при первом создании локального кеша, поэтому порядок fasttrun в shared_preload_libraries его не определяет.

Основные функции в стандартном режиме не изменяют pg_class, pg_statistic и relfilenode целевой таблицы. Предварительное создание таблиц — обычный DDL и не входит в этот контракт.

Совместимость

PostgreSQL Сборка Базовые тесты
PostgreSQL 16 да 13/13
PostgreSQL 17 да 13/13
PostgreSQL 18 да 13/13

Файловая структура

fasttrun.c                    # основной C-код
fasttrun.control              # метаданные расширения
extension/                    # SQL-файлы установки и обновления
Makefile                      # сборка через PGXS
examples/                     # примеры интеграции
scripts/                      # проверки и pre-release runner
sql/                          # 13 наборов pg_regress
expected/                     # ожидаемый вывод pg_regress

About

PostgreSQL extension for non-transactional fast truncation + fast analyze of temporary tables without pg_class bloat and sinval

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages