Как стать автором
Обновить
Флант
DevOps-as-a-Service, Kubernetes, обслуживание 24×7

Наш опыт миграции PostgreSQL с AWS RDS на свою (self-hosted) инсталляцию

Время на прочтение15 мин
Количество просмотров5.9K

В статье рассмотрен способ безболезненной миграции базы данных PostrgeSQL c RDS-сервиса в AWS на self-hosted-инстанс с обеспечением отсутствия простоя в работе и максимальной консистентности данных.

Ликбез

AWS RDS (Relational Database Service) — это база данных как сервис. Вам предоставляют доступ к базе данных без необходимости ее администрировать, а все заботы по поддержанию резервного копирования, настройки репликации и обеспечению высокой доступности берет на себя поставщик услуги (т.е. AWS).

Проще говоря, в пару кликов мыши вы получаете один или несколько экземпляров БД, с которыми можно сразу работать — писать или читать данные. При этом нет необходимости самостоятельно разбираться с установкой и изначальной конфигурацией самой БД.

Согласитесь, звучит круто! Но на самом деле есть нюансы. Ограничения RDS можно в общих чертах сформулировать так:

  • Поскольку весь процесс администрирования берет на себя провайдер, вы, как пользователь, получаете ограниченную возможность конфигурирования БД, что иногда бывает критично, так как при этом отсутствует возможность при необходимости произвести тонкую настройку.

  • Если кто-то делает за вас всю «грязную» работу, то за это придется заплатить, а в случае RDS счет бывает немаленьким.

Часто бывает, что когда проект только начинается, то выгоднее взять RDS. Но по мере роста и развития самого проекта растет, как правило, и база данных, а значит и стоимость за каждый экземпляр RDS. В какой-то момент приходит понимание, что выгоднее иметь self-hosted-решение, даже с учетом затрачиваемого на его развертывание и поддержку времени.

Постановка задачи и выбор метода ее решения

Как раз недавно у нас был такой кейс: нужно было перенести базу данных PostgreSQL из RDS-инстанса в AWS, который активно использовался, на свою инсталляцию СУБД. Переносимые базы были представлены в виде двух экземпляров: master и read-only-реплики. В качестве результата мы хотели получить идентичную архитектуру. 

Кроме того, нужно было гарантировать минимальное время простоя при переносе и консистентность данных.

Существует два варианта, как можно решать подобную эту задачу:

  1. Отключаем всю запись в БД, снимаем дамп и разворачиваем его на подготовленных виртуальных машинах, затем переключаем все приложения на новую БД. Это решение гарантирует 100% консистентность данных, но не удовлетворяет требованию минимального времени простоя.

  2. Настраиваем репликацию из RDS (master) в новую инсталляцию (slave) и ждем, пока slave догонит master. Затем переключаем приложение на новую инсталляцию, «промоутим» ее до master и радуемся. Этот вариант удовлетворяет всем требованиям и был выбран как основной.

У PostgreSQL для запуска дополненного slave есть утилита в комплекте: pg_basebackup. Она позволяет потоково снять полную копию БД и запустить ее как slave (нужно лишь дополнительно выполнить некоторую конфигурацию). Нам остается только дождаться, пока новый slave догонит master.

Звучит как наш вариант, но дьявол кроется в деталях. В начале статьи мы упоминали, что в RDS есть достаточно много ограничений, одно из которых — невозможность создания пользователя с ролью REPLICATION (необходимо для настройки и запуска потоковой репликации с pg_basebackup), а другое — физическая репликация крайне требовательна к бинарной совместимости master'а и slave'ов.

Знатоки PostgreSQL уже, наверное, догадались, к чему все идет. Помимо потоковой репликации есть логическая. Забегая немного вперед: именно логическая репликация нам подошла и позволила реализовать задуманный план.

О потоковой и логической репликации

Потоковая репликация (Streaming Replication). Это репликация, при которой от основного сервера PostgreSQL на реплики передается WAL. И каждая реплика затем по этому журналу изменяет свои данные. Для настройки такой репликации все серверы должны быть одной версии, работать на одной ОС и архитектуре. Потоковая репликация в PostgreSQL бывает двух видов — асинхронная и синхронная.

Логическая репликация (Logical Replication). Логическая репликация оперирует записями в таблицах PostgreSQL. Этим она отличается от потоковой репликации, которая оперирует физическим уровнем данных: биты, байты и адреса блоков на диске. Возможность настройки логической репликации появилась в PostgreSQL 10.

Подготовка к миграции

Начнем! Сейчас у нас есть RDS типа «master -> slave», а в результате мы хотим получить аналогичное self-hosted-решение.

Переезд на self-hosted можно разделить на два этапа:

master (RDS) -> slave1 (RDS)
     |
     > “slave2” (EC2 VM) -> slave3 (EC2 VM)

На первом этапе — воспроизводим схему данных и пользователей с RDS-инстанса master на машине slave2, настраиваем логическую репликацию между ними и дожидаемся полной синхронизации данных.

На втором — создаем потоковую реплику slave3 со “slave2”.  Здесь “slave2” в кавычках, потому что на самом деле этот инстанс PostgreSQL не находится в состоянии recovery (как в случае полноценной потоковой репликации), а лишь будет иметь в себе подписку на публикацию всех таблиц master-RDS-инстанса. Когда slave3 полностью догонит “slave2”, убедившись, что все данные синхронизированы, мы «промоутим» “slave2” в master, по сути переключая на него весь трафик приложения.

Итак, сперва нужно оценить актуальные системные требования для машин ЕС2 в AWS, проанализировав метрики RDS (это можно сделать в веб-интерфейсе AWS), и заказать их. Лучше всего их заказывать в разных зонах доступности, чтобы обеспечить отказоустойчивость кластеров. Конфигурация серверов и сети выходит за рамки темы поста, но я верю — вы справитесь!

На новые EC2-инстансы, в нашем случае их было 2 (master и slave), устанавливаем PostgreSQL — разумеется, одной версии. Это важно для последующей настройки потоковой репликации между новыми кластерами. Версия СУБД на этих новых серверах может отличаться (быть выше, но не меньше!) от версии RDS-инстансов, так как мы используем логическую репликацию.

Теперь проверим интересующие нас параметры в RDS:

select name, setting from pg_settings where name in ('max_wal_senders', 'max_worker_processes' 'max_logical_replication_workers', 'wal_level', 'max_replication_slots');

-[ RECORD 1 ]------------------
name    | max_replication_slots
setting | 5
-[ RECORD 2 ]------------------
name    | max_wal_senders
setting | 15
-[ RECORD 3 ]------------------
name    | max_worker_processes
setting | 8
-[ RECORD 4 ]------------------
name    | wal_level
setting | logical
-[ RECORD 5 ]------------------
name    | max_logical_replication_workers
setting | 1

Чем нам помогает эта информация?

  1. wal_level должен быть logical. Если это не так, то можно исправить через консоль AWS, где нужно зайти в параметр-группу инстанса и изменить значение параметра rds.logical_replication.

  2. Хотя каждая подписка в логической репликации использует один слот репликации. На старте могут создаваться — и, как правило, создаются — дополнительные временные слоты для синхронизации уже существующих данных в таблицах, включенных в публикацию. Этот момент требует внимания.

    Параметр max_replication_slots на стороне публикации следует конфигурировать с некоторым запасом, в зависимости от параметров max_logical_replication_workers и max_sync_workers_per_subscription в базе-подписчике.  

    Разберемся чуть детальнее. Первый параметр (max_logical_replication_workers) задает общий пул worker'ов логической репликации у подписчика. Этот пул включает в себя в том числе временные sync worker'ы, которые работают над первоначальной синхронизацией данных и создают ранее упомянутые временные слоты. Параметр max_sync_workers_per_subscription задает максимальное количество synс worker'ов, которые могут работать одновременно. В базе с публикацией параметр max_replication_slots в частном случае логической репликации должен быть равен количеству подписок + значение max_sync_workers_per_subscription + N запас. Этот запас нужен, потому что возможна ситуация, в которой база-подписчик попытается запустить sync worker, когда предыдущие временные слоты ещё не были освобождены.

    При подобном развитии событий лог базы-подписчика недвусмысленно сообщит об этом. Ошибка будет похожа на следующую: 

    `2022-07-05 13:16:18 UTC::@:[6165]:PANIC: could not find free replication state, increase max_replication_slots`

    Это означает, что нужно смотреть в сторону вышеупомянутых параметров.

  3. Еще один важный момент — параметр logical_decoding_work_mem, появившийся в 13й версии PostgreSQL. По умолчанию он равен 64 Mb и устанавливает максимальное значение используемой памяти для логического декодирования каждого подключения логической репликации, опираясь на которое СУБД определяет, сбрасывать декодированные значения на диск или сразу передавать их подписчику. RDS используют не самые быстрые диски, поэтому нам желательно как можно дольше избежать использование диска в процессе логической репликации. В подобном случае, когда будет только одна подписка,  вполне безопасно устанавливать этот параметр больше work_mem. Тем не менее, не стоит использовать запредельные значения, если база с публикацией находится под большой нагрузкой.

Важно! Не забудьте перезагрузить RDS-инстанс для применения изменений параметра max_replication_slots.

В качестве подготовительного этапа остается лишь убедиться, что пользователь, который будет задействован для создания подписки, является членом группы rds_replication.

В PostgreSQL проверяем, под каким пользователем мы подключены в данный момент:

=> \conninfo
You are connected to database "db" as user "sysadmin_root" on host "172.31.41.232" at port "5432".

А также смотрим, в какие роли включен наш пользователь:

db=> \du sysadmin_root
                                  List of roles
   Role name   |          Attributes           |            Member of
---------------+-------------------------------+---------------------------------
 sysadmin_root | Create role, Create DB       +| {rds_superuser,rds_replication}
               | Password valid until infinity |

Если вы видите схожую картину, то всё в порядке — вы подключились под админом вашего RDS-инстанса (rds_superuser), и у вас есть нужные дополнительные привилегии, чтобы двигаться дальше (rds_replication).

Здесь sysadmin_root — это админ-пользователь RDS-инстанса, имя и пароль которого задается при создании последнего.

Настройка логической репликации

Логическая репликация, в отличие от потоковой, осуществляет лишь репликацию указанных данных, но не схем, пользователей и прочих метаданных. А нам нужно снять дамп схемы данных с RDS-инстанса и, желательно, пользователей. 

При этом прав «настоящего» суперпользователя, как уже отмечалось, нет. Это немного усложнит весь процесс, но не помешает добиться конечной цели.

1. Пользователи и роли

Первым делом получим пользователей БД. Если попытаться полностью вытащить роли вместе с паролями через pg_dumpall:

pg_dumpall --host=172.31.41.232 --port=5432 -U sysadmin_root --password --globals-only --no-privileges --database=db > rds_globals.sql

… то, к сожалению, ничего не получится — кроме ошибки о недостаточности прав на чтение системных таблиц. Тем не менее, определение ролей и их членство в других ролях можно извлечь, добавив к команде выше ключ --no-role-password.

Полученный после выполнения такой команды скрипт можно исправить вручную, удалить ненужных пользователей (артефакты RDS), проверить членство и добавить пароли в определение пользователей. Тут магии нет — открываем получившийся скрипт и ищем строки c CREATE USER …, куда необходимо добавить WITH ENCRYPTED PASSWORD '<Криптографически стойкий, отличный пароль>';

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

CREATE USER <username> WITH ENCRYPTED PASSWORD '<password>';

2. Схема БД

Теперь самое главное — снимаем с RDS дамп схем БД.

RDS не даст сделать это с помощью pg_dumpall --schema-only, потому что у выданного нам админа нет прав на системные таблицы, а pg_dumpall будет пытаться снять и их дамп в том числе. Поэтому попробуем воспользоваться pg_dump:

pg_dump -U sysadmin_root -h 172.31.41.232 --create --dbname db --schema-only > rds_schema.sql

В процессе выполнения этой команды можно встретить такие ошибки:

pg_dump: error: query failed: ERROR:  permission denied for relation <relation_name>
pg_dump: error: query was: LOCK TABLE <schemaname>.<relation_name> IN ACCESS SHARE MODE

Проблема заключается в том, что для снятия дампа в RDS пользователь, которым мы подключаемся, должен не только иметь право на чтение, но и быть явным владельцем (owner) как схемы, так и всех таблиц в этой схеме.

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

Исправлять владельца придется вручную. Для всех таблиц в каждой схеме <SCHEMA_NAME>, существующей в RDS-базе, надо сгенерировать список таких команд ALTER TABLE:

SELECT 'ALTER TABLE '|| schemaname || '.' || tablename ||' OWNER TO <NEW_OWNER>;'
FROM pg_tables WHERE NOT schemaname IN ('pg_catalog', 'information_schema') AND schemaname = '<SCHEMA_NAME>'
ORDER BY schemaname, tablename;

В результате получится список команд ALTER TABLE для изменения owner на <NEW_OWNER>. На них полезно посмотреть и убедиться, что всё корректно. После этого получившийся SQL можно дополнить \gexec, чтобы сразу применить сгенерированные запросы.

Если используется несколько схем, потребуется пройтись по всем из них. В любом случае ошибки при попытке снятия дампа помогут найти пропущенные таблицы. Получившийся итоговый файл с командами для всех схем пусть имеет прежнее название — rds_schema.sql.

3. Загрузка дампов и миграция данных

Теперь нужно загрузить получившиеся дампы (rds_globals.sql, rds_schema.sql) в новый PostgreSQL-кластер — будущий master (вне RDS). Для этого из-под системного пользователя postgres достаточно выполнить:

~$ psql < rds_globals.sql
***
~$ psql < rds_schema.sql

В новой базе права владения таблицами должны восстановиться, потому что мы не использовали ключ -O (--no-owner) у pg_dump при снятии дампа схемы.

4. Создание публикации и подписки

На сервере RDS создадим публикацию для всех таблиц:

db=> CREATE PUBLICATION pub_all_tables FOR ALL TABLES;

Проверим статус репликации, запросив количество слотов репликации:

db=> select * from pg_replication_slots;
(0 rows)

Как видно, слотов ещё нет — это нормально! Слот будет создан подпиской.

В будущем мастере PostgreSQL создадим подписку на публикацию в нужной базе. Для этого на self-hosted (будущий master) подключаемся к БД и настраиваем подписку на публикацию с RDS-базы, выполнив команды:

postgres=# \c db
You are now connected to database "db" as user "postgres".

db=# CREATE SUBSCRIPTION sub_all_tables CONNECTION 'dbname=db host=172.31.41.232 user=sysadmin_root password=XXX' PUBLICATION pub_all_tables;

Здесь могут возникнуть недвусмысленные ошибки, если в базе-подписчике не будет хватать каких-то таблиц, которые есть в публикации. В таком случае нужно вернуться к предыдущим шагам и убедиться, что дамп схемы был снят правильно, и включает в себя все объекты. Или снять и перенести дамп нехватающих таблиц (ключ -t в pg_dump).

Если все в порядке (подписка успешно создана), можно убедиться, что на стороне RDS создались слоты репликации:

db=> select * from pg_replication_slots;
-[ RECORD 1 ]-------+----------------------------------------
slot_name           | sub_all_tables
plugin              | pgoutput
slot_type           | logical
datoid              | 24717
database            | db
temporary           | f
active              | t
active_pid          | 68108
xmin                |
catalog_xmin        | 1125303273
restart_lsn         | E6B4/26EB3B08
confirmed_flush_lsn | E6B4/26FC1020
-[ RECORD 2 ]-------+----------------------------------------
slot_name           | pg_22164_sync_17913_7065019513235540191
plugin              | pgoutput
slot_type           | logical
datoid              | 24717
database            | db
temporary           | f
active              | f
active_pid          |
xmin                |
catalog_xmin        | 1124858970
restart_lsn         | E6B3/FE1B7538
confirmed_flush_lsn | E6B3/FE1B9F70
-[ RECORD 3 ]-------+----------------------------------------
slot_name           | pg_22164_sync_18026_7065019513235540191
plugin              | pgoutput
slot_type           | logical
datoid              | 24717
database            | db
temporary           | f
active              | f
active_pid          |
xmin                |
catalog_xmin        | 1124678089
restart_lsn         | E6B3/F7819580
confirmed_flush_lsn | E6B3/F781A718

Здесь стали видны те самые временные слоты для синхронизации. На этом этапе нужно обратить внимание на лог подписчика на стороне self-hosted PostgreSQL — если слотов не будет хватать, появятся соответствующие ошибки.

Пока все таблицы переезжают на новый сервер, полезно напомнить, что размер новой базы может отличаться (быть меньше) базы в RDS. Это связано с естественным «распуханием» объектов в PostgreSQL — так называемым bloat. К сожалению, часто RDS-базы «забрасывают», не выполняя регулярные очистки через VACUUM FULL или с помощью pg_repack.

Наконец, важно понимать, что текущая модель репликации — это исключительно временное решение. Потому что любое изменение в схеме данных на стороне RDS (новые таблицы, новые столбцы…) не будет автоматически включаться в публикацию.

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

5. Потоковая репликация в новых кластерах

Теперь, перед переключением всего трафика с RDS на новые кластеры PostgreSQL, подготовим полноценную реплику с новой базы-подписчика.

Для этого мы воспользуемся средствами потоковой репликации. Задача довольно тривиальна, поэтому достаточно будет краткого описания основных шагов:

  1. Если это ещё не было сделано, то на новом сервере — будущем master’е (и на реплике) — нужно установить параметр listen_addresses = *, который позволит прослушивать все входящие подключения.

    Здесь важно отметить, что хотя аутентификация пользователей и управляется в pg_hba, в нашем случае мы смело выставили listen_addresses на все адреса (т.е. *), потому что сетевой доступ к self-hosted-инсталляции ограничен правилами security-group в AWS.

    Внимание! Будьте бдительны в этом моменте, ибо брутфорсеры никогда не спят: либо сконфигурируйте файрвол, либо перечислите в listen_adresses только те IP-адреса, с которых к базе будет подключаться ваше приложение и сотрудники.

  2. На том же сервере создадим пользователя для репликации:

    postgres=# CREATE USER replication WITH REPLICATION ENCRYPTED PASSWORD 'strong password';
  3. Добавим в pg_hba.conf master’а строку следующего вида (whitelist репликации):

    # TYPE  DATABASE        USER            ADDRESS                 METHOD
      Host  replication	    replication	    172.19.1.116/24	        trust

    Справиться о значениях записей pg_hba и посмотреть на их примеры можно в документации.

    Если вкратце, то pg_hba — это специальный конфигурационный файл, в котором мы указываем аутентификационные записи для подключения к базе. Каждая запись (строка) декларирует допустимое подключение.

    В нашем pg_hba получились такие записи для репликации:

    • host – запись о TCP-подключении;

    • replication в поле DATABASE— это специальное зарезервированное слово для конфигурации репликации;

    • второе replication – это имя пользователя репликации, под которым будет подключаться pg_basebackup;

    • IP-адрес, с которого разрешено подключаться;

    • метод аутентификации trust, который позволяет войти без пароля.

    Не стоит указывать метод аутентификации trust ни в каких случаях, кроме этого — разве что для локального подключения суперпользователя postgres (конфигурация по умолчанию).

  4. Перезагрузим конфигурацию:

    postgres=# select pg_reload_conf();
  5. Запустим pg_basebackup на втором сервере для создания реплики:

    nohup pg_basebackup -h 172.19.0.115 -p 5432 -D /var/lib/postgresql/db -P -U replication -R --wal-method=stream -C -S replica_slot &

    Примечание! Если база очень большая и, очевидно, будет долго реплицироваться, во избежание последствий в случае разрыва SSH-подключения, pg_basebackup лучше запустить в сессии screen или tmux. Кроме того, на момент запуска pg_basebackup директория, в которой создается кластер реплики, должна быть пуста.

    pg_basepackup будет ждать checkpoint на master’е. Если активности мало — ждать может очень долго. Ускорить этот процесс можно выполнением команды в реплицируемой базе:

    postgres=# CHECKPOINT;
  6. Ожидаем завершения переноса данных и подъема кластера реплики. Для этого можно мониторить логи nohup (файл nohup.out в директории, где вызывали команду из пункта 5). При завершении работы pg_basebackup лог будет выглядеть примерно так:

    ...
    368704110/368704110 kB (100%), 0/1 tablespace
    368719331/368719331 kB (100%), 0/1 tablespace
    368719331/368719331 kB (100%), 1/1 tablespace

    В этот момент вся база данных уже передалась на реплику, но процесс ещё запущен. Всё нормально: реплика в этот момент догоняет master, воспроизводя все WAL-журналы, накопившиеся за время переноса базы данных.

    Если же реплика по какой-то причине не поднялась, WAL-журналы будут копиться на master’е, пока не закончится дисковое пространство (WAL’ы удерживает слот репликации). Обязательно учитывайте это заранее при заказе серверов: если база очень большая, то диск потенциально может забиться за время, пока данные будут передаваться на реплику.

    Проверить статус репликации можно следующими командами:

    На master’е:

    postgres=# select * from pg_stat_replication \gx
    -[ RECORD 1 ]----+------------------------------
    pid              | 817047
    usesysid         | 24379
    usename          | replication
    application_name | 14/main
    client_addr      | 172.19.1.116
    client_hostname  |
    client_port      | 38188
    backend_start    | 2022-02-17 22:30:14.429605+00
    backend_xmin     |
    state            | streaming
    sent_lsn         | A57/2AB12C60
    write_lsn        | A57/2A982590
    flush_lsn        | A57/2A982590
    replay_lsn       | A57/2A92A700
    write_lag        | 00:00:00.041077
    flush_lag        | 00:00:00.041077
    replay_lag       | 00:00:00.04943
    sync_priority    | 0
    sync_state       | async
    reply_time       | 2022-02-22 16:26:28.130111+00
    

    На реплике:

    postgres=# select * from pg_stat_wal_receiver \gx
    -[RECORD1]----------------------------------------------
    pid                   | 160940
    status                | streaming
    receive_start_lsn     | 2CF/5B000000
    receive_start_tli     | 1
    written_lsn           | A57/7E7A5D60
    flushed_lsn           | A57/7E7A5D60
    received_tli          | 1
    last_msg_send_time    | 2022-02-22 16:27:30.039854+00
    last_msg_receipt_time | 2022-02-22 16:27:30.040298+00
    latest_end_lsn        | A57/7E7A5D60
    latest_end_time       | 2022-02-22 16:27:30.039854+00
    slot_name             | replica_slot
    sender_host           | 172.19.0.115
    sender_port           | 5432
    conninfo              | user=replication passfile=/var/lib/postgresql/.pgpass channel_binding=prefer dbname=replication host=172.19.0.115 port=5432 fallback_application_name=14/main sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any
    

Теперь самое время проверить данные на новых серверах БД и переключать приложение на новый кластер! RDS можно выключить. Удалять его не стоит, пока мы не убедимся что все данные в полном порядке.

Заключение

В нашем случае клиент хотел перенести всё без простоя. Если же простой возможен, то более простым и, вероятно, более надёжным решением будет pg_dump не только схемы, но и всех данных с последующим восстановлением в новый кластер. Впрочем, это зависит от допустимого времени простоя и размера переносимой базы данных.

При этом важно не забыть перенести ещё и мониторинг БД, чтобы после переезда не остаться без глаз. В нашем случае это было просто: мы используем Okmeter, который одинаково мониторит инстансы RDS и self-hosted PostgreSQL. Нужно было только донести пароль пользователя okmeter и поправить pg_hba.conf (и даже если забыть про это, то из коробки есть алерт, который напомнит, что агент нашёл БД, но не может подключиться к ней).

В итоге поставленная задача была достигнута без особых сложностей. Конечно, может показаться, что было бы проще сразу сделать надежную потоковую реплику из RDS, но этому препятствуют ограничения в AWS.

Также хочется отметить, что необязательно делать публикацию типа ALL TABLES. Вместо этого можно предварительно провести аудит схем данных в RDS: вдруг там есть что-то явно лишнее? Таковыми могут быть «остатки» от тестов или разработки — таблицы и индексы, которые больше не используются. Эти объекты можно либо дропнуть перед настройкой репликации, либо оставить, создав публикацию только для определенных таблиц.

P.S.

Читайте также в нашем блоге:

Теги:
Хабы:
Всего голосов 22: ↑21 и ↓1+20
Комментарии11

Публикации

Информация

Сайт
flant.ru
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия
Представитель
Тимур Тукаев