2008-10-23

Черновик. Репликация средствами bucardo. №3

Попытка №2 опять неудачная. Непонятно, почему. Попытка 3.

Итак все тот же SLES10 SP2 и PostgreSQL 8.2.5

Сразу после установки SLES по умолчанию имеем следующий набор модулей:

perl-Bit-Vector-6.4-13.5
perl-Bootloader-0.4.19.12-0.3

perl-Carp-Clan-5.3-13.5
perl-Compress-Zlib-1.35-14.2
perl-Config-Crontab-1.11-12.2
perl-Config-IniFiles-2.39-13.4
perl-Crypt-SmbHash-0.12-13.2
perl-Date-Calc-5.4-14.5
perl-Digest-MD4-1.5-13.2
perl-Digest-SHA1-2.10-15.2
perl-gettext-1.05-13.2
perl-Parse-RecDescent-1.80-259.2
perl-PDA-Pilot-0.11.8-138.2
perl-TermReadKey-2.30-13.2
perl-TimeDate-1.16-136.2
perl-URI-1.35-15.2
perl-X500-DN-0.28-133.2
perl-XML-Parser-2.34-43.2
perl-XML-Writer-0.600-13.2

Возможно в попытке №2 чего-то нехватило, возник конфликт с уже установленым или еще непонятно что. Делаем заново и внимательно. Ставим модули в след последовательности:

Pod-Escapes-1.04
Pod-Simple-2.06
Test-Pod-1.00
Net-Daemon-0.27
PlRPC-0.2020
DBI-1.51
ExtUtils-CBuilder-0.24
ExtUtils::MakeMaker 6.32
IO-1.2301
Storable-2.16
POE-Test-Loops-1.002
Curses-1.13
Event-1.10
Устанавливаем пакет gtk-devel
Gtk-Perl-0.7009 (perl Makefile.PL --without-guessing). Модуль загадочный... Дохрена всего нужно проставить из Gnome. В принципе пакет опциональный, так что с ним не заморачиваемся, ставим с указанной опцией. Но в уме держим, что модуль установили ненастроенный.)
IO-Tty-1.02
Test-Pod-1.14
HTML-Tagset-3.20
HTML-Parser-3.56
libwww-perl-5.800
Socket6-0.15
Tk-804.027 (make test нужно делать в X-ах, я например открыл VNC соединение, и тесты делал в нем, иначе все тесты провалятся)
Devel-Symdump-2.08
Pod-Coverage-0.19
Test-Pod-Coverage-1.06
POE-1.003 (make test нужно делать в X-ах, я например открыл VNC соединение, и тесты делал в нем, иначе все тесты провалятся)
Number-Compare-0.01
Text-Glob-0.08
File-Find-Rule-0.30
PathTools-3.2701
Test-Pod-Coverage-1.08
Test-Simple-0.80
Module-CoreList-2.15
Digest-SHA-5.47
Module-Signature-0.55
ExtUtils-ParseXS-2.19
version-0.76
Compress-Raw-Zlib-2.012
IO-Compress-Base-2.012
Compress-Raw-Bzip2-2.012
IO-Compress-Bzip2-2.012
IO-Compress-Zlib-2.012
Compress-Zlib-2.012 (make install UNINST=1 - так как у нас этот модуль уже установлен, но версия не та, то ставим именно так)
IO-Zlib-1.09
Package-Constants-0.01
Archive-Tar-1.40
Pod-Simple-3.07
podlators-2.2.0
Regexp-Common-2.122
Pod-Readme-0.09
Test-Harness-2.62
PAR-Dist-0.40
Archive-Zip-1.26
Module-Build-0.30 (t/use_tap_harness.....skipped all skipped: TAP::Parser not installed говорит, хотя он есть в установленном модуле Test-Harness. Проигнорируем...)
Test-Distribution-1.24
Test-Portability-Files-0.05
Test-Taint-1.04
Readonly-1.03
Readonly-XS-1.04
Params-Validate-0.91
Time-modules-2006.0814
File-MMagic-1.27
MIME-Types-1.24
Module-Find-0.05
Sub-Uplevel-0.18
Array-Compare-1.16
Test-Exception-0.27
Tree-DAG_Node-1.06
Test-Warn-0.11
Archive-Any-0.0932
Class-Accessor-0.31
Algorithm-Diff-1.1902
Array-Diff-0.04
CPAN-DistnameInfo-0.06
File-Slurp-9999.13
IO-Capture-0.05
List-MoreUtils-0.22 (при установке ругнулся, что Test::Pod not installed, но я его ставил. Игнорируем, но держим в уме, что модуль проблемный)
Test-Tester-0.103
Test-NoWarnings-0.084
Test-Deep-0.103
UNIVERSAL-require-0.11
Pod-Strip-1.02
Parse-RecDescent-1.94
Module-ExtractUse-0.23
Module-Pluggable-2.96
Params-Util-0.33
Sub-Install-0.924
Data-OptList-0.103
Sub-Exporter-0.980
Data-Section-0.005 (распаковывать обязательно при помощи tar -xvzf)
Text-Template-1.45
Software-License-0.008
YAML-0.62
Devel-Leak-0.03
YAML-Syck-0.95
Test-YAML-Valid-0.03
Test-YAML-Meta-0.11
Text-CSV_XS-0.57
Module-CPANTS-Analyse-0.82 (make test работает очень долго. Это нормально)
Test-Kwalitee-1.01 (Как его проставить - ХЕЗ. makefile.PL отсутствует как класс. Делаем так. Сначала под alexsf выполняем perl Build.PL, потом под root копируем содержимое blib/lib в /usr/lib/perl5/site_perl/5.8.8, и Test::Kwalitee.3pm в /usr/share/man/man3)
POE-Component-Server-Syslog-1.16 (t/99_test_kwalitee....ok, значит предыдущий пакет поставлен правильно :) )
Sys-Syslog-0.27
Sub-Name-0.02
Sub-Identify-0.03
SUPER-1.16
Algorithm-C3-0.07
Class-C3-XS-0.07
Class-C3-0.19
Class-MOP-0.37
Test-LongString-0.11
Locale-US-1.2
Params-Coerce-0.14
Business-ISBN-Data-20081020
GD-Barcode-1.15
Business-ISBN-2.04 (сказал, что GD::Font не загружен. Такого не нашел. Игнорируем и ставим)
URI-1.37
IO-String-1.08
IO-stringy-2.110
DBM-Deep-1.0013 (ругнулся на несколько тестов, но игнорируем и ставим)
Test-Output-0.12 (ошибки игнорируем)
DateTime-Locale-0.41
Class-Singleton-1.03
DateTime-TimeZone-0.83 (Ругается, но ставим)
DateTime-0.4305 (некоторые тесты не прошли из-за отсутствия пакетов, которые проставлю дальше. Эти пакеты требуют, чтоб DateTime уже был установлен)
Set-Infinite-0.63
DateTime-Set-0.25
DateTime-Event-Recurrence-0.16
DateTime-Event-ICal-0.09
DateTime-Format-ICal-0.09
DateTime-Format-Strptime-1.0800
Class-Factory-Util-1.7
Task-Weaken-1.02
Test-Signature-1.10
DateTime-Format-HTTP-0.37
DateTime-Format-Mail-0.3001
DateTime-Format-IBeat-0.161
PadWalker-1.7
Devel-Cycle-1.10
Test-Memory-Cycle-1.04
DateTime-Format-Builder-0.7901
DateTime-Format-MySQL-0.04
Class-Inspector-1.23
aliased-0.22
Declare-Constraints-Simple-0.03
Moose-0.18
Mail-Sendmail-0.79 (ругается при тесте, что не может соединиться с сервером, но это нормально, игнорируем и ставим)
B-Keywords-1.08
Config-Tiny-2.12
Class-Data-Inheritable-0.08
Devel-StackTrace-1.12
Exception-Class-1.23
Module-Pluggable-3.1
Taint-Runtime-0.03
Clone-0.25
Scalar-List-Utils-1.19
Test-Object-0.07
PPI-1.203
String-Format-1.14
File-HomeDir-0.82
File-Which-0.05
Perl-Tidy-20071205
Pod-Spell-1.01
Regexp-Parser-0.20
Perl-Critic-1.092
Perl-Critic-Bangs-1.00 (на тесте ругается на некоторые вещи, но ставим все равно)
Perl-Critic-Lax-0.007
Test-Dynamic-1.3.3
DBD-Pg-1.49
Как этот модуль проверяется:
В pg_hba.conf меняем на:
#local all all ident sameuser
#host all all 127.0.0.1/32 ident sameuser
local all all password
host all all 127.0.0.1/32 password
host all all 0.0.0.0/0 password
Устанавливаем переменные окружения:
export DBI_PASS=postgres
export TEST_VERBOSE=1
export DBI_USER=postgres
export DBI_DSN='dbi:Pg:dbname=postgres'
И только после этого делаем make test. Потом возвращаем pg_hba.conf на место

Настраиваем postgres под Bucardo:

root# mkdir /u02
root# chown postgres:postgres /u02
root# su - postgres
postgres> mkdir /u02/pgdata/bucardo -p
postgres> psql
postgres=# CREATE USER bucardo SUPERUSER;
postgres=# ALTER ROLE bucardo password 'bucardo';
postgres=# CREATE TABLESPACE bucardo OWNER bucardo LOCATION '/u02/pgdata/bucardo';
postgres=# CREATE DATABASE bucardo WITH ENCODING='UTF8' OWNER=bucardo TABLESPACE=bucardo;
postgres=# CREATE LANGUAGE plperlu;
postgres=# \q
postgres> psql -h 10.77.11.207 -U bucardo bucardo
bucardo=# CREATE LANGUAGE plpgsql;
bucardo=# CREATE LANGUAGE plperlu;
bucardo=# \q
postgres> psql -f bucardo.schema -h 10.77.11.207 -U bucardo bucardo
bucardo=# \q

DBIx-Safe-1.2.4 - из самого bucardo
Bucardo-3.0.9 - из самого bucardo

Особенности установки Bucardo-3.0.9:

0. Во всех скриптах bucardo, исполняемых перловских файлах и т.д. неправильно стоит ссылка на perl, поэтому везде нужно поменять /usr/local/bin/perl на /usr/bin/perl. Я пока поменял только в bucardo_ctl, но и в остальных в будущем нужно будет это сделать.

1. Экспортируем переменные окружения:

export DBI_DSN='dbi:Pg:dbname=bucardo'
export DBI_USER=bucardo
export DBI_PASS=bucardo
export TEST_VERBOSE=1

2. PostgreSQL должен быть настроен на время теста на коннект к базе локальных соединений через пароль. То есть в pg_hba.conf первыми строками д.б.:

local all all password
host all all 127.0.0.1/32 password

3. Редактируем файл Bucardo-3.0.9/t/bucardo.test.data

## This file contains the connection information needed for "make test"

## Master database. This must be a superuser, as we will create a bucardo_test database
DBNAME: bucardo
DBUSER: bucardo
DBHOST: 10.77.11.207
DBPORT: 5432
TESTDB: bucardo_test
TESTBC: bucardo_test

## First database
DBNAME1: postgres
DBUSER1: bucardo
DBHOST1:
DBPORT1: 5432
TESTDB1: bucardo_test1
TESTBC1: bucardo_test1

## Second database:
DBNAME2: postgres
DBUSER2: bucardo
DBHOST2:
DBPORT2: 5432
TESTDB2: bucardo_test2
TESTBC2: bucardo_test2

## Third database:
DBNAME3: postgres
DBUSER3: bucardo
DBHOST3:
DBPORT3: 5432
TESTDB3: bucardo_test3
TESTBC3: bucardo_test3

После этого:

perl Makefile.PL
make
make test TEST_VERBOSE=1

Во время работы теста будут созданы тестовые БД, в них чего-то зальется и т.д. Ход всей работы будет на консоли. Будут ошибки, но я так предполагаю, что не критичные, так как в целом, как я понимаю, все отработало ОК. Поэтому под root make install.

Все. Проставили. Е**тня с модулями закончена...

Так. Теперь настраиваем bucardo. На моем хосте в postgresql есть созданная база bucardo, с такими же пользователем и паролем. логинимся в любой sql менеджер под bucardo и выполняем:

-- соединение с Новокаховской БД, которую нужно реплицировать
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('nkres','10.77.12.4','nkres','nkres','вставляем_свой_пароль_на_nkres');

-- БД куда реплицировать
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('allres','10.77.11.207','allres','allres','allres');

-- скорее всего это группа для РЭСовских БД
INSERT INTO bucardo.dbgroup (name) VALUES ('res_slaves');

-- ну тут понятно - ставим соответствие между БД и группами БД
INSERT INTO bucardo.dbmap (db, dbgroup) VALUES ('nkres','res_slaves');

-- настраиваем, какие таблицы реплицировать
-- тут нюанс. PK у нас везде double precision, но мы использовать этот тип не можем, поэтому
-- ставлю bigint, хотя не факт, что так заработает
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('nkres','nkres','_bucardo_fio','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('nkres','nkres','_bucardo_cars','id','bigint','false');

-- создаем т.н. herd (группа объектов для репликации)
INSERT INTO bucardo.herd (name) VALUES ('nkres_test_bucardo');

-- запихиваем эти самые объекты в группу
INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'nkres_test_bucardo', id
FROM goat WHERE db = 'nkres'

-- ну и собственно как откуда куда и как
INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES ('nkres','nkres_test_bucardo','allres','pushdelta','false','1 minutes','false')

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

В Н.Каховке делаем тестовую базу:

CREATE ROLE nkres_slave LOGIN ENCRYPTED PASSWORD 'md5ecc3ea090f168344b2a37be2c21ebc95'
NOINHERIT
VALID UNTIL 'infinity';

CREATE DATABASE nkres_slave
WITH ENCODING='UTF8'
OWNER=nkres_slave;

-- В базе nkres_slave
CREATE SCHEMA nkres_slave AUTHORIZATION nkres_slave;
CREATE LANGUAGE plpgsql;
CREATE LANGUAGE plperlu;

-- Логинимся под nkres_slave и делаем тестовые таблицы

CREATE TABLE nkres_slave._bucardo_cars
(id double precision NOT NULL,
car character varying(20),
nomer character varying(12),
CONSTRAINT pk__bucardo_cars PRIMARY KEY (id))
WITH (OIDS=FALSE);

CREATE TABLE _bucardo_fio
(id double precision NOT NULL,
fio character varying(100),
CONSTRAINT pk__bucardo_fio PRIMARY KEY (id))
WITH (OIDS=FALSE);

Чистим настройки bucardo в базе на 10.77.11.207:

delete from bucardo.sync;
delete from bucardo.herdmap;
delete from bucardo.herd;
delete from bucardo.goat;
delete from bucardo.dbmap;
delete from bucardo.dbgroup;
delete from bucardo.db;

Настраиваем БД bucardo:

-- Регистрируем БД Н.Каховки
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('nkres_slave','10.77.12.4','nkres_slave','nkres_slave','nkres_slave');
-- Регистрируем локальную БД, куда будет сливаться всё
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('allres','10.77.11.207','allres','allres','allres');

-- Создаем группы БД для sleves РЭСов и master конторы:
INSERT INTO bucardo.dbgroup(name) VALUES('res_slaves');
INSERT INTO bucardo.dbgroup(name) VALUES('obl_master');

-- Ставим соответствие между БД и группами БД
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('nkres_slave','res_slaves');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('allres','obl_master');

-- Указываем, какие объекты из nkres_slave реплицировать
-- Тут нюанс. PK у нас везде double precision, но мы использовать этот тип не можем, поэтому
-- ставлю bigint, хотя не факт, что так заработает
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('nkres_slave','nkres_slave','_bucardo_fio','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('nkres_slave','nkres_slave','_bucardo_cars','id','bigint','false');

-- Создаем группу объектов репликации
INSERT INTO bucardo.herd(name) VALUES('nkres_slave_herd');

-- Заносим таблицы в группу объектов репликации
INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'nkres_slave_herd', id
FROM goat WHERE db = 'nkres_slave';

-- Настраиваем правила и пути репликации
INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('nkres_slave_rep','nkres_slave_herd','allres','pushdelta','false','1 minutes','false');

И получаем:

NOTICE: Issuing rollback() for database handle being DESTROY'd without explicit disconnect() at line 29.

CONTEXT: SQL-команда: "SELECT validate_sync('nkres_slave_rep')"
ERROR: error from Perl trigger function: error from Perl function: No such table found for database allres: "nkres_slave._bucardo_cars" at line 259. at line 30.

********** Ошибка **********

ERROR: error from Perl trigger function: error from Perl function: No such table found for database allres: "nkres_slave._bucardo_cars" at line 259. at line 30.
SQL state: XX000

В базе nkres_slave появились объекты bucardo, на таблицах появились триггеры, но тем не менее ошибка. Что ту можно предположить? Возможно, так как bucardo не реплицирует DDL, то реплицируемые таблицы уже должны существовать в базе allres... Создаем пустые таблицы в allres и пробуем опять.

Нет, не помогло. Та же ошибка. Может нужен herd для базы allres??? А может, что совсем плохо, базы и схемы должны называться одинаково???

Итак, в базе allres делаем схему nkres_slave, а в нец создаем таблицы:

CREATE TABLE nkres_slave._bucardo_cars
( id double precision NOT NULL,
car character varying(20),
nomer character varying(12),
CONSTRAINT pk__bucardo_cars PRIMARY KEY (id)
) WITH (OIDS=FALSE);

CREATE TABLE nkres_slave._bucardo_fio
( id double precision NOT NULL,
fio character varying(100),
CONSTRAINT pk__bucardo_fio PRIMARY KEY (id)
) WITH (OIDS=FALSE);

После этого в bucardo

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('nkres_slave_rep','nkres_slave_herd','allres','pushdelta','false','1 minutes','false');

Запрос отработал. Итак, что имеем... А имеем херню - нужно чтоб во всех РЭСах схемы назывались одинаково... Ладно, с этим позже разберемся...

Теперь нужно запустить bucardo. Для этого есть скриптец bucardo_ctl
Для начала отредактируем его, пропишем корректные пароли и пользователей. А потом пытаемся запустить. Для этого ту папку, из которой ставили модуль bucardo копируем в opt, получаем папку

/opt/Bucardo-3.0.9

Там редактируем bucardo_ctl. Устанавливаем секцию

my $bcargs = {
ctlquiet => 0,
ctlverbose => 0,
dbname => 'bucardo',
dbuser => 'bucardo',
dbpass => 'bucardo_bucardo',
verbose => 1, ## Highly recommended to leave this on
sendmail => 0,
extraname => '',
debugfilesep => 0,
debugname => '',
debugstderr => 0,
debugstdout => 0,
debugsyslog => 1,
debugdir => '',
debugfile => 0,
cleandebugs => 1,
};

Кроме того, в первой строке устанавливаем правильный интерпретатор, то есть меняем
/usr/local/bin/perl на /usr/bin/perl

То же самое делаем с скриптами:

./scripts/bucardo_ctl.rc
./scripts/bucardo-report
./scripts/bucardo_rrd
./scripts/check_bucardo_sync
Bucardo.pm
/usr/lib/perl5/site_perl/5.8.8

Ну и пытаемся запустить:

./bucardo_ctl start "Test start - test"

Получаем

Checking for existing processes
Line 716: Could not open "/home/bucardo/restart.reason": Нет такого файла или каталога

Интересно... Предполагается, что б.д. локальный пользователь bucardo. Делаем его

root# useradd -c "Bucardo Replication" -d /home/bucardo -p bucardo_bucardo -s /bin/bash bucardo
root# mkdir /home/bucardo
root# chown bucardo /home/bucardo

Пытаемся запустить опять:

./bucardo_ctl start "Test start - test"

Получаем

Checking for existing processes
Line 727: Could not create "/var/run/bucardo/fullstopbucardo": Нет такого файла или каталога

Под root делаем:

root# mkdir /var/run/bucardo
root# chown bucardo /var/run/bucardo

Пытаемся запустить опять, получаем:

Checking for existing processes
Removing /var/run/bucardo/fullstopbucardo
Starting Bucardo

То есть типа bucardo стартовал...

Настроим логирование, чтоб легче ьыло анализировать работу...
Редактируем /etc/syslog-ng/syslog-ng.conf.in. Добавляем туда:

filter f_bucardo {match('ucardo');};
destination d_bucardo {file(/var/log/bucardo/bucardo);};
log {source(src);filter(f_bucardo);destination(d_bucardo);};

Потом под root выполняем

root# mkdir /var/log/bucardo
root# chown bucardo /var/log/bucardo
root# SuSEconfig
root# /etc/init.d/syslog restart

Останавливаем и запускаем bucardo для проверки...

./bucardo_ctl stop "Test stop"
./bucardo_ctl start "test start"

Проверяем, чреплицировалось что-нибудь или нет???
Нет. В базе allres таблицы nkres_slave._bucardo_cars и nkres_slave._bucardo_fio не изменились... Почему??? Возможно потому, что у меня стоит тип репликации pushdelta.

Pushdelta - A type of sync in which the changes to a source are replicated to a target. То есть так как после запуска репликации я не менял содержимое таблиц, то ничего и не произошло...
Возможно нужно выполнить для начала репликацию fullcopy

Fullcopy - A type of sync in which the target is truncated and the entire source is replicated to it.

, а потом уже переключиться на pushdelta

Пробуем: меняем в bucardo.sync pushdelta на fullcopy и перезапускаем bucardo.

./bucardo_ctl stop "Stop pushdelta -> fullcopy"
./bucardo_ctl start "Start pushdelta -> fullcopy"

И ничего... Смотрим логи bucardo, и видим:

Nov 10 11:53:37 vmsles10 Bucardo[13683]: KID Warning! Child for "nkres_slave_rep" was killed at line 3822: DBD::Pg::db do failed: ERROR: доступ запрещён для связи pg_class main error: none source error: none target error: 7

Блин. Проверил, у всех ролей, вовлеченных в процесс доступ к pg_class есть... Что за фигня??? Варианта два. 1 - у кого-то нехватает прав. 2 - другой вариант :) Пытаемся проверить вариант 1. Всем участникам процесса даем роль SUPERUSER (allres, nkres_slave)

Мля, получилось.... Появились данные...

Ставим режим репликации на pushdelta, перезапускаем и пробуем манипулировать данными.
Работает. Причем мгновенно :) Радует.

Пробуем 2-ю БД добавить. На сервере 10.77.11.52 клепаем БД bucardo_res, и создаем 2 любимые таблицы в ней. И добавляем все это в конфигурацию bucardo

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('bucardo_res','10.77.11.52','bucardo_res','bucardo_res','bucardo_res');

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('bucardo_res','res_slaves');

INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('bucardo_res','nkres_slave','_bucardo_fio','id','bigint','false');

INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('bucardo_res','nkres_slave','_bucardo_cars','id','bigint','false');

INSERT INTO bucardo.herd(name) VALUES('bucardo_res_herd');

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'bucardo_res_herd', id
FROM goat WHERE db = 'bucardo_res';

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('bucardo_res_rep','bucardo_res_herd','allres','pushdelta','false','1 minutes','false');

При манипулировании данными выяснилось, таки что при pushdelta реплицируются только данные, измененные после запуска репликации. Пробуем fullcopy, а потом pushdelta. И вот тут мы натыкаемся на такую жопу. При fullcopy из таблиц БД allres были удалены все данные, и установлены те, которые находились в bucardo_res. А сие не есть гуд.

Вариант такой. Никогда не использовать fullcopy. Всегда pushdelta, но для того, чтобы все просинхронизировалось изначально, делаем какой-нибудь update на всех таблицах, который реально никаких данных не поменяет, но заставит bucardo провести синхронизаци.

Например

update nkres_slave._bucardo_cars set car = car

Проверил, работает.

Теперь проверим на рабочих базах в тестовом варианте.
Итак на сервере 10.77.11.52 создали 2 БД, cpres и klres, залили в них резервные копии, так что в каждой БД появились схемы cpres и klres соответственно. Естественно перед этим были созданы табличные пространства и пользователи, хозяева БД и схем. После это в каждой БД переименовываем схемы на res. Затем создаем локальную БД (куда будет реплицироваться), называем ее fullres, хозяином делаем пользователя res, и в ней делаем схему res. Напоминаю, что все пользователи, участвующие в процессе д.б. SUPERUSER. После этого нам необходимо в схеме res в БД fullres создать идентичные объекты БД. Потом не забываем создать языки plpgsql и plperlu.

Теперь приступим к настройкам bucardo. У нас в каждой БД 300 с гаком таблиц, которые д.б идентичны по структуре. Нужно немного автоматизировать процесс. Итак.

delete from bucardo.sync;
delete from bucardo.herdmap;
delete from bucardo.herd;
delete from bucardo.goat;
delete from bucardo.dbmap;
delete from bucardo.dbgroup;
delete from bucardo.db;

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('klres','10.77.11.52','klres','klres','klres_klres_klres');

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('cpres','10.77.11.52','cpres','cpres','cpres_cpres_cpres');

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('fullres','10.77.11.207','fullres','res','res_res');

INSERT INTO bucardo.dbgroup(name) VALUES('res_slaves');
INSERT INTO bucardo.dbgroup(name) VALUES('res_master');

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('klres','res_slaves');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('cpres','res_slaves');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('fullres','res_master');

Для ввода данных в таблицу goat используем след. запрос. Мы его выполняем в klres, а результаты в bucardo.

select 'insert into bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy) VALUES (''klres'',''res'','''||cl.relname||''','''||con.conname||''',''bigint'',''false'');'
from pg_constraint con, pg_class cl, pg_tables t
where t.schemaname = 'res'
and t.tablename = cl.relname
and con.contype = 'p'
and con.conrelid = cl.oid

И оказалось, что таблиц 389, а первичных ключей всего 386. То есть где-то нету PK. Нужно найти и либо поставить PK, либо эту таблицу не реплицировать.

Засранцев ловим таким образом:

select distinct t.tablename
from pg_tables t, pg_class cl, pg_constraint con
where t.schemaname = 'res'
and t.tablename = cl.relname
and t.tablename not in (select cl.relname
from pg_constraint con, pg_class cl
where contype = 'p'
and con.conrelid = cl.oid)

Пойманные засранцы оказались временными и в репликации участия не принимают. Но в будущем наличие PK все равно нужно проверять. Указанный выше запрос используем для вставки данных в таблицу goat для klres и cpres. Не забываем в запросе менять klres и cpres.

INSERT INTO bucardo.herd(name) VALUES('klres_herd');
INSERT INTO bucardo.herd(name) VALUES('cpres_herd');

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'klres_herd', id
FROM goat WHERE db = 'klres';

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'cpres_herd', id
FROM goat WHERE db = 'cpres';

Ну и последний штрих:

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('klres','klres_herd','fullres','pushdelta','false','30 minutes','false');

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('cpres','cpres_herd','fullres','pushdelta','false','30 minutes','false');

Так. отработало... Уф, аж вспотел...

Ну и не менее ответственный шаг - перезапуск bucardo, и пинок в задницу в виде ничего не значащено update по всем таблицам в реплицируемых схемах...

Bucardo перезапустили. Ошибок в логах нет, и синхронизации нет. Нужен костыль для пинка. Пишем...

--select con.conname, cc.column_name, cc.constraint_schema, con.contype, cl.relname
select 'update res.'||cl.relname||' set '||cc.column_name||'='||cc.column_name||';'
from pg_constraint con, information_schema.constraint_column_usage cc, pg_tables t, pg_class cl
where con.conname = cc.constraint_name
and con.contype = 'p'
and con.conrelid = cl.oid
and t.tablename = cl.relname
order by cl.relname

Пытаюсь выполнить результат и получаю ошибку:

ERROR: record "old" has no field "pk_act"
CONTEXT: PL/pgSQL function "bucardo_add_delta_u_pk_act" line 3 at SQL statement

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

delete from bucardo.sync;
delete from bucardo.herdmap;
delete from bucardo.herd;
delete from bucardo.goat;
delete from bucardo.dbmap;
delete from bucardo.dbgroup;
delete from bucardo.db;

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('klres','10.77.11.52','klres','klres','klres_klres_klres');

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('fullres','10.77.11.207','fullres','res','res_res');

INSERT INTO bucardo.dbgroup(name) VALUES('res_slaves');
INSERT INTO bucardo.dbgroup(name) VALUES('res_master');

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('klres','res_slaves');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('fullres','res_master');

insert into bucardo.goat(db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('klres','res','act','pk_act','bigint','false');

INSERT INTO bucardo.herd(name) VALUES('klres_herd');

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'klres_herd', id
FROM goat WHERE db = 'klres';

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('klres','klres_herd','fullres','pushdelta','false','5 minutes','false');

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

################################################################

res (10.77.11.207)

CREATE LANGUAGE plpgsql;
CREATE LANGUAGE plperlu;

CREATE TABLE res._bucardo_cars
( id double precision NOT NULL,
car character varying(20),
nomer character varying(12),
CONSTRAINT pk__bucardo_cars PRIMARY KEY (id)
) WITH (OIDS=FALSE);

CREATE TABLE res._bucardo_fio
( id double precision NOT NULL,
fio character varying(100),
CONSTRAINT pk__bucardo_fio PRIMARY KEY (id)
) WITH (OIDS=FALSE);

res (10.77.11.52)

CREATE LANGUAGE plpgsql;
CREATE LANGUAGE plperlu;

CREATE TABLE res._bucardo_cars
( id double precision NOT NULL,
car character varying(20),
nomer character varying(12),
CONSTRAINT pk__bucardo_cars PRIMARY KEY (id)
) WITH (OIDS=FALSE);

CREATE TABLE res._bucardo_fio
( id double precision NOT NULL,
fio character varying(100),
CONSTRAINT pk__bucardo_fio PRIMARY KEY (id)
) WITH (OIDS=FALSE);

klres (10.77.11.52)

CREATE TABLE res._bucardo_cars
( id double precision NOT NULL,
car character varying(20),
nomer character varying(12),
CONSTRAINT pk__bucardo_cars PRIMARY KEY (id)
) WITH (OIDS=FALSE);

CREATE TABLE res._bucardo_fio
( id double precision NOT NULL,
fio character varying(100),
CONSTRAINT pk__bucardo_fio PRIMARY KEY (id)
) WITH (OIDS=FALSE);

bucardo(10.77.11.207)

delete from bucardo.sync;
delete from bucardo.herdmap;
delete from bucardo.herd;
delete from bucardo.goat;
delete from bucardo.dbmap;
delete from bucardo.dbgroup;
delete from bucardo.db;

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('slave_res_52','10.77.11.52','res','res','res');
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('slave_klres_res_52','10.77.11.52','klres','klres','klres_klres_klres');
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('master_res','10.77.11.207','res','res','res_res');

INSERT INTO bucardo.dbgroup(name) VALUES('slaves_res');
INSERT INTO bucardo.dbgroup(name) VALUES('master_obl');

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('slave_res_52','slaves_res');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('slave_klres_res_52','slaves_res');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('master_res','master_obl');

INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_res_52','res','_bucardo_fio','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_res_52','res','_bucardo_cars','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','_bucardo_fio','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','_bucardo_cars','id','bigint','false');

INSERT INTO bucardo.herd(name) VALUES('herd_res_52');
INSERT INTO bucardo.herd(name) VALUES('herd_klres_res_52');

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'herd_res_52', id
FROM goat WHERE db = 'slave_res_52';
INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'herd_klres_res_52', id
FROM goat WHERE db = 'slave_klres_res_52';

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_res_52','herd_res_52','master_res','pushdelta','false','5 minutes','false');
INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_klres_res_52','herd_klres_res_52','master_res','pushdelta','false','5 minutes','false');

root(10.77.11.207 ssh)

./bucardo_ctl start 'New'

Дальше в каждой схеме делаем пинок, как описано выше.

В логах bucardo после этого имеем:

Nov 25 09:19:41 vmsles10 Bucardo[7286]: KID Warning! Child for "sync_res_52" was killed at line 3822: DBD::Pg::db do failed: ERROR: доступ запрещён для связи pg_class main error: none source error: none target error: 7
Nov 25 09:19:42 vmsles10 Bucardo[7289]: KID Warning! Child for "sync_klres_res_52" was killed at line 3822: DBD::Pg::db do failed: ERROR: доступ запрещён для связи pg_class main error: none source error: none target error: 7

Непонимайу!!! Все юзеры суперюзеры!!! По крайней мере через pgAdmin. Даем руками:

alter role res superuser;
alter role klres superuser;

Оппачки. А так работает. Так что суперюзеров давать ТОЛЬКО РУКАМИ!!!

Так, пытаемся добавить одну из рабочих таблиц схемы klres, на которой не работала репликация...

res(10.77.11.207)

CREATE TABLE res.act
(
code double precision NOT NULL,
"name" character varying(50),
dategen timestamp with time zone NOT NULL,
"content" character varying(100),
valuekwt double precision NOT NULL,
calcvalue double precision NOT NULL,
startdate timestamp with time zone NOT NULL,
enddate timestamp with time zone NOT NULL,
isincourt double precision NOT NULL,
topayvalue double precision NOT NULL,
recordpontrfcode double precision NOT NULL,
statuscode double precision NOT NULL,
thefttypecode double precision NOT NULL,
modify_time double precision,
domain_info character varying(100) NOT NULL,
agreedpower double precision,
receptioncapacty double precision,
hoursusageperday double precision,
summrprdstrtmnth double precision,
summerperdstrtdy double precision,
wintrprdstrtmnth double precision,
winterperdstrtdy double precision,
changingdate timestamp with time zone,
blanknumber character varying(50) NOT NULL,
considerationdat timestamp with time zone,
verdict text,
formula text,
phases double precision,
commissionformula text,
coordinatedtypecode double precision,
coordinatedtypedate date,
CONSTRAINT pk_act PRIMARY KEY (code)
)
WITH (OIDS=FALSE);

bucardo(10.77.11.207)

INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','act','code','bigint','false');

root(10.77.11.207 ssh)

./bucardo_ctl stop 'add Act'
./bucardo_ctl start 'add Act'

Делаем пинок таблицы act

klres(10.77.11.52)

update res.act set code=code;

И получаем:

ERROR: record "old" has no field "pk_act"
CONTEXT: PL/pgSQL function "bucardo_add_delta_u_pk_act" line 3 at SQL statement
********** Ошибка **********
ERROR: record "old" has no field "pk_act"
SQL state: 42703
Контекст:PL/pgSQL function "bucardo_add_delta_u_pk_act" line 3 at SQL statement

Блин. Это при том, что

update res._bucardo_cars set id=id
update res._bucardo_fio set id=id

работает... Где провтык???

Порылся, и заметил замечательную закономерность. На таблице _bicardo_cars в триггере bucardo_add_delta_u ссылка на процедуру bucardo.bucardo_add_delta_u_id(), а в act на процедуру bucardo.bucardo_add_delta_u_pk_act(). Обратите внимание, что в первом случае в имени процедуры указано поле первичного ключа, а во втором случае имя первичного ключа... Возможно, в этом проблема... Варианты???

Возможно code зарезервировано для служебных целей... Пробуем:

bucardo(10.77.11.207)

delete from bucardo.goat where tablename = 'act';
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','act','"code"','bigint','false');

root(10.77.11.207 ssh)

./bucardo_ctl stop 'add Act code'
./bucardo_ctl start 'add Act code'

Пинок:

klres(10.77.11.52)

update res.act set code=code;

Блин. То же самое. Как вариант удалить объекты bucardo связанные с таблицей act в klres и заново сделать конфигурацию для этой таблицы...

bucardo(10.77.11.207)

delete from bucardo.goat where tablename = 'act';

klres(10.77.11.52)

DROP TRIGGER bucardo_add_delta_d ON res.act;
DROP TRIGGER bucardo_add_delta_i ON res.act;
DROP TRIGGER bucardo_add_delta_u ON res.act;
DROP TRIGGER bucardo_triggerkick_klres ON res.act;
DROP FUNCTION bucardo.bucardo_add_delta_d_pk_act();
DROP FUNCTION bucardo.bucardo_add_delta_i_pk_act();
DROP FUNCTION bucardo.bucardo_add_delta_u_pk_act();

bucardo(10.77.11.207)

INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','act','"code"','bigint','false');

Самое интересное - триггера на таблице не появились. Наверное это происходит в другой момент... Ищем в какой...

delete from bucardo.sync where name = 'sync_klres_res_52';

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_klres_res_52','herd_klres_res_52','master_res','pushdelta','false','5 minutes','false');

Нет, ничего. Перезапустим bucardo...

Ничего... Так. Нужно найти, как вычистить все объекты bucardo в схеме klres и завести все заново...

root(10.77.11.207)

./bucardo_ctl stop 'drop bucardo from klres'

klres(10.77.11.52)

drop schema bucardo cascade;

Вроде так. Все триггера с таблиц уехали, и схема тоже... Теперь:

bucardo(10.77.11.207)

delete from bucardo.sync where name = 'sync_klres_res_52';
delete from bucardo.herdmap where herd = 'herd_klres_res_52';
delete from bucardo.herd where name = 'herd_klres_res_52';
delete from bucardo.goat where db = 'slave_klres_res_52';
delete from bucardo.dbmap where db = 'slave_klres_res_52';
delete from bucardo.db where name = 'slave_klres_res_52';

Мда, погорячился я... Схему bucardo в klres можно было выносить только после удаления всего klres-овского хозяйства из таблиц bucardo...

delete from bucardo.sync where name = 'sync_klres_res_52';
delete from bucardo.herd where name = 'herd_klres_res_52';

не отработали. Ругаются, блин...

Ну да ладно. Руками некоторые объекты bucardo в klres посоздавал по аналогии с cpres, и все получилось. Теперь заново настраиваем:

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('slave_klres_res_52','10.77.11.52','klres','klres','klres_klres_klres');

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('slave_klres_res_52','slaves_res');

INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','_bucardo_fio','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','_bucardo_cars','id','bigint','false');
INSERT INTO bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy)
VALUES ('slave_klres_res_52','res','act','code','bigint','false');

INSERT INTO bucardo.herd(name) VALUES('herd_klres_res_52');

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'herd_klres_res_52', id
FROM goat WHERE db = 'slave_klres_res_52';

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_klres_res_52','herd_klres_res_52','master_res','pushdelta','false','5 minutes','false');

Во-о-о-о-о-о!!! Теперь похоже на правду. По крайней мере триггера сделаны по одному принципу.
Запускаем bucardo и раздаем пинки...

Пинки прошли удачно!!! Данные пошли!!!

Так, пробуем опять сделать все на рабочей схеме 2-х РЭСов - cpres, klres

######################################################################

bucardo(10.77.11.207)

delete from bucardo.sync;
delete from bucardo.herdmap;
delete from bucardo.herd;
delete from bucardo.goat;
delete from bucardo.dbmap;
delete from bucardo.dbgroup;
delete from bucardo.db;

Потом в klres и cpres выносим схемы bucardo

res(10.77.11.207)

drop schema res cascade;
CREATE SCHEMA res AUTHORIZATION res;

В схеме res создаем такие-же объекты, как в klres. дальше:

bucardo(10.77.11.207):

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('slave_cpres','10.77.11.52','cpres','cpres','cpres_cpres_cpres');
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('slave_klres','10.77.11.52','klres','klres','klres_klres_klres');
INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('master_res','10.77.11.207','res','res','res_res');

INSERT INTO bucardo.dbgroup(name) VALUES('slaves_res');
INSERT INTO bucardo.dbgroup(name) VALUES('master_obl');

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('slave_klres','slaves_res');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('slave_cpres','slaves_res');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('master_res','master_obl');

А вот и ошибка. Для вставки данных в goat я использовал запрос

select 'insert into bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy) VALUES (''klres'',''res'','''||cl.relname||''','''||con.conname||''',''bigint'',''false'');'
from pg_constraint con, pg_class cl, pg_tables t
where t.schemaname = 'res'
and t.tablename = cl.relname
and con.contype = 'p'
and con.conrelid = cl.oid;

В данном запросе вместо поля PK используется его имя. Вот почему и не работало... Исправляем запрос:

select 'insert into bucardo.goat (db, schemaname,tablename,pkey,pkeytype,analyze_after_copy) VALUES (''slave_klres'',''res'','''
||cl.relname||''','''||cc.column_name||''',''bigint'',''false'');'
from pg_constraint con, pg_class cl, pg_tables t, information_schema.constraint_column_usage cc
where t.schemaname = 'res'
and t.tablename = cl.relname
and con.contype = 'p'
and con.conrelid = cl.oid
and cc.constraint_name = con.conname;

Естественно имя базы для каждого варианта свое...

Кстати, обнаружил, что на одной из таблиц PK был составной. Такого быть не должно. Только из одного поля.

INSERT INTO bucardo.herd(name) VALUES('herd_klres');
INSERT INTO bucardo.herd(name) VALUES('herd_cpres');

INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'herd_klres', id
FROM goat WHERE db = 'slave_klres';
INSERT INTO bucardo.herdmap (herd, goat)
SELECT 'herd_cpres', id
FROM goat WHERE db = 'slave_cpres';

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_cpres','herd_cpres','master_res','pushdelta','false','5 minutes','false');
INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_klres','herd_klres','master_res','pushdelta','false','5 minutes','false');

Запускаем bucardo, пинки, и о чудо, репликация пошла!!!

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

Комментариев нет: