
Согласно статистике, в сентябрьских коммитфестах меньше всего коммитов. Но похоже, что для релизного цикла 18-й версии это не так. Много принятых патчей и много интересных новых возможностей, информацией о которых хочется поделиться.
Напомню, что самое интересное из июльского коммитфеста можно прочитать здесь: 2024-07.
Мониторинг конфликтов логической репликации
Планировщик: теперь без 10000000000
Планировщик: управление памятью и мониторинг ее использования для временного хранилища строк
Чтение буферов при сканировании индекса в обратном на��равлении
pg_upgrade: асинхронное выполнение операций в нескольких базах данных
Оптимизация соединения хешированием
Оптимизация работы с текстовыми значениями в JSON
Оптимизация умножения чисел типа numeric
Оптимизация деления чисел типа numeric
ANALYZE ONLY и VACUUM ONLY
Уточненная статистика процесса контрольной точки
pg_stat_statements: нормализация команд SET
postgres_fdw_get_connections и статус удаленного соединения
file_fdw: игнорирование ошибок преобразования форматов
Функция has_largeobject_privilege
Функции crc32 и crc32c
Клиент-серверный протокол: информирование о search_path
psql: поддержка именованных подготовленных операторов
pg_verifybackup: проверка целостности копий в формате tar
Мониторинг конфликтов логической репликации
commit: 9758174e2, edcb71258, 640178c92, 6c2b5edec
Данные в таблицах на сервере-подписчике можно изменять независимо от сервера публикации. Сервер публикации об этих изменениях не узнает. Отсюда могут возникать конфликты логической репликации. Добавляем строку в таблицу на сервере публикации, а строка с таким же первичным ключом уже есть на подписчике. Или изменяем/удаляем строку на сервере публикации, а ее уже нет на подписчике. Всего вариантов конфликтов может быть 6 и теперь они описаны в одноименном разделе главы о логической репликации: Конфликты. Стоит отметить, что не все конфликты прерывают работу логической репликации.
Кроме описания появились еще и средства для отслеживания конфликтов. Рассмотрим их на примере конфликта update_missing.
На сервере публикации и сервере-подписчике созданы таблицы следующей командой:
CREATE TABLE test(id int PRIMARY KEY);
На сервере публикации добавим строку в таблицу:
pub=# INSERT INTO test (id) VALUES (1);
А на подписчике ее сразу удалим:
sub=# DELETE FROM test WHERE id = 1;
Все готово к появлению конфликта. На сервере публикации обновим единственную строку:
pub=# UPDATE test SET id = 2 WHERE id = 1 RETURNING *;
id ---- 2 (1 row)
Обновление проходит, но оно не будет применено на подписчике, ведь там строки с id=1 нет. Логическая репликация, как и прежде, игнорирует эту команду на подписчике и продолжает работать. Но если в предыдущих версиях мы об этом даже не узнаем, то теперь есть две возможности получить детали случившегося.
Во-первых, в представлении pg_stat_subscription_stats появились счетчики для конфликтов всех типов. Обратившись к представлению, можно увидеть, что счетчик confl_update_missing уже не нулевой:
sub=# SELECT * FROM pg_stat_subscription_stats WHERE subname = 'sub'
-[ RECORD 1 ]---------------+------ subid | 16390 subname | sub apply_error_count | 0 sync_error_count | 0 confl_insert_exists | 0 confl_update_origin_differs | 0 confl_update_exists | 0 confl_update_missing | 1 confl_delete_origin_differs | 0 confl_delete_missing | 0 stats_reset |
Во-вторых, в журнале сервера есть подробная информация о конфликте:
2025-01-23 23:33:35.763 MSK [170775] LOG: conflict detected on relation "public.test": conflict=update_missing 2025-01-23 23:33:35.763 MSK [170775] DETAIL: Could not find the row to be updated. Remote tuple (2); replica identity (id)=(1). 2025-01-23 23:33:35.763 MSK [170775] CONTEXT: processing remote data for replication origin "pg_16390" during message type "UPDATE" for replication target relation "public.test" in transaction 746, finished at 0/183DC60
Примечание. Очень хотелось бы увидеть следующий логичный ход в разработке. А именно появление стратегий автоматического разрешения конфликтов, прерывающих работу логической репликации. Для таких надежд есть основания, ведь переписка разработчиков начинается в том числе с предложения реализовать две стратегии:apply(remote_apply)иskip(keep_local). Но задача отслеживания конфликтов ценна сама по себе и реализацию начали именно с неё. Ждем продолжения!
Планировщик: теперь без 10000000000
commit: e22253467, c01743aa4, 161320b4b, 84b8fccbe
Семейство параметров enable_* позволяет указать планировщику не использовать соответствующие пути выполнения запроса. Например предложим обойтись без последовательного сканирования таблицы:
CREATE TABLE t (id int); SET enable_seqscan = off; EXPLAIN (settings) SELECT * FROM t;
Однако планировщик все равно выберет последовательное сканирование, ведь у таблицы нет индексов, а значит любой вариант индексного сканирования невозможен.
В плане запроса для 17-й версии мы увидим огромную стоимость:
QUERY PLAN ------------------------------------------------------------------------ Seq Scan on t (cost=10000000000.00..10000000035.50 rows=2550 width=4) Settings: enable_seqscan = 'off' (2 rows)
К реальной стоимости узла Seq Scan добавлена запретительная константа 10_000_000_000. Добавление этой константы к стоимости последовательного сканирования сделало бы другие варианты доступа к таблице менее затратными, при их наличии. Но других вариантов нет и мы видим это огромное число.
В 18-й версии константу удалили, планировщик научился без нее учитывать значения параметров enable_*. А план запроса теперь выглядит так:
QUERY PLAN ----------------------------------------------------- Seq Scan on t (cost=0.00..35.50 rows=2550 width=4) Disabled: true Settings: enable_seqscan = 'off' (3 rows)
По-прежнему выбрано последовательное сканирование, но с реальной стоимостью, без «накруток». Строка Disabled: true говорит о том, что планировщик вынужден выбрать запрещенный узел из-за отсутствия других незапрещенных вариантов.
Если другой вариант есть, он будет использован:
CREATE INDEX ON t(id); ANALYZE t; EXPLAIN (settings) SELECT * FROM t;
QUERY PLAN ----------------------------------------------------------------------- Index Only Scan using t_id_idx on t (cost=0.12..8.14 rows=1 width=4) Settings: enable_seqscan = 'off' (2 rows)
Дополнительно заметим, что включение в команду EXPLAIN параметра settings (появился в 12-й версии) может быть очень полезным, т. к. позволяет узнать, изменение каких параметров повлияло на план запроса.
Планировщик: управление памятью и мониторинг ее использования для временного хранилища строк
commit: 1eff8279d, 53abb1e0e, 590b045c3, 97651b013, 04bcf9e19, 908a96861, 9fba1ed29, 95d6e9af0, 5d56d07ca, 40708acd6
Эта большая работа велась в рамках нескольких записей коммитфеста, отсюда и длинный список коммитов. Но их удобно описать в одном месте.
Ряд узлов плана запросов (список ниже) используют общий интерфейс для временного хранения строк (tuplestore). В этом интерфейсе произошли два важных изменения.
Во-первых, для хранения строк теперь используется отдельный контекст памяти с типом Generation, вместо более общего CurrentMemoryContext. Не вдаваясь в детали, такое изменение положительно влияет на управление памятью и скорость работы.
Во-вторых, EXPLAIN (analyze) теперь показывает тип используемой памяти (Memory или Disk) и максимальное количество выделенной памяти для узлов плана, использующих общий интерфейс. К этим узлам относятся:
- Materialize ― сохранение промежуточного набора строк для последующего многократного использования;
- CTE Scan ― для запросов с общими табличными выражениями;
- WindowAgg ― для запросов с оконными функциями;
- Recursive Union ― для рекурсивных запросов;
- Table Function Scan ― для запросов к функциям JSON_TABLE, XMLTABLE (не путать с Function Scan).
В следующем примере данные для узла Materialize поместились в оперативной памяти. Смотрим на новую строку Storage:
EXPLAIN (costs off, analyze, timing off, summary off, buffers off) SELECT * FROM airports a1 CROSS JOIN airports a2;
QUERY PLAN ---------------------------------------------------------------------- Nested Loop (actual rows=10816 loops=1) -> Seq Scan on airports_data ml (actual rows=104 loops=1) -> Materialize (actual rows=104 loops=104) Storage: Memory Maximum Storage: 35kB -> Seq Scan on airports_data ml_1 (actual rows=104 loops=1) (5 rows)
А вот для CTE в следующем примере строки не поместились в память, поэтому для их хранения использовались временные файлы:
EXPLAIN (analyze, costs off, summary off, timing off, buffers off) WITH b AS MATERIALIZED ( SELECT * FROM bookings ) SELECT * FROM b;
QUERY PLAN ------------------------------------------------------------ CTE Scan on b (actual rows=2111110 loops=1) Storage: Disk Maximum Storage: 67856kB CTE b -> Seq Scan on bookings (actual rows=2111110 loops=1) (4 rows)
Для узла WindowAgg сделаны дополнительные оптимизации при переключении на следующий раздел (PARTITION BY). Чем больше переключений разделов ― тем больше эффект. Например следующий запрос в 18-й версии работает на 12% быстрее, чем в 17-й.
EXPLAIN (analyze, costs off, summary off, timing off, buffers off) SELECT book_ref, count(*) OVER (PARTITION BY book_ref) FROM tickets;
QUERY PLAN ------------------------------------------------------------------------------------------- WindowAgg (actual rows=2949857 loops=1) Storage: Memory Maximum Storage: 17kB -> Index Only Scan using tickets_book_ref_idx on tickets (actual rows=2949857 loops=1) Heap Fetches: 0 (4 rows)
План рекурсивного запроса также показывает, сколько и какой памяти используется в узлах Recursive Union и CTE Scan.
EXPLAIN (analyze, costs off, summary off, timing off, buffers off) WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t;
QUERY PLAN ----------------------------------------------------------------- Aggregate (actual rows=1 loops=1) CTE t -> Recursive Union (actual rows=100 loops=1) Storage: Memory Maximum Storage: 33kB -> Result (actual rows=1 loops=1) -> WorkTable Scan on t t_1 (actual rows=1 loops=100) Filter: (n < 100) Rows Removed by Filter: 0 -> CTE Scan on t (actual rows=100 loops=1) Storage: Memory Maximum Storage: 20kB (10 rows)
И, наконец, узел для обработки функций JSON_TABLE, XMLTABLE.
EXPLAIN (analyze, costs off, summary off, timing off, buffers off) SELECT * FROM JSON_TABLE('{"a":1}'::jsonb, '$[*]' COLUMNS (a int));
QUERY PLAN ------------------------------------------------------------- Table Function Scan on "json_table" (actual rows=1 loops=1) Storage: Memory Maximum Storage: 17kB (2 rows)
Чтение буферов пр�� сканировании индекса в обратном направлении
commit: 3f44959f4, 1bd4bc85c, b5ee4e520, caca6d8d2, 4e6e375b0
При сканировании индекса в обратном направлении, без особой необходимости повторно блокировались ранее прочитанные буферы. Это приводило к дополнительному чтению буферов. Сравним количество буферов при прямом сканировании индекса в первом запросе и обратном сканировании индекса во втором запросе:
17=# EXPLAIN (analyze, buffers, costs off, summary off, timing off) SELECT * FROM flights ORDER BY flight_id ASC;
QUERY PLAN ----------------------------------------------------------------------- Index Scan using flights_pkey on flights (actual rows=214867 loops=1) Buffers: shared hit=3499
17=# EXPLAIN (analyze, buffers, costs off, summary off, timing off) SELECT * FROM flights ORDER BY flight_id DESC;
QUERY PLAN -------------------------------------------------------------------------------- Index Scan Backward using flights_pkey on flights (actual rows=214867 loops=1) Buffers: shared hit=4087
В 18-й версии сканирование индекса в любом направлении потребует минимального количества буферов. Для этого запроса ― 3499.
pg_upgrade: асинхронное выполнение операций в нескольких базах данных
commit: 40e2e5e92, 6d3d2e8e5, 7baa36de5, 46cad8b31, 6ab8f27bc, bbf83cab9, 9db3018cf, c34eabfbb, cf2f82a37, f93f5f7b9, c880cf258
Ряд действий утилиты pg_upgrade требует выполнения одного и того же запроса во всех базах данных обновляемого кластера. Утилита всегда выполняла эти действия одно за другим, последовательно подключаясь к каждой базе данных.
Первый коммит создает инфраструктуру для асинхронного выполнения таких задач, используя соответствующие возможности libpq. Остальные коммиты переводят отдельные задачи на использование новой инфраструктуры.
Асинхронная обработка управляется параметром --jobs, ее эффективность возрастает вместе с количеством баз данных в обновляемом кластере.
Эта работа продолжает серию оптимизаций pg_upgrade, описанную в предыдущей статье.
Примечание. Следующие четыре оптимизации примечательны тем, что для них нет никаких настроек. Просто перечисленные операции будут быстрее работать.
Оптимизация соединения хешированием
commit: adf97c156
Ускорили соединение хешированием, особенно если условие соединения по нескольким столбцам. Примерные оценки ускорения можно найти в сообщении о коммите.
Оптимизация работы с текстовыми значениями в JSON
commit: ca6fde922
Оптимизирована конвертация JSON в текст за счет использования SIMD при экранировании имен свойств и их значений. Чем длиннее текстовые значения, тем больше эффект от оптимизации. Примерные оценки ускорения можно найти в сообщении о коммите.
Оптимизация умножения чисел типа numeric
commit: ca481d3c9, c4e44224c, 8dc28d7eb
Числа типа numeric будут умножаться быстрее.
Оптимизация деления чисел типа numeric
commit: 9428c001f
Числа типа numeric будут делиться быстрее.
ANALYZE ONLY и VACUUM ONLY
commit: 62ddf7ee9
Автоочистка и автоанализ приходят только в секции секционированной таблицы, но не в саму секционированную таблицу. В ней нет строк, поэтому невозможно настроить правила срабатывания.
Но если вычищать мертвые строки в секционированной таблице не требуется, то регулярно собирать статистику может быть полезным для выполнения некоторых за��росов. Ведь часть сводной статистики собирается для самой секционированной таблицы. Обычная команда ANALYZE будет собирать статистику не только по секционированной таблице, но и по всем секциям, и это может занять много времени. А варианта с ONLY, как в командах DML, до 18-й версии не было.
Теперь сбор сводной статистики, без секций, можно выполнять так:
ANALYZE ONLY partitioned_table;
ANALYZE
То же самое для VACUUM.
VACUUM (analyze) ONLY partitioned_table;
WARNING: VACUUM ONLY of partitioned table "partitioned_table" has no effect VACUUM
Предупреждение говорит о том, что нет смысла запускать очистку секционированной таблицы без параметра analyze.
Добавление ONLY в команды VACUUM и ANALYZE коснулось не только секционированных таблиц, но и родительских таблиц с наследниками. Причем изменение несовместимо с предыдущими версиями.
Раньше команда
ANALYZE parent_table;
собирала статистику только для родительской таблицы. А теперь будет обрабатывать еще и всех наследников. Для сбора статистики только по родительской таблице нужно указать ONLY:
ANALYZE ONLY parent_table;
Аналогично для VACUUM.
Уточненная статистика процесса контрольной точки
commit: 17cc5f666
Статистику работы процесса контрольной точки можно увидеть в появившемся в 17-й версии представлении pg_stat_checkpointer и журнале сервера.
Оказалось что количество записанных буферов в этих двух источниках информации отличается. Причина в том, что сообщение в журнале сервера включает как записанные буферы общего буферного кеша (shared_buffers), так и записанные буферы SLRU, тогда как в pg_stat_checkpointer.buffers_written учитываются только буферы shared_buffers.
Чтобы статистика совпадала, сделали следующее. В журнале сервера информацию о записанных буферах разделили: отдельно указывается, сколько записано буферов из shared_buffers, и сколько записано буферов SLRU.
LOG: checkpoint complete: wrote 47 buffers (0.3%), wrote 0 SLRU buffers; …
А в представление pg_stat_checkpointer в дополнение к столбцу buffers_written добавили столбец slru_written.
Легко убедиться, что теперь информация в двух источниках совпадает.
CHECKPOINT; SELECT pg_stat_reset_shared('checkpointer');
Создаем нагрузку и сразу выполняем контрольную точку.
CREATE TABLE bookings_copy AS SELECT * FROM bookings; CHECKPOINT;
Проверяем информацию в журнале сервера и pg_stat_checkpointer:
\! tail -n 1 logfile 2024-11-12 16:56:33.443 MSK [63957] LOG: checkpoint complete: wrote 2016 buffers (12.3%), wrote 1 SLRU buffers; 0 WAL file(s) added, 5 removed, 5 recycled; write=0.044 s, sync=0.311 s, total=0.773 s; sync files=20, longest=0.226 s, average=0.016 s; distance=165531 kB, estimate=444398 kB; lsn=1/4A6A8E20, redo lsn=1/4A6A8DC8
SELECT buffers_written, slru_written FROM pg_stat_checkpointer;
buffers_written | slru_written -----------------+-------------- 2016 | 1 (1 row)
pg_stat_statements: нормализация команд SET
commit: ba90eac7a, dc6851596
Очередное продолжение работы над нормализацией команд. На этот раз речь о команде установки параметров SET. Команды с разным форматированием и разными значениями параметров будут приведены к одному виду, что поможет сократить количество хранимых запросов в pg_stat_statements.
SELECT pg_stat_statements_reset(); SET SEARCH_PATH = pg_catalog; SET search_path = public; set search_path=bookings,public; SET CUSTOM.PARAMETER = 1; SET CUSTOM.parameter = 2; set custom.parameter=42; SELECT queryid, query, calls FROM pg_stat_statements WHERE query ILIKE 'SET%';
queryid | query | calls ----------------------+---------------------------+------- -5443897209013767274 | SET CUSTOM.PARAMETER = $1 | 3 -1735366501441382531 | SET SEARCH_PATH = $1 | 3 (2 rows)
postgres_fdw_get_connections и статус удаленного соединения
commit: c297a47c5, 857df3cef, 4f08ab554
Выполним в транзакции обращение к сторонней таблице.
BEGIN; SELECT count(*) FROM remote_bookings.tickets;
count --------- 2949857 (1 row)
Сейчас удаленное соединение должно быть активно, мы можем проверить статус всех соединений функцией postgres_fdw_get_connections. А помогут сделать это три новых столбца функции: user_name, user_in_xact и closed:
SELECT * FROM postgres_fdw_get_connections(check_conn => true);
server_name | user_name | valid | used_in_xact | closed -------------+-----------+-------+--------------+-------- demo_srv | postgres | t | t | f (1 row)
Столбец user_name ― это имя локального пользователя, used_in_xact ― используется ли соединение в текущей транзакции, а closed показывает статус соединения.
Если с подключением всё в порядке, то локальную транзакцию можно продолжать. Иначе ее нужно откатывать: нет смысла что-то делать, ведь транзакция не сможет завершиться успешно из-за закрытого соединения на удаленном сервере.
file_fdw: игнорирование ошибок преобразования форматов
commit: e7834a1a2, a1c4c8a9e
В 17-й версии у команды COPY появилась возможность игнорировать ошибки преобразования форматов при загрузке строк. Для этого в команду были добавлены параметры on_error и log_verbosity. Расширение file_fdw для чтения файлов использует именно COPY, поэтому вполне логичным было добавить в расширение соответствующие возможности.
Для примера создадим вот такой файл, в котором не все строки являются числами:
$ cat /tmp/t.txt
1 два три 4
Создаем стороннюю таблицу для этого файла c целочисленным столбцом и указанием игнорировать ошибки преобразования формата:
CREATE EXTENSION file_fdw; CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw; CREATE FOREIGN TABLE ft ( id integer ) SERVER file_server OPTIONS ( filename '/tmp/t.txt', on_error 'ignore', log_verbosity 'verbose' );
Запрос к таблице работает, показывая только успешно загруженные строки и предупреждения для остальных:
SELECT * FROM ft;
NOTICE: skipping row due to data type incompatibility at line 2 for column "id": "два" NOTICE: skipping row due to data type incompatibility at line 3 for column "id": "три" NOTICE: 2 rows were skipped due to data type incompatibility id ---- 1 4 (2 rows)
Вывод предупреждений можно отключить при помощи нового значения silent параметра log_verbosity:
ALTER FOREIGN TABLE ft OPTIONS (SET log_verbosity 'silent'); SELECT * FROM ft;
id ---- 1 4 (2 rows)
Функция has_largeobject_privilege
commit: 4eada203a
Прибавление в семействе функций has_*_privilege. Теперь проверить права доступа к большим объектам можно с помощью функции has_largeobject_privilege.
\lo_import 'logfile' lo_import 24578 GRANT SELECT ON LARGE OBJECT 24578 TO public; SELECT has_largeobject_privilege('alice', 24578, 'SELECT');
has_largeobject_privilege --------------------------- t (1 row)
Важным достоинством этого семейства функций является то, что проверяются привилегии, выданные не только напрямую, но и косвенно через членство в роли, имеющей привилегии, включая членство в роли владельца объекта.
Примечание. Если сравнить список типов объектов, на которые можно выдавать привилегии, и список функций has_*_privilege, то можно заметить отсутствие функции has_domain_privilege. Всё дело в том, что домены не совсем самостоятельный тип объекта, по своей сути они являются типами и хранятся в системном каталоге pg_type. Поэтому вместо функции has_domain_privilege с до��енами прекрасно работает has_type_privilege. Но для создания доменов существует отдельная команда CREATE DOMAIN, привилегии на домены также выдаются отдельной командой GRANT… ON DOMAIN. Поэтому отсутствие функции has_domain_privilege может сбить с толку. Возможно это повод внести в документацию для функции has_type_privilege уточнение о работе с доменами.
Функции crc32 и crc32c
commit: 760162fed
Новые функции для вычисления контрольных сумм CRC-32 и CRC-32C:
SELECT crc32('42'::bytea), crc32c('42'::bytea);
crc32 | crc32c -----------+----------- 841265288 | 779959755 (1 row)
Клиент-серверный протокол: информирование о search_path
commit: 28a1121fd, 0d06a7eac
Клиент-серверный протокол работает так, что сразу после подключения клиенты получают сообщение ParameterStatus о значениях некоторых параметров, влияющих на работу приложения, например client_encoding или DateStyle. При изменении этих параметров в сеансе (обычно командой SET) серверный процесс автоматически уведомляет клиента о новых значениях.
Список параметров фиксирован, но первым коммитом в него был добавлен параметр search_path.
Изменение полезно для разработчиков драйверов, реализующих клиент-серверный протокол. Особенно для разработчиков пулеров соединений. В транзакционном режиме, когда разные клиенты обслуживаются одним серверным процессом, пулер сможет автоматически приводить в соответствие search_path на сервере с запомненным значением для текущего клиента.
psql: поддержка именованных подготовленных операторов
commit: d55322b0d
Поддержка расширенного протокола запросов в psql была добавлена в 16-й версии. Но поддерживались только неименованные подготовленные операторы.
С новыми командами \parse, \bind_named и \close появилась возможность более полноценно работать с подготовленными операторами. Вот как это выглядит:
SELECT $2||$1 AS str \parse q SELECT * FROM pg_prepared_statements \gx
-[ RECORD 1 ]---+------------------------------ name | q statement | SELECT $2||$1 AS str prepare_time | 2024-11-08 09:30:50.326934+03 parameter_types | {text,text} result_types | {text} from_sql | f generic_plans | 0 custom_plans | 0
Первая команда объявляет подготовленный оператор с именем q и двумя текстовыми параметрами. Вторым запросом можно убедиться, что подготовленный оператор появился.
Для связывания параметров с фактическими значеними и выполнения подготовленного оператора используем команду \bind_named. Сначала указываем имя подготовленного оператора, затем значения двух параметров:
\bind_named q 42 'Answer: ' \g
str ------------ Answer: 42 (1 row)
\bind_named q 'World!' 'Hello,' \g
str -------------- Hello,World! (1 row)
И, наконец, закрытие подготовленного оператора (DEALLOCATE):
\close q
pg_verifybackup: проверка целостности копий в формате tar
commit: 8dfd31290
Утилита pg_verifybackup сможет проверять целостность копии кластера не только в формате plain, но и tar. Копия в формате tar может быть сжата:
$ pg_basebackup -D backup --format=tar --gzip $ ls -l backup
total 569416 -rw------- 1 pal pal 190742 дек 24 18:57 backup_manifest -rw------- 1 pal pal 582862489 дек 24 18:57 base.tar.gz -rw------- 1 pal pal 17120 дек 24 18:57 pg_wal.tar.gz
Но есть ограничение. Утилита pg_verifybackup не умеет работать с файлами WAL, поэтому нужно явно указывать, что проверка WAL не требуется:
$ pg_verifybackup backup --format=tar --no-parse-wal
backup successfully verified
На этом пока всё. Впереди основные события ноябрьского коммитфеста.
