В статье описывается один из способов обновления дистибутива операционной системы с работающим кластером баз данных Postgres Pro Standard версии 11. В дальнейшем, для краткости, кластер баз данных Postgres Pro Standard 11 будем называть Postgrespro, Centos7 - Centos, а AlmaLinux8 - Almalinux. Способ заключается в том, чтобы настроить физическую репликацию Postgrespro между разными дистрибутивами операционной системы с последующим переводом слейва в режим мастера. При чтении множества статей о настройке физической репликации я нигде не встречал упоминание о том, что так делать нельзя. Естественно, что архитектура Centos и Almalinux должны совпадать, а так же должны совпадать мажорные версии Postgrespro.

Немного о том, что за Postgrespro переносим

  • провайдер сортировки ICU

  • размер всех баз составляет 100G

  • в среднем 3000 транзакций в секунду при 21 коннекте

  • для коннектов используется pgbouncer в режиме session (pool_mode = session)

Исходные данные

Postgrespro на сервере с Centos - рабочая база

                                                 version
---------------------------------------------------------------------------------------------------------
 PostgreSQL 11.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28), 64-bit

Postgrespro на сервере с Almalinux - чистая установка

                                                 version
---------------------------------------------------------------------------------------------------------
 PostgreSQL 11.18 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5), 64-bit

Краткая последовательность действий

  1. Настраиваем физическую репликацию между Postgrespro на Centos (мастер) и Almalinux (слейв).

  2. Стопаем клиентские коннекты в pgbouncer.

  3. Переводим слейв в режим мастера.

  4. Переключаем клиентские коннекты в pgbouncer на новый мастер.

  5. На новом мастере в каждой базе обновляем версию правил сортировки.

Описание действий

Не буду описывать как настроить физическую репликацию, т.к. в интернете много инструкций. Скажу лишь, что у нас настроена потоковая репликация + архивирование wal файлов.

У pgbouncer есть замечательная команда PAUSE для приостановки клиентских коннектов. Но она не совсем подходит (я бы сказал, что совсем не подходит) для базы с постоянными соединениями, т.к. она не отработает до тех пор, пока все активные клиентские коннекты не отключатся. Например, если открыть psql, подлючиться к базе, выполнить любой запрос и оставить psql подключенной к базе, затем (в другом терминале) подлючиться к pgbouncer и выполнить PAUSE, то она замрет в ожидании закрытия psql. В то время как PAUSE будет ждать закрытие соединения psql, новые клиенты не смогут подключаться к базе. Проблема PAUSE еще и в том, что пока она "виснет", сделать на базу KILL так же не выйдет, будет ошибка ERROR: already suspended/paused, т.е. PAUSE вообще никак не прервать - только перезапуск pgbouncer. Поэтому, мы решили стопать клиентские коннекты через KILL, как итог, получили пару седых волос, но об этом позже.

После останова клиентских коннектов ждем пару секунд (чтобы слейв догнал мастер)
и переводим слейв в режим мастера. Способов несколько, мы это делаем через создание триггерного файла. Кто-то скажет, что подождать пару секунд так себе вариант чтобы убедиться в том, что слейв догнал мастер, тогда можно выполнить запрос к pg_stat_replication на текущем мастере и убедится, что sent_lsn, write_lsn, flush_lsn, replay_lsn равны.

После того, как новый мастер готов принимать соединения, в конфиге pgbouncer меняем строки коннекта к текущим базам на новый мастер и в подключении к pgbouncer возобновляем клиентские коннекты командой RESUME.

Поскольку в дистрибутивах Centos и Almalinux разные версии библиотеки ICU, а Postgrespro отслеживает изменение версий ICU в системе, то на новом мастере при каждом коннекте к базе, показывается такое предупреждение:

WARNING:  collation "default" has version mismatch
DETAIL:  The collation in the database was created using version 58.0.6.50, but the operating system provides version 153.80.
HINT:  Check all objects affected by this collation and run ALTER COLLATION pg_catalog."default" REFRESH VERSION

Чтобы избавиться от этого предупреждения, мы написали простенький скрипт на perl, который нужно запустить на новом мастере:

#!/usr/bin/env perl

# https://postgrespro.ru/docs/postgrespro/11/sql-altercollation#SQL-ALTERCOLLATION-NOTES
# Обновляет версии провайдера сортировки ICU во всех базах.
# Запускать на всех базах кластера, кроме template0 (т.к. к ней нельзя подконнектится).
# Запускать от пользователя postgres

use strict;
use warnings;
use DBI;

my @dbs = qw/analytics log postgres template1 testdb/;

# https://edu.postgrespro.ru/dba2-13/dba2_16_admin_localization.html
my $sql = qq/
    SELECT
        c.collname,
        c.collversion AS version,
        pg_collation_actual_version(c.oid) AS actual_version
    FROM
        pg_collation c
    WHERE
        pg_collation_actual_version(c.oid) IS NOT NULL
/;

for my $dbname (@dbs) {
    print "------- $dbname -------\n";
    my $dbh = DBI->connect("dbi:Pg:dbname=$dbname", "", "", { RaiseError=>1});
    my $arr = $dbh->selectall_arrayref($sql, { Slice => {} });
    for (@$arr) {
        if ($_->{version} ne $_->{actual_version}) {
            print "$_->{collname}: $_->{version} to $_->{actual_version}\n";
            $dbh->do('ALTER COLLATION pg_catalog."'.$_->{collname}.'" REFRESH VERSION');
        }
    }
}

Хочу заметить, что у нас в Postgrespro сортировка у всех баз одинаковая en_US.UTF-8@icu и в базах не используется сортировка отличная от default. Если у Вас иначе, то читать до понимания происходящего эту и эту ссылки.

Вот казалось бы и все, задача решена, дистрибутив операционной системы заменен, новый мастер держит нагрузку, ошибок нет. Помните, ранее, я говрил о седых волосах, так вот спустя пару минут работы на новом мастере мы заметили, что порядковый номер заказов прыгнул на 31, т.е. последовательность (первичный ключ) в таблице orders была 1004304, а на новом мастере вдруг стала 1004335. Мы не поверили своим глазам, как так то, ведь перед тем, как мы перевели слейв в режим мастера мы убедились, что sent_lsn, write_lsn, flush_lsn, replay_lsn равны, т.е. мастер и слейв были в одинаковых состояниях. Взглянув на другие посследовательности на новом мастере мы обнаружили, что такой скачок произошел и в других последовательностях. Немного подумав, мы поняли, что на самом деле ничего старшного в этом нет, ну прыгнули они и прыгнули, нас напугал сам факт того, что такой скачок произошел. А может тогда еще что-то где-то сломалось? Ведь мы использовали разные версии дистрибутивов операционной системы. Если коротко, то найти ответ на вопрос о скачке последовательностей нам помог второй слейв, да у нас был второй слейв, который застыл в сотоянии когда мы отрубили коннекты от старого мастера. И вот что показал запрос на втором слейве:

# SELECT * FROM orders_id_seq;
 last_value | log_cnt | is_called 
------------+---------+-----------
    1004304 |      31 | t
(1 row)

Вся фишка в log_cnt, погуглив мы тут же наткнулись на ответ. Если коротко, то да, скачок последовательностей возможен. Прикиньте!

Выводы

  • Простой в работе занял ровно минуту.

  • Скачок последовательностей произошел из-за того, что мы остановили клиентские коннекты на pgbouncer через команду KILL.

  • Возможно (не проверяли), проблема команды PAUSE описанная в этой статье возникает только в режиме session, а в режиме transaction нет.

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


  1. CrushBy
    20.01.2023 09:55
    +1

    Кстати, а не нарывались на проблему с индексами по строкам, если там есть специфические символы ?
    Бывало, что если через физическую репликацию перегонять сервер с CentOS 7 и на CentOS 8, то все такие индексы просто начинали работать неправильно. Есть подозрение, что для сравнения строк PostgreSQL использует библиотеки ОС, и там по разному реализовано сравнение в разных ОС для некоторых символов...


    1. krpsh Автор
      20.01.2023 10:28
      +1

      Мы с таким не сталкивались, но я знаю о такой проблеме. Такие индексы нужно проверять перед миграцией. Этот момент очень хорошо освещен тут. По ссылке есть пример запроса, которым можно проверить поведение сортировки. Вот например как это выглядит у нас:

      На Centos7

      postgres@postgres=# WITH t(c) AS (
      postgres@postgres(#   VALUES('а'),('б'),('в'),('a'),('b'),('c'),('1'),('2'),('3')
      postgres@postgres(# ) 
      postgres@postgres-# SELECT string_agg(t.c,',' ORDER BY t.c) AS "default",
      postgres@postgres-#        string_agg(t.c,',' ORDER BY t.c COLLATE "ru-x-icu") AS "ru-x-icu"
      postgres@postgres-# FROM t \gx
      -[ RECORD 1 ]---------------
      default  | 1,2,3,a,b,c,а,б,в
      ru-x-icu | 1,2,3,a,b,c,а,б,в

      А вот так тот же запрос выглядит на AlmaLinux8

      postgres@postgres=# WITH t(c) AS (                                                                                                                                         
      postgres@postgres(#   VALUES('а'),('б'),('в'),('a'),('b'),('c'),('1'),('2'),('3')                                                                                          
      postgres@postgres(# )                                                                                                                                                      
      postgres@postgres-# SELECT string_agg(t.c,',' ORDER BY t.c) AS "default",                                                                                                  
      postgres@postgres-#        string_agg(t.c,',' ORDER BY t.c COLLATE "ru-x-icu") AS "ru-x-icu"                                                                               
      postgres@postgres-# FROM t \gx                                                                                                                                             
      -[ RECORD 1 ]---------------                                                                                                                                               
      default  | 1,2,3,a,b,c,а,б,в                                                                                                                                               
      ru-x-icu | 1,2,3,а,б,в,a,b,c

      Обратите внимание, что на AlmaLinux8 при ru-x-icu сначала идет кирилица, что правильно, а на Centos7 там же идет латиница, что по моему мнению является багом. А вот почему этот баг проявляется, сложно сказать, в обоих базах провайдер сортировки ICU и поэтому должно бы работать одинаково. Может Ваше предположение и верно, о том, что в Centos7 хоть и написано, что используется ICU, но по факту может и libc использоваться.


      1. CrushBy
        20.01.2023 10:57

        Мы обычно просто после миграции, на всякий случай, перестраиваем индексы по строкам (благо их немного).

        Кстати, еще один момент. Не сталкивались с тем, что в CentOS 8 начинаются какие-то проблемы при работе с большим количеством временных таблиц ? У меня несколько раз при миграции с CentOS 7 на CentOS 8 при большом количестве пользователей и соединений (1000+) висли запросы вида TRUNCATE t131 и ANALYZE t439. К сожалению, не было времени разбираться в причинах из-за жалоб пользователей и приходилось просто откатывать на CentOS 7 обратно.

        Работа идет на ванильном PostgreSQL и там нет fast_trunc (да и там логика все-таки подразумевает транзакционность очистки временных таблиц). Единственная идея почему именно на CentOS 8 возникала проблема - это в том, что там как-то по другому реализована работа с файлами, и TRUNCATE тормозит на создании нового файла под временную таблицу на уровне операционной системы из-за того, что в каталоге оказывается под миллион файлов. Причем там был именно xfs, у которого известные проблемы с большим количеством мелких файлов, на ext4, к сожалению, проверить не получилось.


        1. krpsh Автор
          20.01.2023 11:10

          Нет, с проблемой в работе со временными таблицами не сталкивались, хоть у нас везде xfs. Я не совсем знаю как и где Postgresql использует временные таблицы, но могу предположить, что характер нашей нагрузки не предполагает использвания множетва временных таблиц у нас же не 1С ).