Pull to refresh

Миграция PostgreSQL под ОС AstraLinux c использованием pgcopydb с PG11 на PG15

Reading time5 min
Views1.2K

Многие в настоящее время сталкиваются с необходимостью миграции с AstraLinux 1.7 на AstraLinux 1.8.

Для тех, кто пользуется штатной PostgreSQL 11 из поставки AstraLinux 1.7, встает во весь рост вопрос, как перетащить существующие базы на новую версию. Если базы маленькие, особых проблем нет. А вот если базу под 1ТБ, то проблемы встают в полный рост.

Для штатной pg_upgrade требуются обе версии (PostgreSQL-11 и PostgreSQL-15). Но в поставке AstraLinux 1.8 нет PostgreSQL 11. При этом, так как Astra PostgreSQL не совместим бинарно с "Ванилью", то и не получится собрать самому нужную версию для Astra 1.8.

"Вкорячить" его из поставки Astra 1.7 можно, но разница NAMEDATALEN между версиями ставит крест на использование pg_upgrade (Смотрите засаду 1).

Штатный pg_dump | pg_restore в одну нитку, для базы 1.1 Тб у нас на стенде работал аж 27 часов. Максимальный размер технологического окна при этом установлен 24 часа.

Может быть еще одна причина для выбора pg_dump/pg_restore варианта - если datafile исходной базы и целевой базы бинарно несовместимы. Как например несовместимы между собой datafiles наиболее часто использующихся дистрибутивов PostgreSQL:

  • "Ваниль"

  • PostgresPro EE/SE

  • Tantor

  • Штатная PostgreSQL 11 AstraLinux 1.7

  • Штатная PostgreSQL 15 AstraLinux 1.8

Поиск решения (помимо логической репликации), навел на утилиту pgcopydb

Для начала засады:

1) в Astra1.8 в PostgreSQL уменьшили длину типа name с 255 байт до 63.

https://docs.astralinux.ru/latest/guide/application/devfeature/

Не поддерживаются длинные имена идентификаторов
При использовании длинных идентификаторов (имен) возникают ошибки. Максимальная длина идентификатора составляет:
    при использовании символов ASCII (в частности, латиницы) — 63 символа;
    при использовании кириллицы (кодировка utf8) — 31 символ;
    при использовании других символов максимальная длина идентификатора уменьшается и зависит от использованного набора символов.
Рекомендации
Максимальная допустимая длина идентификатора задается при сборке и не может быть изменена.
В Astra Linux Special Edition x.7 использовалась максимальная длина идентификатора 255 байт (сборка ЗСУБД с параметром NAMEDATALEN = 256).
В Astra Linux Special Edition x.8 в целях оптимизации производительности, улучшения тестирования и повышения устойчивости работы ЗСУБД максимальная длина идентификатора уменьшена до 63 байт (NAMEDATALEN=64).
Прикладное ПО должно быть адаптировано к этим изменениям.

Поэтому, при использовании длинных идентификаторов (имен) возникают ошибки при pg_dump15/pg_restore15 при работе с PostgreSQL-11. При этом утилиты pg11 без проблем работают с PostgreSQL-15.

Из-за этого, для миграции с использованием pgcopydb его нужно собирать именно на astra1.7.

При тестировании наткнулся на еще одну проблему в связи с эти "улучшением":
Попытка
> pg_upgrade -m clone 11 main ...
> старая и новая максимальная длина идентификаторов в pg_controldata различаются или
> некорректны
> Ошибка, выполняется выход
Увы, pg_upgrade не сработает для набора этих версий.

2)  pgcopydb не понимает MAC LABEL SECURITY

3) PG12+: больше не поддерживается созданием таблиц с кляузой "WITH OIDS"

Ранее в таблицу можно было добавить столбец oid (невидимый в обычных запросах), создавая её с указанием WITH OIDS; теперь такой возможности нет. При этом по-прежнему возможно создавать столбцы с типом oid. С учётом этого изменения порядок использования таблиц, созданных с указанием WITH OIDS, потребуется скорректировать.

Системные каталоги, в которых были скрытые столбцы oid, теперь содержат обычные столбцы oid. Таким образом, эти столбцы будут выводиться в результате запроса SELECT *, тогда как ранее они выводились только в случае явного указания.

Сборка pgcopydb

Сборку рекомендуется выполнять на отдельной машине, так как средства разработки не допускаются в закрытом контуре.

apt-get install -y --no-install-recommends \
  git \
  build-essential \
  autotools-dev \
  libedit-dev \
  libgc-dev \
  libpam0g-dev \
  libreadline-dev \
  libselinux1-dev \
  libxslt1-dev \
  libssl-dev \
  libkrb5-dev \
  zlib1g-dev \
  liblz4-dev \
  libpq5 \
  libpq-dev \
  libzstd-dev \
  postgresql-server-dev-all \
  postgresql-common \
  postgresql \
  python3-sphinx

git clone https://github.com/dimitri/pgcopydb

cd pgcopydb
make

Инсталляция pgcopydb на другую машину

Для инсталляции нужен только сам исполняемый файл pgcopydb и библиотека libgc.so.1

## Скопировать полученный файл на нужную машину
scp src/bin/pgcopydb/pgcopydb targethost:bin/ 

## разрешить установку права запуска для файлов
sudo astra-nochmodx-lock disable

## добавляем возможность запуска
chmod 0755 pgcopydb

## доставить библиотеку libgc.so.1
apt install libgc1c2

Так, как pgcopydb не понимает MAC LABEL SECURITY, ставим обертку для pg_dump, pg_restore, pg_dumpall

cd /usr/lib/postgresql/11/bin

mv pg_dump pg_dump.bin
mv pg_dumpall pg_dumpall.bin
mv pg_restore pg_restore.bin

## добавляем опции отключающие MAC LABEL
echo "/usr/lib/postgresql/11/bin/pg_dump.bin --no-security-labels --disable-macs $@" > pg_dump
echo "/usr/lib/postgresql/11/bin/pg_dumpbin.bin --no-security-labels $@" > pg_dumpall
echo "/usr/lib/postgresql/11/bin/pg_restore.bin --no-security-labels $@" > pg_restore
chmod 0755 pg_dump pg_dumpall pg_restore

Подготовка исходной базы

Правим длинные имена

получить список объектов для поля типа name > 63 bytes

Для этого тупо генерируем запросы select ... from object where length(name) > 63 для всех объектов где есть поле name, и выполняем полученный tmp.sql в отдельном шаге.

Готовим файл запроса полей name и формирования запросов к каждой такой таблице (файл q1.sql):

select
  'SELECT '''||table_schema||'.'||table_name||'.'||column_name||': ''||'|| column_name || 

  ' FROM "'||table_schema||'"."'||table_name||'" WHERE '||'length("'||column_name||'") > 63;' 
from information_schema.columns
where
data_type = 'name'
;

Выполняем следующую команду:

## для каждой базы, формируем tmp.sql и потом выполняем его для получения списка объектов
psql -AXtc "select datname from pg_database"|while read db 
 do echo "==== $db =====";
 psql -AXt -d $db -f q1.sql > tmp.sql
 psql -d $db -AtX -f tmp.sql > $db.txt
done

Передаем полученный список разработчикам, чтобы убрали длинные имена.

Правим "WITH OIDS" у таблиц

Получаем команды для правки таблиц

-- генерация команд для правки таблиц
 
SELECT 'ALTER TABLE "' || n.nspname || '"."' || c.relname || '" SET WITHOUT OIDS;'
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE 1=1
  AND c.relkind = 'r'
  AND c.relhasoids = true
  AND n.nspname <> 'pg_catalog'
order by n.nspname, c.relname;
 
-- выполняем полученные команды, например
test=# ALTER TABLE "abc"."recipients_m11" SET WITHOUT OIDS;
ALTER TABLE
test=#  ALTER TABLE "abc"."config_m11" SET WITHOUT OIDS;
ALTER TABLE
test=#  ALTER TABLE "abc"."data_m11" SET WITHOUT OIDS;

Подготовка целевой базы

Тут все просто, шаги те же что и для обычного pg_dump/pg_restore

например:

создаем базу с нужным owner

переносим роли и extension

# first two commands would use a superuser role
$ pgcopydb copy roles --source ... --target ...
$ pgcopydb copy extensions --source ... --target ...

Можно запускать pgcopydb для миграции

export PGCOPYDB_SOURCE_PGURI="postgres://user@source.host.dev/dbname"
export PGCOPYDB_TARGET_PGURI="postgres://role@target.host.dev/dbname"

pgcopydb clone --table-jobs 4 --index-jobs 4

Tags:
Hubs:
+3
Comments3

Articles