Greenplum secrets🎩
698 subscribers
38 photos
8 videos
10 files
67 links
The channel about best practice coding for Greenplum / Канал о том как писать оптимальный код в Greenplum. by @smartyru
Download Telegram
Ваша средняя з.п в уходящем 2024 на руки(продолжение).Your netto average salary in the outgoing 2024( part 2) !
Anonymous Poll
63%
< 600 000 (RUB)
0%
< 650 000
0%
< 700 000
1%
< 750 000
0%
< 800 000
0%
< 850 000
0%
< 900 000
1%
< 950 000
7%
< 1 000 000
28%
Я стейкхолдер компании Arenadata ( I am owner of Arenadata PJSC)
Дорогие друзья! Ввиду того, что пришло время охладиться ( канал уходит в отпуск до 10.01.25), опрос года - рабочий вопрос! __Dear all! Since it's time to cool down (the channel is going on vacation until 10.01.25), the last poll in this year (still regarding business stuff)
Опровержение
Т.к. я обещал завязать с секретами на время отпуска канала, то просто хотел бы поделиться результатами одного эксперимента.
@andreikapolin на канале Greenplum Russia сделал вчера сногсшибательное заявление, цитирую

"Есть партицированная табличка по дате, партиции делаю только с 2022, все что до, в дефолтной
Получается в таблице есть история 3 млрд строк, все лежит в дефолтной
Начинаю подгружать 300 миллионов строк и создавать для них партиции, подвисает, с чем может быть связаны?"

Т.к. меня это заинтриговало, хоть и считаю, что в default партиции не должно быть многолюдно, решил проверить сие утверждение.
Создадим табл-у, куда зальем 3 ярда в дефолтную партицию:
CREATE TABLE tst1
(id INT,
order_date DATE
)
WITH (appendoptimized=true, orientation=column, compresstype=ZLIB, compresslevel=1)
DISTRIBUTED BY(id)
PARTITION BY RANGE(order_date)
(START(date '2022-01-01') INCLUSIVE
END(date '2023-01-01') EXCLUSIVE
EVERY(INTERVAL '1 month'),
DEFAULT PARTITION other);


Синтетика для записи в дефолтную партицию (с пустой датой) :
create table smpl_1m WITH (appendonly = true, orientation = column, compresstype = zstd, compresslevel = 1)
as
select generate_series(1, 1e6::int) id, null::date distributed by(id);

insert into smpl_1m
select s.* from smpl_1m s
join (select generate_series(1,3000)) a
on 1=1

Чек числа строк:
select count(*) from smpl_1m; -- 3 001 000 000

Запишем данные в дефолтную партицию:
insert into tst1
select * from smpl_1m;


Теперь смоделируем шаг записи 300 млн строк в партиции, отличные от дефолта ( которых состоят из 12 месяцев 2022 г. ):
insert into tst1
select id, '2022-01-01'::date + mod(id, 365)
from smpl_1m
limit 300e6::int;

300,000,000 rows affected in 2 m 27 s 313 ms


Теперь создадим таблицу tst2, идентичную tst1 ( скрипт не привожу, чтобы не мусорить - он такой же как для tst1 ) и запишем 300 млн строк
insert into tst2
select id, '2022-01-01'::date + mod(id, 365)
from smpl_1m
limit 300e6::int;

300,000,000 rows affected in 2 m 27 s 203 ms


Вывод: Исходя из имеющихся вводных - вывод автора не подтвержден. Время совпало с точностью до секунды.

Возмоэжно, у @andreikapolin был другой сценарий и если он прочтет этот пост, поделится, в чем разница.
👍2
Полезные заметки о PXF или тонкости интеграции с Hive

Друзья, да здравствет день вечного студента, 25 янааря, а значит пора за уроки!
Вот несколько, которые я извлек при попытке прорубить окно в Hadoop из Greenplum,
тем более, что ответы местами не гуглились, а были обнаружены в нейросетке просветленных коллег.
Зарублю ка на носу тут, на память!

Урок 1.
Так бывает, что в DEV контуре версия либок отстает от прод, и закон Мерфи тут как тут: "Если что-то может пойти не так, оно пойдет не так"!
Обновили как-то в Hadoop сборку, создали с нуля тест табл-у, создали профиль hive в GP, чтобы ее прочитать по PXF.
Создали внешнюю таблицу в GP, запрос к которой рвет с ошибкой
PXF server error : Can not read value at 0 in block -1 in file ...

Вскрытие показало, что в новой конфигурации Hadoop таблица Hive создана в Parquet со сжатием по дефолту zsdt - не поддерживаемый кодек на старой сборке PXF.

Решение:
Указать в DDL Hive в TBLPROPERTIES 'parquet.compression' = 'SNAPPY'

Урок 2.
Создали writable external table в GP, через которую попытались записать 1 млн строк в Hive через jdbc в рамках одной транзакции.
Запрос кончил с ошибкой:
ERROR: PXF server error : Failed to obtain secured JDBC connection
java.sql.SQLTransientConnectionException: HikariPool-74 - Connection is not available, request timed out after 30000ms


Однако, в Hive таблице появилось 130k строк.
Ошибку с ходу не нагуглил, но убедился, что Hive это не про ACID(atomicity, consistency, isolation, durability) и о консистентности транзакций
речи быть не может.

Урок 3.
Не факт, что чтение и запись из/в Hive по PXF через jdbc имеют равные шансы на успех.
Попытался записать данные в Hive через
CREATE WRITaBLE EXTERNAL TABLE ext_hive_jdbc_snappy_w(
"bool" boolean,
"int4" int4,
"int2" int2,
"int_tiny" int2,
"int64_col" int8,
"float_col" float4,
"double_col" float8,
"json_col" text,
"col_binary" bytea,
"timestamp_micros" timestamp)
location ('pxf://...?PROFILE=jdbc&SERVER=...') on all format 'custom' ( formatter='pxfwritable_export' ) encoding 'utf8';

ERROR: PXF server error : Method not supported
Если исключить из insert-а поле с типом bytea, запись выполняется без ошибок.
Да, конечно в Hive через jdbc писать не стоит, для этого есть Spark, но все же неожиданно, на фоне того, что чтение поля binary этой же табл-ы Hive по PXF идет
без проблем, как и должно быть согласно документации (слайд для маппинга под постом )

Решение не найдено.

Урок 4.
Внимательный подписчик заметит, что в маппинге 3 урока нет типа DATE. Спешу вас успокоить, такой необходимый тип таки поддерживается в Hive табл-е,
но в нашей версии PXF сборки 16.1, чтобы прочитать его, также как и timestamp пришлось в DDL внешней табл-ы в GP добавить недокументированную опцию date_wide_range=false,
иначе select полей с этими типами из внешки (PXF external table) стошнит в
ERROR: PXF server error : Illegal conversion
👍51🔥1
Useful notes on PXF or nuances of integration with Hive /*English version of the previous news*/

Friends, long live the day of the eternal student, January 25, which means it's time for lessons!
Here are a few ones that I learned while trying to cut a window into Hadoop from Greenplum,
especially since the answers in some places were not googled, but were found in the neural network of my enlightened colleagues.
I'll write this down for memory!

Lesson 1.
It happens that in the DEV sandbox the version of the binaries lags behind the production, and Murphy's law is right there: "If something can go wrong, it will go wrong"!
We updated the Hadoop assembly somehow, created a test table from scratch, created a hive profile in GP to read it via PXF.
Created an external table in GP, ​​the query to which breaks with the error
PXF server error: Can not read value at 0 in block -1 in file ...

The analysis revealed that in the new Hadoop configuration, the Hive table was created in Parquet with default compression zsdt - an unsupported codec on the old PXF build.

Solution:
Specify in the Hive DDL in TBLPROPERTIES 'parquet.compression' = 'SNAPPY'

Lesson 2.
Created a writable external table in GP, ​​through which they tried to write 1 million rows to Hive via jdbc within a single transaction.
The query ended with an error:
ERROR: PXF server error : Failed to obtain secured JDBC connection
java.sql.SQLTransientConnectionException: HikariPool-74 - Connection is not available, request timed out after 30000ms


However, 130k rows appeared in the Hive table.
I didn't find the error right away, but I was convinced that Hive is not about ACID (atomicity, consistency, isolation, durability) and there can be no talk of transaction consistency.

Lesson 3.
It is not a fact that reading and writing from/to Hive via PXF via jdbc have equal chances of success.
I tried to write data to Hive via
CREATE WRITaBLE EXTERNAL TABLE ext_hive_jdbc_snappy_w(
"bool" boolean,
"int4" int4,
"int2" int2,
"int_tiny" int2,
"int64_col" int8,
"float_col" float4,
"double_col" float8,
"json_col" text,
"col_binary" bytea,
"timestamp_micros" timestamp)
location ('pxf://...?PROFILE=jdbc&SERVER=...') on all format 'custom' ( formatter='pxfwritable_export' ) encoding 'utf8';

and fall in
ERROR: PXF server error : Method not supported
If you exclude the bytea type field from the insert, the write is performed without errors.
Yes, of course, you shouldn't write to Hive via jdbc, there is Spark for that, but still, it's unexpected, given that reading the binary field of the same Hive table via PXF goes
without problems, as it should be according to the documentation (slide for mapping given above the post)

No solution found.

Lesson 4.
An attentive subscriber will notice that there is no DATE type in the mapping of lesson 3. I hasten to reassure you, such a necessary type is supported in the Hive table,
but in our version of PXF build 16.1, to read it, as well as timestamp, we had to add an undocumented option date_wide_range=false to the DDL of the external table in GP,
otherwise, the select of fields with these types from the external table will throw up in
ERROR: PXF server error : Illegal conversion
🔥1
Друзья, не сочтите за спам. Т.к. я уверен в несокрушимом потенциале технологий с открытым исходным кодом, а крипта - яркий пример его реализации на благо цивилизации,
то ее некоторые успехи будут освещаться здесь
Friends, please don't consider this as spam. Since I am confident in the indestructible potential of open source technologies, and crypto is a shining example
of its implementation for the benefit of civilization, some of its successes can be found here
👎21
Секрет 27 ( И снова про перестановку слагаемых или почему 2 <> 1+1 )
Secret 27 (And again about the permutation of terms or why 2 <> 1+1 )
На днях обнаружил рассылку от высокого начальника с вопросом, цитирую
"
Таблица X
Расхождение в объемах более 10 %
ПРОД 897,35 GB
DR 1016,38 GB

Расхождение на момент сверки по количеству строк менее 0,01%
Структура объектов и дистрибьюция совпадают.
Такие ситуации на многих объектах.

Есть объяснение такому расхождению?
"

Я был заинтригован, и проверил все вплоть до типов сжатия полей, т.к. иногда их добавляют без сжатия, полагая, что zstd будет унаследовано от самой таблицы, что не так, как мы знаем из ч.2 секрета 15.
Отличий в этой лист-партицированной AOCO табл-е из 109 млрд строк, разбитой на 6 секций, каждая из 3х bigint и 3х text полей, не нашел, кроме факта озвученного в вопросе - каждая партиция на проде имела недовес ~10% в сравнении с DR

Подумав про VACUUM, который согласно pg_stat_all_tables никогда не делался на обоих контурах, дал рекомендацию сделать оный, который должен вымести потенциальные зомби строки, появившиеся после update/delete, и проверить выровнялся ли размер.

Но вот о чем я не подумал, вернувшись из отпуска, что у нас на DR и PROM число сегментов отличается почти в 3 раза , и это и была истинная причина разницы в объеме.
Secret 27 (And again about the permutation of terms or why 2 <> 1+1 )
The other day I found a mailing from a high-ranking boss with a question, I quote
"
Table X
Volume discrepancy is more than 10%
PROD 897.35 GB
DR 1016.38 GB

Discrepancy at the time of reconciliation by the number of lines is less than 0.01%
Object structure and distribution are the same.
Such situations occur at many objects.

Is there an explanation for this discrepancy?
"

I was intrigued, and checked everything down to the types of field compression, because sometimes they are added without compression,
assuming that zstd will be inherited from the table itself, which is not the case, as we know from part 2 of secret 15.
I did not find any differences in this leaf-partitioned AOCO table of 109 billion rows, divided into 6 sections, each of 3 bigint and 3 text fields, except for the fact voiced in the question - each partition on production had a shortfall of ~10% compared to DR

Having thought about VACUUM, which according to pg_stat_all_tables was never done on both servers, I recommended doing one, which should sweep out potential zombie rows that appeared after update/delete, and check whether the size has aligned.

But what I didn't think about when I returned from vacation is that the number of segments on DR and PROD differs by almost 3 times, and this was the real reason for the difference in volume.

Стало интересно, насколько влияет эта разница.
Создал тривиальный тест из 1 колоночной табл-ы в 1 млн строк.
I became interested in how much this difference affects.
I created a trivial test from a 1-column table with 1 million rows.

create table tst_1m   WITH (appendonly=true,orientation=column,compresstype=zstd,compresslevel=1)
as select generate_series(1,1000000) n distributed by(n);

Размеры табл-иц, на PROD:
Tables size, on PROD:
SQL> select pg_relation_size( 'tst_1m' )
pg_relation_size
-----------------------------------------
3 245 088


на DR:
DR:
SQL> select pg_relation_size( 'tst_1m' )
pg_relation_size
-----------------------------------------
3 166 432


Забавно, что в реальной табл-е картина обратна тесту на синтетике выше, т.е. при увеличении числа сегментов в 3 раза, размер табл-ы на пром уменьшился, но суть, думаю, ясна.
It's funny that in the real table the picture is the opposite of the test on synthetics above, i.e. with a 3-fold increase in the number of segments, the size of the table on the industrial scale decreased, but the essence, I think, is clear.
🤔3🔥1
Срочные новости о нашумевшем ИИ
Не подтверждено, но сообщается, что Wiz Research обнаружили общедоступную базу данных ClickHouse, принадлежащую DeepSeek, где хранится крайне конфиденциальная информация, включая секретные ключи, текстовые сообщения чата, сведения о бэкэнде и журналы.
Breaking news
It is not confirmed, but Wiz Research has reportedly discovered a publicly accessible ClickHouse database owned by DeepSeek that contains highly sensitive information including secret keys, chat text messages, backend details, and logs.
😱3
Пятничный квиз
Friday quizz
Вы создали таблицу X в PostgreSQL, после чего выполнили команду
Что не будет удалено автоматически ?
Варианты ответа ниже
You created a table X in PostgreSQL, then executed the command
What will not be deleted automatically

Answer options below

create table x (
id integer unique default nextval('my_seq') ); --создание таблицы // create table

alter table x drop column id cascade; -- команда // command
Greenplum secrets🎩
Варианты ответа // Answer options
Поздравляю всех, кто ответил "Последовательность"!
Congratulations to all those whose answer was Sequence - that's right!
👍1
В программе обещают про Greenplum, я зарегался
Forwarded from Lyubov Medvedeva
Делюсь конфой, если кому будет интересно) за качество одного доклада точно ручаюсь, будет выступать мой близкий друг)

https://rshb.sk.jugru.org/
🔥5
Секрет 28 ( Massacre или опять BDSM на мелких таблицах )
Secret 28 (Massacre or BDSM on small tables again)
2 дня охотился за запросом : вчера не было данных во временных табл-ах Y и Z,
сегодня повезло - "поймал данные" и зверушку в ловушку.
Вчера и сегодня запрос падал через 1ч20м на проде со спиллом 37 TB
И если вчера в табл-е Y было 22 млн строк, то сегодня 0.
В Z же вчера и сегодня 3 строки DISTRIBUTED RANDOMLY
Таким образом, Обе оказались без собранной статистики ввиду настроек gp_autostats_mode_in_functions = on_change и gp_autostats_on_change_threshold > 1 млн
и опущенного по умолчанию флага принудительного сбора статы внутри CDC ф-ии.
I've been hunting for a query for 2 days: yesterday there was no data in temporary tables Y and Z,
I was lucky today - I "caught the data" and the animal in a trap.
Yesterday and today the query crashed after 1 hour 20 minutes on production with a 37 TB spill
And if yesterday there were 22 million rows in table Y, today there are 0.
In Z, yesterday and today there are 3 rows DISTRIBUTED RANDOMLY
Thus, both ended up without collected statistics due to the settings gp_autostats_mode_in_functions = on_change and gp_autostats_on_change_threshold > 1 million
and the forced stats collection flag inside the CDC function that was omitted by default.

Дичь: Wild beast
select t1.id,
t1.collection_id,
t1.c_group_prop::text,
coalesce(t1.c_date_beg, '1900-01-01'::date) as c_date_beg,
t1.effective_date,
row_number()
over (partition by t1.c_group_prop, t1.collection_id, t1.effective_date order by t1.id desc) as prop_rn
from (select a.*
from rdv.mart_properties a
join (select b.properties_rk, b.effective_date, max(b.version_id) version_id
from rdv.mart_properties b
where (b.version_id between :1 and :2)
group by b.properties_rk, b.effective_date) c
using (properties_rk, effective_date, version_id)
where 1=1
and a.version_id between :1 and :2) t1
inner join Y t2 on t1.collection_id = t2.collection_id
where 1 = 1
and t1.c_group_prop::text in
(select property_id from Z)

Вскрытие показало, что проблема была именно в справочнике с фиксированным наполнением Z, а не в вот этой вот всей сложной аналитике в основной части как подумали 70% из вас ( проваливших прошлый тест -) )
Будь у Z либо корректный хэш (property_id), либо собранная статистика, либо и то и то, то запрос выполнится за секунды,
при том что в <rdv.mart_properties between :1 and :2> 3 млрд строк.
Забавно, что сегодня после inner join в итоге на in фильтр должно было придти 0 строк, но это не спасло.
Так пусть же ваши запросы всегда бьют точно в цель, и на работе, и в жизни.
Не стесняйтесь мелочиться и быть занудой, похоже это и есть путь к успеху.

The investigation found out that the problem was in the reference table with fixed content Z, and not in all this complex analytics in the main part, as 70% of you thought (who failed the last test -) )
If Z had either a correct hash (property_id), or collected statistics, or both, then the query would be executed in seconds,
given that in <rdv.mart_properties between :1 and :2> there are 3 billion rows.
It's funny that today after inner join 0 rows should have come to the in filter, but it didn't save.
So let your queries always hit the mark, both at work and in life.
Don't be shy about being petty and boring, it seems that this is the path to success.
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
Тот же секрет картинкой - Валентинкой -) Всем добра!
👍2🐳1
Секрет 29 ( Красиво не значит правильно )
Secret 29 (Beautiful does not mean correct)

Давно я вас не утомлял планами! Подвезли новые,
Часто при построении отчета надо передать параметры в запрос: кто-то материализует их в отдельную табл-у, которую встраивает в запрос, что работает не так быстро, как хар-код, а кто-то умудряется из хард-кода сделать "произведение искусства".
Одно из таких попалось на той неделе, где автор обернул параметры в подзапрос, видимо интуитивно полагая, что
это будет работать так же хорошо как и явный хардкод параметров, но не все так просто.

Выяснилось, что в зависимости от структуры табл-ы, из которой читаем данные, можно получить либо Broadcast,
либо Redistribute, как увидим ниже.
It's been a while since I've bothered you with query plans! We've brought new ones,
Often when building a report, you need to pass parameters to a query: someone materializes them in a separate table, which is embedded in the query,
which does not work as fast as a hard code, and someone manages to make a "work of art" out of a hard code.
One of these came across last week, where the author wrapped the parameters in a subquery, apparently intuitively believing that
this would work as well as an explicit hard code of parameters, but it's not that simple.

It turned out that depending on the structure of the table from which we read the data, you can get either Broadcast,
or Redistribute, as we'll see below.


По традиции создадим синтетику, без ключа и без статы:
As usual, let's create synthetics, without a key and stats:
create table tst_1k_nk_dat_no_stat2   WITH (appendonly=true,orientation=column,compresstype=zstd,compresslevel=1)
as select (generate_series(1,1000))::text n_txt;

и выполним запрос: and run query
explain analyze
with data_batch as (
select rn::int8 rn, mdmId::text mdmId, report_dt::date report_dt, report_from_dt::date report_from_dt, report_to_dt::date report_to_dt
from (
values
(0, 10, '2024-01-01', '2023-01-01', '2023-12-31')
) t(rn, mdmId, report_dt,report_from_dt, report_to_dt)
)
select b.rn
,b.report_from_dt
,b.report_to_dt
,b.report_dt
,s0.*
from tst_1k_nk_dat_no_stat2 s0
join data_batch b
on b.mdmId = s0.n_txt

Получили Broadcast горемычный в плане, где тиражируется не 1 строка с параметрами а данные табл-ы:
We received a poor Broadcast where data from table are replicated:
Gather Motion 348:1  (slice2; segments: 348)  (cost=0.00..431.00 rows=1 width=28) (actual time=21.969..29.806 rows=1 loops=1)
-> Hash Join (cost=0.00..431.00 rows=1 width=28) (actual time=18.992..20.031 rows=1 loops=1)
Hash Cond: (((column2)::text) = n_txt)
" Extra Text: (seg152) Hash chain length 1.0 avg, 2 max, using 999 of 524288 buckets."
-> Result (cost=0.00..0.00 rows=1 width=28) (actual time=0.341..0.341 rows=1 loops=1)
-> Result (cost=0.00..0.00 rows=1 width=32) (actual time=0.267..0.267 rows=1 loops=1)
One-Time Filter: (gp_execution_segment() = 152)
-> Result (cost=0.00..0.00 rows=1 width=32) (actual time=0.041..0.041 rows=1 loops=1)
-> Hash (cost=431.00..431.00 rows=1 width=8) (actual time=16.519..16.519 rows=1000 loops=1)
-> Broadcast Motion 348:348 (slice1; segments: 348) (cost=0.00..431.00 rows=1 width=8) (actual time=0.048..16.214 rows=1000 loops=1)
-> Seq Scan on tst_1k_nk_dat_no_stat2 (cost=0.00..431.00 rows=1 width=8) (actual time=0.093..0.098 rows=9 loops=1)
Planning time: 17.846 ms
(slice0) Executor memory: 447K bytes.
" (slice1) Executor memory: 172K bytes avg x 348 workers, 172K bytes max (seg1)."
" (slice2) Executor memory: 4306K bytes avg x 348 workers, 4307K bytes max (seg0). Work_mem: 32K bytes max."
Memory used: 311296kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 101.132 ms


Уменьшим энтропию, задав ключ: Let's reduce the entropy by setting the key:
alter table tst_1k_nk_dat_no_stat2 set distributed by(n_txt)

и выполним запрос снова.
Получим оптимальный план, который ждали:
We get the optimal plan:
Gather Motion 348:1  (slice1; segments: 348)  (cost=0.00..431.00 rows=1 width=28) (actual time=5.109..5.150 rows=1 loops=1)
-> Hash Join (cost=0.00..431.00 rows=1 width=28) (actual time=2.458..3.223 rows=1 loops=1)
Hash Cond: (((column2)::text) = n_txt)
" Extra Text: (seg26) Hash chain length 1.0 avg, 1 max, using 8 of 524288 buckets."
-> Result (cost=0.00..0.00 rows=1 width=28) (actual time=0.105..0.105 rows=1 loops=1)
-> Result (cost=0.00..0.00 rows=1 width=28) (actual time=0.041..0.041 rows=1 loops=1)
-> Result (cost=0.00..0.00 rows=1 width=32) (actual time=0.016..0.016 rows=1 loops=1)
-> Hash (cost=431.00..431.00 rows=1 width=8) (actual time=0.065..0.065 rows=8 loops=1)
-> Seq Scan on tst_1k_nk_dat_no_stat2 (cost=0.00..431.00 rows=1 width=8) (actual time=0.058..0.061 rows=8 loops=1)
Planning time: 12.860 ms
(slice0) Executor memory: 427K bytes.
" (slice1) Executor memory: 4314K bytes avg x 348 workers, 4316K bytes max (seg0). Work_mem: 1K bytes max."
Memory used: 311296kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 11.493 ms

Вернем табл-у в исходное, но соберем стату:
Let's return the table to its original state, but collect the stats:
alter table tst_1k_nk_dat_no_stat2 set distributed randomly;
analyze tst_1k_nk_dat_no_stat2;


В результате исходного запроса попали на Redistribute:
The original query switched to Redistribute:
Gather Motion 348:1  (slice2; segments: 348)  (cost=0.00..431.05 rows=1000 width=23) (actual time=16.175..19.786 rows=1 loops=1)
-> Hash Join (cost=0.00..431.00 rows=3 width=23) (actual time=12.145..13.017 rows=1 loops=1)
Hash Cond: (((column2)::text) = n_txt)
" Extra Text: (seg26) Hash chain length 1.0 avg, 1 max, using 8 of 524288 buckets."
-> Result (cost=0.00..0.00 rows=1 width=28) (actual time=0.182..0.183 rows=1 loops=1)
-> Result (cost=0.00..0.00 rows=1 width=28) (actual time=0.091..0.091 rows=1 loops=1)
-> Result (cost=0.00..0.00 rows=1 width=32) (actual time=0.031..0.031 rows=1 loops=1)
-> Hash (cost=431.00..431.00 rows=3 width=3) (actual time=11.607..11.607 rows=8 loops=1)
-> Redistribute Motion 348:348 (slice1; segments: 348) (cost=0.00..431.00 rows=3 width=3) (actual time=5.425..11.602 rows=8 loops=1)
Hash Key: n_txt
-> Seq Scan on tst_1k_nk_dat_no_stat2 (cost=0.00..431.00 rows=3 width=3) (actual time=0.081..0.086 rows=8 loops=1)
Planning time: 12.717 ms
(slice0) Executor memory: 447K bytes.
" (slice1) Executor memory: 172K bytes avg x 348 workers, 172K bytes max (seg0)."
" (slice2) Executor memory: 4226K bytes avg x 348 workers, 4235K bytes max (seg260). Work_mem: 1K bytes max."
Memory used: 311296kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 63.870 ms


Почему в первом варианте есть One-Time Filter: (gp_execution_segment() = 152) для генерации параметров, а в последнем - нет, не понял, но лучше избегать такого подхода, когда кортеж констант базируется в СТЕ.
Why the first option has One-Time Filter: (gp_execution_segment() = 152) for generating parameters, but the last one doesn't, I don't understand, but it's better to avoid such approach when the constant tuple is based on CTE.
👏3
Секрет 30 (Девиз GUCCI) Secret 30 (GUCCI motto)

Сегодня на проме попалась PL/pgSQL ф-я на 1k строк, которая упорно давала осечку с ошибкой workfile per query size limit exceeded
, генеря каждый раз спилл более 30 TB.

Я прогнал ее под своей учеткой в песочнице и вдруг она сразу отработала без спилла за 10 мин.
Я всегда явно выставляю на мою сессию принудительный сбор статы через set gp_autostats_mode_in_functions = on_no_stats;

Т.к. данный параметр в оригинальной ф-ии установлен не был, я решил проверить, берется ли он из GUC для тех учеток, которые используются в даге (DAG – Directed Acyclic Graph) где выполняется ф-я.
=========================================================================
Today I came across a PL/pgSQL function on 1k lines on PROD, which stubbornly misfired with the error workfile per query size limit exceeded, generating a spill of more than 30 TB each time.
I ran it under my account in the sandbox and suddenly it immediately worked without a spill in 10 minutes.
I always explicitly set forced stats collection for my session via set gp_autostats_mode_in_functions = on_no_stats;
Since this parameter was not set in the original function, I decided to check whether it is taken from GUC for those accounts that are used in the DAG (DAG – Directed Acyclic Graph) where the function is executed.

Запрос
select u.usename, * from pg_db_role_setting x, pg_database d, pg_user u
where x.setdatabase = d.oid
and x.setrole = u.usesysid
and u.usename in ('dtpl-cdm-etl-tec', 'dtpl-cdm-ddl-tec');

выявил, что параметр сбора статистики задан в профиле GUC только для для УЗ dtpl-cdm-etl-tec:
{gp_autostats_mode=on_change,gp_autostats_mode_in_functions=on_change,gp_autostats_on_change_threshold=100000}

А т.к. в этой ф-ии на 100% используются CTAS запросы, т.е. DDL по сути, то выполнялась она под dtpl-cdm-ddl-tec, а значит для нее действует глобальный параметр БД,
который выдает show all, а он запрещает сбор статы (gp_autostats_mode_in_functions=none).
Ну а что бывает с запросами, которые join-ят табл-ы без статы думаю я уже вам все уши прожужжал - с большой вероятностью как фишка ляжет.

Таковы последствия нарушения принципа GUC Continuous Integration - что то не доехало до конфигурации учетки, в итоге - ничего не поехало.

Если верить книге Во имя Гуччи, основатель легендарного дома моды сказал: "Качество помнят еще долго после того, как забывается цена".

Гениально. и ведь это про создание кода тоже. Само написание кода это лишь малая часть затрат, и на коде, написанном на скору руку,
без отрисовки блок-схемы если хотите, без проверки сход-развала, читай настроек профиля GUC в этом случае, далеко не уедешь.
=========================================================================
The query above revealed that the statistics collection parameter is set in the GUC profile only for the dtpl-cdm-etl-tec account:
{gp_autostats_mode=on_change,gp_autostats_mode_in_functions=on_change,gp_autostats_on_change_threshold=100000}

And since this function uses 100% CTAS queries, i.e. DDL in essence, it was executed under dtpl-cdm-ddl-tec, which means that the global DB parameter is in effect for it,
which gives show all, and it prohibits the collection of stats (gp_autostats_mode_in_functions=none).
Well, what happens with queries that join tables without stats, I think I've already told you all about it - most likely it will work out.

These are the consequences of violating the GUC Continuous Integration principle - something didn't make it to the account configuration, and as a result, nothing went.

According to the book In the Name of Gucci, the founder of the legendary fashion house said: "Quality is remembered long after the price is forgotten."

Brilliant. And this is also about creating code. Writing the code itself is only a small part of the costs, and you won't get far with code written in a hurry,
without drawing a block diagram if you like, without checking the wheel alignment, read the GUC profile settings in this case.
👍61
Сорри за оффтоп-)