Согласно статистике, в сентябрьских коммитфестах меньше всего коммитов. Но похоже, что для релизного цикла 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




На этом пока всё. Впереди основные события ноябрьского коммитфеста.

Комментарии (0)