Dando sequência à minha pergunta sobre algumas consultas no Postgres 12 serem mais lentas do que no 11 , acho que consegui diminuir o problema. Parece que um CTE recursivo baseado em valores de função é o ponto problemático.
Consegui isolar uma consulta SQL bastante pequena que é executada significativamente mais no Postgres 12.1 do que no Postgres 11.6, como ca 150ms no Postgres 12.1 vs ca 4ms no Postgres 11.6. Consegui reproduzir o fenômeno em vários sistemas: em várias VMs no VirtualBox; via Docker em duas máquinas físicas diferentes. (Consulte o apêndice para os comandos do docker). No entanto, estranho o suficiente, não consigo reproduzi-lo em https://www.db-fiddle.com/ (não há diferença a ser vista lá, ambos são rápidos).
Agora para a consulta. Primeiro, criamos esta função simples
CREATE OR REPLACE FUNCTION public.my_test_function()
RETURNS SETOF record
LANGUAGE sql
IMMUTABLE SECURITY DEFINER
AS $function$
SELECT
1::integer AS id,
'2019-11-20'::date AS "startDate",
'2020-01-01'::date AS "endDate"
$function$;
Então, para a consulta real
WITH "somePeriods" AS (
SELECT * FROM my_test_function() AS
f(id integer, "startDate" date, "endDate" date)
),
"maxRecursiveEndDate" AS (
SELECT "startDate", "endDate", id,
(
WITH RECURSIVE prep("startDateParam", "endDateParam") AS (
SELECT "startDate","endDate" FROM "somePeriods" WHERE id = od.id
UNION
SELECT "startDate","endDate" FROM "somePeriods", prep
WHERE
"startDate" <= ("endDateParam" + '1 day'::interval ) AND ("endDateParam" + '1 day'::interval ) <= "endDate"
)
SELECT max("endDateParam") FROM prep
) AS "endDateNew"
FROM "somePeriods" AS od
)
SELECT * FROM "maxRecursiveEndDate";
O que isso realmente faz não é tão importante aqui, eu acho. O ponto importante é provavelmente que existem vários CTEs envolvidos, incluindo RECURSIVE
um.
O que eu tentei:
- Eu tentei sem
my_test_function
, ou seja, colocando os valores diretamente no primeiro CTE. Dessa forma, não houve problema algum. Corre igualmente rápido em 12 e em 11. - No Postgres 12, eu brinquei com
MATERIALIZED
, mas não consegui ver nenhum efeito. A consulta ainda é executada tão lenta quanto antes.
Não sei se isso pode realmente ser um bug do Postgres 12 (ou regressão de desempenho) ou se estou perdendo algo aqui.
Apêndice: Comandos do Docker que usei para reproduzir
Primeiro, extraia imagens de ambas as versões
docker pull postgres:12.1
docker pull postgres:11.6
Agora, execute o Postgres 12
docker run -d --name my_postgres_12_container postgres:12.1
Agora, execute a consulta
docker exec my_postgres_12_container psql -U postgres -c "
CREATE OR REPLACE FUNCTION public.my_test_function()
RETURNS SETOF record
LANGUAGE sql
IMMUTABLE SECURITY DEFINER
AS \$function\$
SELECT
1::integer AS id,
'2019-11-20'::date AS \"startDate\",
'2020-01-01'::date AS \"endDate\"
\$function\$;
EXPLAIN ANALYZE WITH \"somePeriods\" AS (
SELECT * FROM my_test_function() AS
f(id integer, \"startDate\" date, \"endDate\" date)
),
\"maxRecursiveEndDate\" AS (
SELECT \"startDate\", \"endDate\", id,
(
WITH RECURSIVE prep(\"startDateParam\", \"endDateParam\") AS (
SELECT \"startDate\",\"endDate\" FROM \"somePeriods\" WHERE id = od.id
UNION
SELECT \"startDate\",\"endDate\" FROM \"somePeriods\", prep
WHERE
\"startDate\" <= (\"endDateParam\" + '1 day'::interval ) AND (\"endDateParam\" + '1 day'::interval ) <= \"endDate\"
)
SELECT max(\"endDateParam\") FROM prep
) AS \"endDateNew\"
FROM \"somePeriods\" AS od
)
SELECT * FROM \"maxRecursiveEndDate\";
"
Pare o contêiner Postgres 12
docker stop my_postgres_12_container
Inicie o Postgres 11 para comparação
docker run -d --name my_postgres_11_container postgres:11.6
Execute a consulta no Postgres 11
docker exec my_postgres_11_container psql -U postgres -c "
CREATE OR REPLACE FUNCTION public.my_test_function()
RETURNS SETOF record
LANGUAGE sql
IMMUTABLE SECURITY DEFINER
AS \$function\$
SELECT
1::integer AS id,
'2019-11-20'::date AS \"startDate\",
'2020-01-01'::date AS \"endDate\"
\$function\$;
EXPLAIN ANALYZE WITH \"somePeriods\" AS (
SELECT * FROM my_test_function() AS
f(id integer, \"startDate\" date, \"endDate\" date)
),
\"maxRecursiveEndDate\" AS (
SELECT \"startDate\", \"endDate\", id,
(
WITH RECURSIVE prep(\"startDateParam\", \"endDateParam\") AS (
SELECT \"startDate\",\"endDate\" FROM \"somePeriods\" WHERE id = od.id
UNION
SELECT \"startDate\",\"endDate\" FROM \"somePeriods\", prep
WHERE
\"startDate\" <= (\"endDateParam\" + '1 day'::interval ) AND (\"endDateParam\" + '1 day'::interval ) <= \"endDate\"
)
SELECT max(\"endDateParam\") FROM prep
) AS \"endDateNew\"
FROM \"somePeriods\" AS od
)
SELECT * FROM \"maxRecursiveEndDate\";
"
Graças aos caras prestativos da lista de discussão pgbugs, descobri que a compilação Just-in-time (algumas informações úteis que podem ser encontradas aqui ) sendo ativada por padrão no PostgreSQL 12 foi o meu problema.
Executando minha consulta
SET jit = off;
resolveu o problema: Sem ela, minha consulta é executada rápido como deveria.