Содержание

Предыдущий раздел

Разделение строки на символы

Следующий раздел

Кеширование результата запроса в PL/pgSQL функции

Различия в работе volatile и stable функций

Зачем разработчику использовать stable-функции, в чём их преимущества?

Stable-функции предназначены для чтения согласованных данных, операции изменения данных в них запрещены. Однако внутри можно вызывать volatile-функции, на которых это ограничение не распространяется.

Помимо ограничений stable-функции обладают одной особенностью, которую sql-разработчики обязаны знать - запросы, выполненные непосредственно внутри этих функции, "видят" только данные, которые были зафиксированы (в том числи параллельными транзакциями) на момент вызова stable-функции. Получается, что в момент запуска stable-функции PostgreSQL как бы замораживает БД для этой функции и все запросы к базе данных происходят на одних и тех же данных. Эта особенность stable-функции очень полезна для отчетов и прочих функции, которые агрегируют данные нескольких таблиц и важно, чтобы целостность этих данных не была нарушена.

Как это работает внутри (упрощенное описание)

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

Алгоритм можно представлен в виде псевдокода:

for expression in expression_list:        # список выполняемых выражений внутри хранимой процедуры
    if not context.readonly:              # проверка, в контексте какого типа процедуры выполняется выражение
        snapshot = get_current_snapshot() # Вычисление текущего снапшота
        snapshot_stack.push(snapshot)     # Текущий снапшот кладется на стек и становится активным (стек используется для поддержки иерархических вызовов и ...)
    execute(expression)                   # Выполняется выражение
    if not context.readonly:
        snapshot_stack.pop()              # Возвращем текущий снапшот на место

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

Вложенные вызовы

Ниже рассмотрены 4 варианта вложенных вызовов хранимых процедур.

  • volatile_func -> volatile_func

    Данный вариант является самым простым - при выполнении любых выражений происходит вычисление активного снапшота

  • volatile_func -> stable_func

    На момент выполнения stable_func происходит вычисление активного снапшота, который и используется для всех выражений внутри stable-функции

  • stable_func -> stable_func

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

Предупреждение

  • stable_func -> volatile_func

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

Дополнительные материалы

Снапшоты

PostgreSQL Documentation: Function Volatility Categories

comments powered by Disqus