Цикл внутри функции выполняется как одна транзакция и считывает данные одного моментального снимка

#postgresql #plpgsql #postgresql-13

Вопрос:

Я пишу функцию для сбора строк из системного представления pg_stat_activity (PostgreSQL 13).

Внутри этой функции у меня есть простой цикл для вставки данных pg_stat_activity в таблицу журнала.

Проблема в том, что когда я запускаю функцию, она вставляет строки, действительные в первую секунду, и дублирует их позже. Похоже, функции работают как единая транзакция и получают данные из моментального снимка строк с момента первого запуска. Как я могу запустить функцию «тело» в нескольких отдельных транзакциях, чтобы получить результат за каждую секунду, или какой обходной путь я могу найти?

 CREATE OR REPLACE FUNCTION public.fn_activity(
    integer)
    RETURNS void 
    LANGUAGE 'plpgsql'
    --COST 100
    --VOLATILE PARALLEL UNSAFE
AS $BODY$
declare counter integer := 0;
begin
    while counter < $1 loop
        raise notice 'Counter %', counter;
        counter := counter   1;
        
        insert into public.pg_stat_activity_log("time",datid,datname,pid,leader_pid,usesysid,usename,application_name,client_addr,client_hostname,
        client_port,backend_start,xact_start,query_start,state_change,wait_event_type,wait_event,state,backend_xid,backend_xmin,query,backend_type)
        SELECT now()::time(0),datid,datname,pid,leader_pid,usesysid,usename,application_name,client_addr,client_hostname,client_port,backend_start,xact_start,query_start,
        state_change,wait_event_type,wait_event,state,backend_xid,backend_xmin,query,backend_type
        from pg_stat_activity;
        
        raise notice '-------';
        
        PERFORM pg_sleep(1);
   
        end loop;
        return;
END;
$BODY$;

truncate table public.pg_stat_activity_log;
select public.fn_activity(60);
 

В настоящее время у меня есть результат только для 14:53:15, но мне нужно иметь:
14:53:15
14:53:16
14:53:17
14:53:18

Спасибо!

Комментарии:

1. now()::time(0) может быть упрощено до localtime или localtime(0)

Ответ №1:

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

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

Смотрите комментарий к функции из pgstat_read_current_status in src/backend/utils/activity/backend_status.c , который копирует данные:

 /* ----------
 * pgstat_read_current_status() -
 *
 *  Copy the current contents of the PgBackendStatus array to local memory,
 *  if not already done in this transaction.
 * ----------
 */
 

Комментарии:

1. Ищу обходной путь…

2. @OlegAlekseiev: вы могли бы использовать процедуру, а затем выполнять фиксацию после каждой вставки