2008-12-03

Репликация PostgreSQL+Bucardo. Рабочий вариант №1

Итак, после трех черновиков наступило время промышленной эксплуатации.

Описание задачи

Херсонская область. 19 структурных подразделений энергосбыта. Биллинг бытовых и промышленных потребителей. БД крутится в каждом филиале на SLES9 + PostgreSQL 8.2.5. Структура БД везде одинакова. Первичные ключи организованы таким образом, что уникальны не только в пределах филиала, но и области в целом. Каналы связи хреновые. 2 района с более-менее человеческой связью, 5 вообще только дозвон, остальные одно название. Есть головная контора, Херсоноблэнерго. Задача в том, чтоб филиалы, имеющие связь, реплицировались в головную контору с целью собрать общую БД быта и прома по области. Районы с дозвоном в данном мероприятии не участвуют, естественно.


Выбор компонентов

ОС центральной БД - SLES10 SP2 (SuSE Linux Enterprise Server 10 Service Pack 2)
БД - PostgreSQL 8.2.5 (так как во всех районах используется именно эта версия)
Репликация - Bucardo 3.0.9

Конфигурация сервера

IP-адрес сервера 10.77.11.71

Аппаратную часть не описываю - не актуально. Со временем сервер поменяем на более мощный.
Большую часть ПО ставил по умолчанию.

1. Доставил пакеты orarun, gtk-devel
2. Ни один из компонентов postgresql не устанавливал - версия не устраивает. Будем ставить нужную руками.
3. Целиком установил группу "C/C++ Compiler and Tools"
4. Что касается perl и его модулей... Вот листинг команды "rpm -qa | grep perl | sort"

limal-ca-mgm-perl-1.1.74-0.3
limal-nfs-server-perl-1.1.72-0.3
limal-perl-1.1.72-0.3
perl-5.8.8-14.7
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-PDA-Pilot-0.11.8-138.2
perl-Parse-RecDescent-1.80-259.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
perl-gettext-1.05-13.2
sax2-libsax-perl-7.1-125.54
yast2-perl-bindings-2.13.11-0.22

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

Предвариловка

В каталоге /files/install находится все, необходимое для установки и настройки.

/files/install/bucardo/perl_modules - набор perl-овских модулей, необходимых для установки и работы bucardo
/files/install/bucardo/Bucardo-3.0.9.tar.gz - собственно сам bucardo
/files/install/postgresql - rpm пакеты с postgresql 8.2.5. Вот их листинг:

postgresql-8.2.5-4.1.i586.rpm
postgresql-contrib-8.2.5-4.1.i586.rpm
postgresql-debuginfo-8.2.5-4.1.i586.rpm
postgresql-devel-8.2.5-4.1.i586.rpm
postgresql-docs-8.2.5-4.1.i586.rpm
postgresql-libs-8.2.5-4.1.i586.rpm
postgresql-pl-8.2.4-2.nosrc.rpm
postgresql-plperl-8.2.4-2.i586.rpm

Принятые условные обозначения

/dir/anotherdir/filename.fff - обозначение файлов и каталогов, листинг файлов
root#
- обозначение консоли, открытой под root
postgres>
- обозначение консоли, открытой под непривилегированным пользователем (в данном случае postgres)
rpm -qa | grep perl | sort
- команда, выполняемая в каком-либо интерпретаторе (bash, psql...), результаты, возвращенные работой команды...

PostgreSQL


Устанавливаем PostgreSQL:

root# cd /files/install/postgresql
root# rpm -ivh *

Устанавливаем необходимые параметры ядра:

root# echo "kernel.shmmax = 1000000000" >> /etc/sysctl.conf
root# sysctl kernel.shmmax=1000000000

Делаем первый запуск PostgreSQL:

root# /etc/init.d/postgresql start

Редактируем конфиги postgresql:

Листинг /var/lib/pgsql/data/pg_hba.conf:

local all all ident sameuser
host all all 127.0.0.1/32 ident sameuser
host all all 0.0.0.0/0 password

Листинг /var/lib/pgsql/data/postgresql.conf:

#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
listen_addresses = '*'
max_connections = 100
superuser_reserved_connections = 5
#------------------------------------------------------------------------------
# RESOURCE USAGE (except WAL)
#------------------------------------------------------------------------------
max_fsm_pages = 153600
max_stack_depth = 2MB
shared_buffers = 500MB
temp_buffers = 30MB
work_mem = 5MB
#------------------------------------------------------------------------------
# WRITE AHEAD LOG
#------------------------------------------------------------------------------
fsync = off
#------------------------------------------------------------------------------
# QUERY TUNING
#------------------------------------------------------------------------------
constraint_exclusion = on
#------------------------------------------------------------------------------
# ERROR REPORTING AND LOGGING
#------------------------------------------------------------------------------
log_line_prefix = '%s %h %u '
log_min_duration_statement = 60000
log_min_error_statement = warning
redirect_stderr = on
#------------------------------------------------------------------------------
# RUNTIME STATISTICS
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# AUTOVACUUM PARAMETERS
#------------------------------------------------------------------------------
autovacuum = off
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
datestyle = 'iso, dmy'
lc_messages = 'ru_RU.UTF-8'
lc_monetary = 'ru_RU.UTF-8'
lc_numeric = 'ru_RU.UTF-8'
lc_time = 'ru_RU.UTF-8'
#------------------------------------------------------------------------------
# LOCK MANAGEMENT
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# VERSION/PLATFORM COMPATIBILITY
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------

Перезапускаем PostgreSQL с новыми параметрами:

root# /etc/init.d/postgresql restart

После этого ставим PostgreSQL на автозапуск в YaST.
Меняем пароль пользователя postgres в БД:

root# su - postgres
postgres> psql
postgres=# ALTER ROLE postgres PASSWORD 'postgres';
postgres=# \q
postgres> exit

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

root# mkdir /u02/pgdata/res -p
root# chown postgres:postgres /u02/pgdata -R
root# su - postgres
postgres> psql
postgres=# CREATE ROLE res LOGIN PASSWORD 'respasswd';
postgres=# ALTER ROLE res SUPERUSER;
postgres=# CREATE TABLESPACE res OWNER res LOCATION '/u02/pgdata/res';
postgres=# CREATE DATABASE res WITH ENCODING='UTF8' OWNER=res TABLESPACE=res;
postgres=# \q
postgres> psql -h 10.77.11.71 -U res -W 'res'
res=# CREATE SCHEMA res AUTHORIZATION res;
res=# CREATE LANGUAGE plpgsql;
res=# CREATE LANGUAGE plperlu;
res=# \q
postgres> exit

Bucardo

Модули ставим в следующей последовательности:

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-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, потом Build, потом под 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 password
host all all 127.0.0.1/32 password
host all all 0.0.0.0/0 password

Устанавливаем переменные окружения:

user> export DBI_PASS=postgres
user> export TEST_VERBOSE=1
user> export DBI_USER=postgres
user> export DBI_DSN='dbi:Pg:dbname=postgres'

Перезапускаем PostgreSQL и только после этого делаем make test. Потом возвращаем pg_hba.conf на место.

Конфигурируем PostgreSQL под bucardo:

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> exit
root# cd /files/install/bucardo
root# tar -xvzf Bucardo-3.0.9.tar.gz
root# su - postgres
postgres> cd /files/install/bucardo/Bucardo/Bucardo-3.0.9/
postgres> psql -h 10.77.11.71 -U bucardo bucardo
bucardo=# CREATE LANGUAGE plpgsql;
bucardo=# CREATE LANGUAGE plperlu;
bucardo=# \q
postgres> psql -f bucardo.schema -h 10.77.11.71 -U bucardo bucardo
postgres> exit

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

В каталоге /files/install/bucardo/Bucardo/Bucardo-3.0.9 есть модуль DBIx-Safe-1.2.4. Устанавливаем его. make test не делаем.

Затем из этого же каталога устанавливаем модуль Bucardo-3.0.9. Перед установкой делаем следующее:

1. В скриптах bucardo-report, bucardo_ctl.rc, bucardo_rrd, check_bucardo_sync, Bucardo.pm, bucardo_ctl меняем в самом начале файла строку

#!/usr/localbin/perl -- -*-cperl-*-

на строку

#!/usr/bin/perl -- -*-cperl-*-

2. Экспортируем нелобходимые для теста переменные окружения:

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

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

local all all password
host all all 127.0.0.1/32 password

4.
Редактируем файл Bucardo-3.0.9/t/bucardo.test.data. Следующие строки должны быть такими:

DBNAME: bucardo
DBUSER: bucardo
DBHOST: 10.77.11.71

Только после этого ставим модуль:

perl Makefile.PL
make
make test
TEST_VERBOSE=1

Во время теста могут быть некоторые ошибки связанные с тестовыми базами, которые мы не настраивали. Игнорируем. Во время таста были созданы тестовые базы bucardo_test, bucardo_test1, bucardo_test2, bucardo_test3. После установки модуля их можно удалить.

Поднастраиваем postgres. Файл pg_hba.conf должен выглядеть так:

#local all all ident sameuser
#host all all 127.0.0.1/32 ident sameuser
#host all all 0.0.0.0/0 password
local all all password
host all all 127.0.0.1/32 password
host all all 0.0.0.0/0 password

Если нужно будет локально подключиться под postgres без ввода пароля, то закоментированные строки разкомментируем, незакомментированные комментируем, перезапускаем postgresql, и логинимся. Но потом возвращаем все на место, иначе bucardo работать не будет!

Ставим bucardo на место:

root# cp /files/install/bucardo/Bucardo/Bucardo-3.0.9 /opt -R

Редактируем файл /opt/Bucardo-3.0.9/bucardo_ctl. Устанавливаем в секции my $bcargs следующие значения:

my $bcargs = {
ctlquiet => 0,
ctlverbose => 0,
dbname => 'bucardo',
dbuser => 'bucardo',
dbpass => '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,
};

Создаем локального пользователя 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
root# mkdir /var/run/bucardo
root# chown bucardo /var/run/bucardo
root# mkdir /var/log/bucardo
root# chown bucardo /var/log/bucardo

Создаем скрипты для запуска и останова bucardo:

root# echo '#!/bin/bash' >> /etc/init.d/bucardo_start
root# echo '/opt/Bucardo-3.0.9/bucardo_ctl start "Start $(date +%Y-%m-%d.%H.%M.%S)"' >> /etc/init.d/bucardo_start
root# echo '#!/bin/bash' >> /etc/init.d/bucardo_stop
root# echo '/opt/Bucardo-3.0.9/bucardo_ctl stop "Stop $(date +%Y-%m-%d.%H.%M.%S)"' >> /etc/init.d/bucardo_stop
root# chmod oug+x /etc/init.d/bucardo_start
root# chmod oug+x /etc/init.d/bucardo_stop
root# ln -s /etc/init.d/bucardo_stop /etc/init.d/rc3.d/K10bucardo
root# ln -s /etc/init.d/bucardo_stop /etc/init.d/rc5.d/K10bucardo

Так как нам не нужно, чтобы bucardo стартовал при запуске системы, линки на автозапуск не делаем. Только на останов.

Настраиваем syslog-ng (у нас не используется старый syslog) на логирование bucardo. Редактируем файл /etc/syslog-ng/syslog-ng.conf.in.

В секцию # Filter definition добавляем:

filter f_bucardo {match('ucardo');};

Этот фильтр обязательно должен быть установлен до определения фильтра f_messages, который мы меняем следующим образом:

filter f_messages { not facility(news, mail) and not filter(f_iptables) and not filter(f_bucardo); };

В самый конец добавляем:

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

Потом в консоли:

root# SuSEconfig
root# /etc/init.d/syslog reload
root# mkdir /var/log/arhiv/bucardo -p

Настраиваем ротацию логов. Создаем файл /etc/logrotate.d/bucardo со следующим содержимым:

/var/log/bucardo/* {
dateext
compress
copytruncate
daily
missingok
notifempty
olddir /var/log/arhiv/bucardo
rotate 100
}

Так, с установкой bucardo вроде все. Теперь настраиваем собственно репликацию.

Настройка репликации. Добавление первой подчиненной БД.

Для начала, нам нужно на центральном сервере в ранее созданой БД сделать схему, аналогичную той, что будет реплицироваться из районов. Bucardo, к сожалению, не умеет реплицировать DDL, так что схему придется сделать руками. Выливаем ее из БД одного из районов, который имеет честь быть первым :). По логике вещей, схема во всех районах идентичная. Если нет, будет терзать программистов :). Ну и заливаем ее в БД res на хосте, где будет крутиться bucardo. Ну и малеко конфигурируем подчиненную БД. Все действия выполняются на том хосте, где будет крутиться bucardo.

root# su - postgres
postgres> mkdir ~/work
postgres> cd ~/work
postgres> pg_dump -h 10.77.25.3 -p 5432 -U postgres -F p -s -s -v -f "skres_schema.sql" -n 'res' "UTF8_sk"

postgres> psql -f skres_schema.sql -h 10.77.11.71 -U postgres res
postgres> psql -h 10.77.25.3 -U postgres UTF8_sk
UTF8_sk=# ALTER ROLE res SUPERUSER;
UTF8_sk=# CREATE LANGUAGE plperlu;
UTF8_sk=# CREATE LANGUAGE plpgsql;
UTF8_sk=# \q
postgres> exit

Теперь нам необходимо оптимизировать схему для заливки всех возможных районов. Что значит оптимизировать — удалить constraints и rules, свойственные каждому конкретному РЭСу. Иначе у нас в такие таблицы кроме одного конкретного РЭСа ни один не сможет данные записать, так как они не будут проходить проверку. На головной базе res выполняем запросы, а потом выполняем результаты этих запросов.

SELECT 'ALTER TABLE res.'||t.tablename||' DROP CONSTRAINT '||con.conname||';'
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 = 'c'
AND con.conrelid = cl.oid
AND cc.constraint_name = con.conname
AND cl.relname not in ('billsqlreport','dual','rp_abon')
ORDER BY con.conname;

SELECT 'DROP RULE '||rulename||' ON '||tablename||';'

FROM pg_rules
WHERE schemaname = 'res'
ORDER BY tablename, rulename;


Прежде, чем выполнять результаты запросов обязательно проверяем, что туда не залетело ничего левого, то есть того, что не должно быть удалено.

Сливаем дамп базы на сервере в районе и вытягиваем его на сервер с bucardo:

root# ssh root@10.77.25.3
root@remote# su - postgres -c "cd /files/ftp; pg_dump -C -F c -b -f pgsql.UTF8_sk "UTF8_sk""
root@remote# exit
root# wget -c --ftp-user=sit --ftp-password=shdgasd ftp://10.77.25.3/pgsql.UTF8_sk

Естественно, здесь подразумевается, что на РЭСовском сервере поднят FTP.

Ну а теперь непосредственно bucardo. Вносим информацию в БД bucardo. Для этого при помощи любого доступного SQL менеджера для PostgreSQL подключаемся к базе bucardo под пользователем bucardo и выполняем:

Описание всех участвующих в процессе репликации БД. Сюда вносятся как подчиненные БД, так и главная БД.

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('skres','10.77.25.3','UTF8_sk','res','supersecretpasswordofresinskres');

INSERT INTO bucardo.db (name, dbhost, dbname, dbuser, dbpass)
VALUES ('allres','10.77.11.71','res','res','respasswd');

Описание групп БД. Группы БД позволят управлять репликацией не отдельно каждой БД, а наборами БД, объединенными в группы. В принципе, логично было бы все подчиненные БД кинуть в отдельную группу, главную БД в отдельную группу. Так как я пока не знаю, что будет лучше, то так и сделаю.

INSERT INTO bucardo.dbgroup(name) VALUES('slave');
INSERT INTO bucardo.dbgroup(name) VALUES('master');

Привязываем БД к группам БД.

INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('skres','slave');
INSERT INTO bucardo.dbmap(db, dbgroup) VALUES('allres','master');

Вносим объекты, которые будут участвовать в репликации. К таким объектам относятся только таблицы. Так как таблиц у нас очень много, больше 400 штук на данный момент, то вставку будем осуществлять массировано при помощи скрипта. Выполняем его на подчиненной БД, а результаты в подключенной ранее БД bucardo. Обратите внимание, что часть таблиц из запроса исключаются. Это те, которые не участвуют в процессе репликации.

SELECT 'INSERT INTO bucardo.goat (db,schemaname,tablename,pkey,pkeytype,analyze_after_copy) VALUES (''skres'',''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
AND cl.relname not in ('billsqlreport','dual','rp_abon')
ORDER BY cl.relname;

ВНИМАНИЕ!!! Возможны составные первичные ключи на таблицах, чего для работы репликации быть не должно. Поэтому перед выполнением вышеуказанного запроса ищем все таблицы, PK в которые состоит более чем из одного поля и что-то с ними решаем!!! Ищем эти таблицы следующим образом:

SELECT cl.relname, count(*)
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
AND cl.relname not in ('billsqlreport','dual','rp_abon')
GROUP BY cl.relname
HAVING count(*) > 1;

Создаем так называемый herd для Скадовской БД. Для каждой последующей необходимо будет создавать свой. Herd — это группа объектов(goat), которые мы вносили в bucardo в предыдущем запросе.

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

Сопоставляем существующие goat созданному herd.

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

Ну и напоследок создаем правила синхронизации:

INSERT INTO bucardo.sync (name,source,targetdb,synctype,stayalive,checktime,analyze_after_copy)
VALUES('sync_skres','herd_skres','allres','pushdelta','false','60 minutes','false');

Во-о-о-от...
А потом оказалось, что таблицу log из процесса репликации необходимо исключить... Делаем это так:

DELETE FROM goat WHERE db='skres' AND tablename = 'log';

Но при этом остаются триггера и ссылки на таблицу в районской схеме bucardo... Смотрим в схеме bucardo в РЭСе созданные индексы на таблице bucardo_delta. Ищем среди них те, что созданы для таблицы log. Определяем, что tablename = 24926

DROP TRIGGER bucardo_add_delta_d ON res.log;
DROP TRIGGER bucardo_add_delta_i ON res.log;
DROP TRIGGER bucardo_add_delta_u ON res.log;
DROP TRIGGER bucardo_triggerkick_sync_skres ON res.log;

DELETE FROM bucardo.bucardo_delta
WHERE tablename = 24926;

DELETE FROM bucardo.bucardo_delta_targets
WHERE tablename = 24926;

DROP INDEX bucardo.bucardo_delta_res_log_rowid;
DROP INDEX bucardo.bucardo_delta_res_log_txn;

Исключили. Если еще какую-нибудь таблицу придется исключать, действуем по такому же принципу.

Всё, bucardo практически настроен. Теперь нам нужно залить бэкап РЭСа, только данные. После этого мы запустим bucardo и он начнет на них наворачивать изменения, произошедшие в удаленной базе после добавления правила синхронизации. Для того, чтоб залить из бэкапа только данные, нам нужно удалить все FK, залить бэкап, и восстановить все FK. Все просто.

Для удаления FK воспользуемся следующим запросом, результаты которого выполним в БД res на центральном сервере:

SELECT 'ALTER TABLE res.'||t.tablename||' DROP CONSTRAINT '||con.conname||';'
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 = 'f'
AND con.conrelid = cl.oid
AND cc.constraint_name = con.conname
ORDER BY con.conname;

Теперь заливаем из бэкапа только данные (я не помню, в каком каталоге находится pgsql.UTF8_sk, будем считать, что в ~/work относительно домашнего каталога пользователя postgres):

root# su - postgres
postgres> cd ~/work
postgres> /usr/bin/pg_restore -h 10.77.11.71 -p 5432 -U res -d res -a -v "pgsql.UTF8_sk"

Ну и восстанавливаем FK:

postgres> grep FOREIGN -B 1 skres_schema.sql > restore_fk.sql
postgres> psql -f restore_fk.sql -h 10.77.11.71 -U postgres res

Если необходимо все нахрен вычистить!!!

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, вместе с ней уйдут все созданные bucardo объекты.

Изменения в структуре БД

И вот то, чего я ждал, наконец произошло... Структура БД поменялась. Как назло я был в отпуске, программисты мне об этом сказали уже постфактум...

Продолбался долго, в итоге наваял 2 поста:

Тестирование
Правила


Читать далее

2008-11-19

Жопа :)

Взято со странички моего племянника, Сереги Закоры на http://vkontakte.ru

(_!_)-жопа обыкновенная
(__!__) - жирная жопа
(_._) - жопа плоская

(!) - тощая жопа
{_!_} - шикарная жопа
(_*_) - геморройная жопа
(_zzz_) - жопа усталая
(_?_) - жопа безмозглая
(_о_) - жопа пользованная
(_О_) - много раз пользованная
(_$_) - новорусская жопа
(_x_) - поцелуй меня в жопу!
(_Х_) - оставь мою жопу в покое!
(_^_) - заносчивая жопа.
("|") - волосатая жопа
. - жопа, вид из космоса
(_~_) - хитрая жопа
(_ _) - очень хитрая жопа
(_e=mc^2_) - умная жопа
(_Ъ_) - твердая жопа
(_Ь_) - мягкая жопа
(_GO_) - иди в жопу
->(_!_) пошел в жопу
(_SOS_) - жопа в беде
(_#_) - жопа зека
{________O________} очень большая жопа
(_100%_) - полная жопа (крылатая фраза)
(_!_)] - жопа с ручкой
(_!_)S - жопа с ручкой
->(_!_)-> через жопу (делать что-либо)
(_=_) какая, в жопу, разница?
(_Я_) - Я в жопе...
(__Я__) - Я в полной жопе...
(_Мы_) - Мы в жопе:
(_->._) - Иди в жопу!
Є(_!_)Э - жопа с ушами
c(_o_) - использованная жопа с ручкой
(_!_)(___!___)(_!_)(__!__) -Кинотеатор

Читать далее

2008-11-12

Учет трафика на cisco 3660

Итак, есть такая проблема - в удаленном районе кто-то жрет трафик. Засранца вычислить не могут.
Район приходит на CISCO 3660 через модем WATSON5 SHDSL 2Pair (это то, что я на нем смог прочитать...)

Полазив по задней стенке кошки, нашел, что кабель из можема заходит на интерфейс, подписанный как ETHERNET 0. Полазив в конфигах кошки вычислил, что интерфейс называется Ethernet2/0, что достаточно странно. Обычно надписи на оборудовании совпадают с тем, что указано в конфигах.


Порылся в google, кое что надыбал. Подключаюсь к cisco 3660 любым способом (консоль, telnet, ssh). Переходим в режим enable. Дальше командуем:

conf t
interface Ethernet2/0
ip accounting output-packets

Вот. Теперь вроде как можем посмотреть статистику по интерфейсу следующим образом:

show ip accounting

В конце выдается Accounting data age is 8. Я так понимаю, это указывается время, в течении которого собирается статистика. Есди нам нужно обнулить и посмотреть статистику с нового момента, делаем так:

clear ip accounting
show ip accounting

Информацию брал отсюда


Читать далее

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, пинки, и о чудо, репликация пошла!!!

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


Читать далее