powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / выбор модели репликации
21 сообщений из 21, страница 1 из 1
выбор модели репликации
    #38985562
файрбердд
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я знаю 2 модели репликации
1- на тригерах. После каждого изменения БД, триггер собирает запрос для второй БД

2- добавляется поле 'ismodifed' которое ставится в 1 после каждого изменения , и переносятся те поля которые изменились.


Какие есть плюсы и минусы у этих решений?
На вскидку у 2 проблемы с удалением и последовательностью вставки.
У 1 лишнее разбухание БД
...
Рейтинг: 0 / 0
выбор модели репликации
    #38985573
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Первая неэффективна при редкой репликации, когда между сессиями успевает обновиться
большая часть БД, причём некоторые записи - по несколько раз.
Вторая неэффективна при большом количестве записей, когда между сессиями обновляется
только малый процент от них.

Вторая может нарваться на конфликт обновлений, когда репликационная транзакция сбрасывает
флаг, а кто-то другой как раз запись опять меняет. Первая бесконфликтна.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986038
MikeDD
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Видел на прошлой работе вариант 2: с увеличением БД увеличивается время выгрузки реплики, т.к. приходится перечитывать все таблицы. Уж лучше было бы писать инфу о модификации записи в отдельный журнал вида ид таблицы, ид записи, тип модификации, но там этого почему-то сделано не было. Реплика выгружалась по 30-40 минут при БД в 30Гб.
У себя сделал вариант 1. Имхо лучше, чем 2, т.к. позволяет грузить данные в хронологическом порядке. Совет: не нужно собирать запрос в триггере, лучше передавать в реплике инфу о типе изменения данных и сами данные, а запросы с параметрами собирать уже при импорте реплики. Если к этому еще прикрутить повторное использование одинаковых по тексту запросов, можно добиться заметного прироста производительности, сэкономишь ресурсы проца и время на препарирование.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986055
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
файрбердд,

у меня по неопытности был сначала вариант 2. Просуществовал пару лет, затем задолбали конфликты. Да и перебор всех таблиц всегда - тоже плохо.
Переделал лет 10 назад на вариант 1: при изменении в таблицу логов добавляется запись (если такой не было) с ID таблицы и ID записи и тип изменения (I или D). Когда запускается репликатор он в снапшоте работает с этой таблицей, после работы - очищает ее.
Да, нужна таблица со списком участвующих в репликации таблиц. Для присвоения им ID. По этой же таблице создаются автоматически триггера для таблиц.
После этого проблем нет.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986056
miwaonline
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MikeDD Совет: не нужно собирать запрос в триггере, лучше передавать в реплике инфу о типе изменения данных и сами данные, а запросы с параметрами собирать уже при импорте реплики. Если к этому еще прикрутить повторное использование одинаковых по тексту запросов, можно добиться заметного прироста производительности, сэкономишь ресурсы проца и время на препарирование.
Да что ты говоришь?
Тоесть запрос вида
Код: sql
1.
select query_text from replication_log where id > :last_replication_id order by id


будет работать медленнее, чем серия препарированных запросов вида
Код: sql
1.
select <modified data> from <any table> where is_modified = 1


да еще и по разным таблицам? Ню-ню.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986068
Фотография Секретное имя пользователя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miwaonlineMikeDD Совет: не нужно собирать запрос в триггере, лучше передавать в реплике инфу о типе изменения данных и сами данные, а запросы с параметрами собирать уже при импорте реплики. Если к этому еще прикрутить повторное использование одинаковых по тексту запросов, можно добиться заметного прироста производительности, сэкономишь ресурсы проца и время на препарирование.
Да что ты говоришь?
Тоесть запрос вида
Код: sql
1.
select query_text from replication_log where id > :last_replication_id order by id


будет работать медленнее, чем серия препарированных запросов вида
Код: sql
1.
select <modified data> from <any table> where is_modified = 1


да еще и по разным таблицам? Ню-ню.А откуда берется last_replication_id ?
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986082
Шавлюк Евгений
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Секретное имя пользователяА откуда берется last_replication_id ?
В моем варианте:
Храню в отдельной таблице
1) последний_подтвержденный_принятый_мой_id
2) последний_принятый_их_id

Передаю этот id вместе с пакетом репликации.
Для каждого получателя генерирую пакет с "where id > :последний_подтвержденный_принятый_мой_id"
При получении игнорирую данные в которых ID <= последний_принятый_их_id
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986100
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miwaonlineзапрос вида
Накроется весьма забавным медным тазом на таблице с блобами.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986178
Фотография Attid
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
у меня сейчас запросы собираются и хранятся , в принципе все отлично но если ковычки затеряются в тексте то может встать запись. но это бывает редко и с не критичными таблицами.

хотя вариант в таблице "реплики" хранить ИД таблицы и ИД измененной записи и Тип изменения звучит интересно.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986183
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Attidзвучит интересно
Особенно это интересно в случае, когда запись вставили и поменяли пару раз ещё перед
запуском репликации. А уж когда изменения включают в себя ПК, то интересность вырастает до
захватывающих размеров.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986196
Фотография Attid
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov,

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

а в первом варианте возможно что одну запись поменяют в двух БД одновременно(между синхронизациями) на разные значения, после репликации значения поменяются, но будут разные. и бд не будут идентичны.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986203
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Attidизменение несколько раз записи перед репликаций ничего плохого не сделает,
сэкономит передачу.
"Эт вряд ли..." (с) Сухов.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986254
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry SibiryakovОсобенно это интересно в случае, когда запись вставили и поменяли пару раз ещё перед
запуском репликации.

В данном случае под "репликация" я подразумеваю процесс, после которого "не основная" база становится такой же, как "основная", на данный момент. Всё, что бывает в промежутках между этими "репликациями" - меня не интересует. Меня такой подход вполне устраивает.

Да, и ещё, я соврал (забыл, что я так начинал делать, но потом убрал). Я не проверяю, есть ли запись в таблице логов, просто всегда INSERT. Во-первых, так намного быстрее, во-вторых - не бывает проблем, когда при обработке логов в снапшоте добавится еще один лог по обрабатываемой записи, логи по которой будут удалены после обработки. Вот, а в "не основную" базу уходит запись с MAX(ID) лога этой записи.

Dimitry SibiryakovА уж когда изменения включают в себя ПК,
Такие изменения запрещены доп. триггером, конечно.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986306
miwaonline
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakovmiwaonlineзапрос вида
Накроется весьма забавным медным тазом на таблице с блобами.
Естесственно. Это, как по мне, главная проблема первого варианта. Не удивлен, что ты об этом в курсе. Вообще есть что-то в репликации, о чем ты не в курсе?

Секретное имя пользователяА откуда берется last_replication_id ?
Сохраняется из предыдущего сеанса связи. Изначально инициализируется тем, кто настраивает репликацию или нулем.

Attidа в первом варианте возможно что одну запись поменяют в двух БД одновременно(между синхронизациями) на разные значения, после репликации значения поменяются, но будут разные. и бд не будут идентичны.
От этого не застрахованы оба предложенных варианта.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986309
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miwaonlineесть что-то в репликации, о чем ты не в курсе?
Да. Когда я был на докладе по работе Галеры, там упоминались какие-то академические работы
и изыскания в которых я не понял ни слова. Но говорилось о них с таким придыханием, что
это должен быть полный переворот в данной области. С другой стороны, с тех пор прошло
шесть лет, а Галера ещё не захватила мир...
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986328
MikeDD
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miwaonlineMikeDD Совет: не нужно собирать запрос в триггере, лучше передавать в реплике инфу о типе изменения данных и сами данные, а запросы с параметрами собирать уже при импорте реплики. Если к этому еще прикрутить повторное использование одинаковых по тексту запросов, можно добиться заметного прироста производительности, сэкономишь ресурсы проца и время на препарирование.
Да что ты говоришь?
Тоесть запрос вида
Код: sql
1.
select query_text from replication_log where id > :last_replication_id order by id


будет работать медленнее, чем серия препарированных запросов вида
Код: sql
1.
select <modified data> from <any table> where is_modified = 1


да еще и по разным таблицам? Ню-ню.

А нафига ТСу генерить селективные запросы в триггерах, если в них и так ясно что и как меняли? Ты читать умеешь? Я говорил про запросы insert/delete/update, которые ТС собирается генерить в виде текста в триггерах. И таки да, влияет, подробнее тут: http://www.sql.ru/forum/836300/tyazhelaya-procedura-v-triggere
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986341
Фотография Секретное имя пользователя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miwaonlineСекретное имя пользователяА откуда берется last_replication_id ?
Сохраняется из предыдущего сеанса связи. Изначально инициализируется тем, кто настраивает репликацию или нулем.

Шавлюк ЕвгенийСекретное имя пользователяА откуда берется last_replication_id ?
В моем варианте:
Храню в отдельной таблице
1) последний_подтвержденный_принятый_мой_id
2) последний_принятый_их_id

Передаю этот id вместе с пакетом репликации.
Для каждого получателя генерирую пакет с "where id > :последний_подтвержденный_принятый_мой_id"
При получении игнорирую данные в которых ID <= последний_принятый_их_id

а кто ид генерит и в какой момент ?
длинные транзакции нормально реплицируются ?
меня смущает сравнение ид на больше-меньше
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986390
Шавлюк Евгений
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Структура таблиц
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
CREATE DOMAIN DSYSREGION AS
INTEGER
DEFAULT 1;

DECLARE EXTERNAL FUNCTION BASE_ID
RETURNS INTEGER BY VALUE
ENTRY_POINT 'f_1' MODULE_NAME 'consts';

COMMENT ON EXTERNAL FUNCTION BASE_ID IS 
'Идентификатор БД';

CREATE GENERATOR BASE_ID;
SET GENERATOR BASE_ID TO 1;

COMMENT ON GENERATOR BASE_ID IS 
'Идентификатор БД';

-- Для каждой БД значение по-умолчанию домена DSYSREGION функции BASE_ID() и генератора BASE_ID должны быть равны

CREATE GENERATOR GEN_TRANSACTION;


CREATE TABLE SYS$TRANSACTIONS (
    ID_TRANSACTION  DID NOT NULL /* DID = INTEGER NOT NULL */,
    ID_HOST         DID NOT NULL /* DID = INTEGER NOT NULL */,
    SYSDAY          DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */
);
COMMENT ON TABLE SYS$TRANSACTIONS IS
'Репликация. История формирования пакетов репликации для каждого региона';

ALTER TABLE SYS$TRANSACTIONS ADD CONSTRAINT PK_SYS$TRANSACTIONS PRIMARY KEY (ID_TRANSACTION, ID_HOST);

CREATE TABLE UPDATE_LOG (
    ID_TABLE        DID /* DID = INTEGER NOT NULL */,
    ID_RECORD       DID /* DID = INTEGER NOT NULL */,
    OPER            DSMALL NOT NULL /* DSMALL = SMALLINT */,
    ID_TRANSACTION  DID NOT NULL /* DID = INTEGER NOT NULL */,
    SYSDAY          DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */,
    SYSUSER         DSYSUSER  /* DSYSUSER = VARCHAR(12) DEFAULT CURRENT_USER */,
    SYSREGION       DSYSREGION /* DSYSREGION = INTEGER DEFAULT 1 */,
    SYSTRANSACTION  DSYSTRANSACTION /* DSYSTRANSACTION = INTEGER DEFAULT current_transaction */
);

COMMENT ON TABLE UPDATE_LOG IS 
'Репликация. Лог обновлений';


ALTER TABLE UPDATE_LOG ADD CONSTRAINT PK_UPDATE_LOG PRIMARY KEY (ID_TRANSACTION);
CREATE INDEX UPDATE_LOG_IDX1 ON UPDATE_LOG (ID_RECORD, ID_TABLE, ID_TRANSACTION);
CREATE INDEX UPDATE_LOG_IDX2 ON UPDATE_LOG (ID_TABLE, ID_RECORD);

CREATE OR ALTER TRIGGER UPDATE_LOG_BI0 FOR UPDATE_LOG
ACTIVE BEFORE INSERT POSITION 0
AS
begin
  new.id_transaction = gen_id(gen_transaction,1);
  if (current_user = 'REPLICATOR' and gen_id(base_id, 0) = 1) then
    new.sysregion = coalesce(rdb$get_context('USER_TRANSACTION', 'FROMREGION'), new.sysregion);
end;

COMMENT ON TRIGGER UPDATE_LOG_BI0 IS 
'Если данные пришли через репликатор на главную базу (base_id = 1),
данные рассылаются остальным (кроме региона FROMREGION).
Контекстная переменная устанавливается в процедуре BEFORE_LOADDELTA';

CREATE TABLE UPDATE_TABLES (
    ID      DID /* DID = INTEGER NOT NULL */,
    NAME    DSTRING30 /* DSTRING30 = VARCHAR(30) DEFAULT '' NOT NULL */,
    PK      DSTRING30 /* DSTRING30 = VARCHAR(30) DEFAULT '' NOT NULL */,
    HOSTS   DSTRING84N /* DSTRING84N = VARCHAR(84) DEFAULT '' */,
    BODY    DLONGTEXT /* DLONGTEXT = BLOB SUB_TYPE 1 SEGMENT SIZE 256 */
);
ALTER TABLE UPDATE_TABLES ADD CONSTRAINT PK_UPDATE_TABLES PRIMARY KEY (ID);

CREATE OR ALTER TRIGGER UPDATE_TABLES_BIU0 FOR UPDATE_TABLES
ACTIVE BEFORE INSERT OR UPDATE POSITION 0
as
declare variable r int;
begin
  if (coalesce(new.pk,'') = '') then
  select first 1 min(trim(s.rdb$field_name))
  from RDB$INDICES i
  join RDB$INDEX_SEGMENTS s on (s.RDB$INDEX_NAME = i.RDB$INDEX_NAME)
  join RDB$RELATION_CONSTRAINTS c on (c.rdb$index_name = i.RDB$INDEX_NAME)
  join RDB$RELATION_FIELDS r on (r.RDB$RELATION_NAME = i.rdb$relation_name and r.rdb$field_name = s.rdb$field_name)
  where c.rdb$constraint_type in ('PRIMARY KEY', 'UNIQUE') and upper(i.rdb$relation_name) = upper(new.name)
  group by c.rdb$constraint_type, i.rdb$relation_name
  having count(*) = 1
  into new.pk;

  if (exists(select * from RDB$RELATION_FIELDS where RDB$RELATION_NAME = new.name and rdb$field_name = 'SYSREGION')) then r=1; else r=0;

new.body = '/*  '||trim(new.NAME)||'.'||trim(new.PK)||'  */

CREATE OR ALTER TRIGGER R_LOG_'||trim(new.NAME)||' FOR '||trim(new.NAME)||'
ACTIVE AFTER INSERT OR UPDATE OR DELETE POSITION 9999
AS
BEGIN
  if (gen_id(base_id, 0) <> base_id()) then exit;
  if (rdb$get_context(''USER_SESSION'', ''REPLICATOR_OFF'') = ''1'') then exit;'||
  iif(r = 1, '
  if (CURRENT_USER <> ''REPLICATOR'') then
  begin
    if ((updating or inserting) and (coalesce(new.sysregion,0) not in (0, gen_id(base_id,0)))) then exception regionerror;
    if (deleting and (coalesce(old.sysregion,0) not in (0, gen_id(base_id,0)))) then exception regionerror;
  end', cast('' as varchar(1)))||'
  if (CURRENT_USER <> ''REPLICATOR'' or gen_id(base_id, 0) = 1) then
  INSERT INTO UPDATE_LOG(ID_TABLE, ID_RECORD, OPER)
  VALUES('||cast(new.ID as Varchar(6))||', iif(deleting, old.'||trim(new.PK)||', new.'||trim(new.PK)||'), iif(updating, 1, iif(inserting, 2, 3)));
END;
';
end;

CREATE TABLE UPDATE_TRANSACTIONS (
    ID                DID NOT NULL /* DID = INTEGER NOT NULL */,
    NAME              DSTRING30N /* DSTRING30N = VARCHAR(30) DEFAULT '' */,
    SYSDAY            DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */,
    ID_TRANSACTION    DINT /* DINT = INTEGER */,
    ID_LASTRECEIVE    DINT /* DINT = INTEGER */,
    PHONE             DSTRING30N /* DSTRING30N = VARCHAR(30) DEFAULT '' */,
    EMAIL             DSTRING50 /* DSTRING50 = VARCHAR(50) */,
    CONF              DLONGTEXT /* DLONGTEXT = BLOB SUB_TYPE 1 SEGMENT SIZE 256 */,
    DATE_TRANSACTION  DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */,
    DATE_LASTRECEIVE  DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */,
    DATE_LAST         DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */,
    DATE_SEND         DSYSDATE /* DSYSDATE = TIMESTAMP DEFAULT CURRENT_TIMESTAMP */
);

COMMENT ON COLUMN UPDATE_TRANSACTIONS.DATE_TRANSACTION IS
'Дата формирования данных (регион)';

COMMENT ON COLUMN UPDATE_TRANSACTIONS.DATE_LASTRECEIVE IS 
'Последние подтвержденные данные (наши)';

COMMENT ON COLUMN UPDATE_TRANSACTIONS.DATE_LAST IS 
'Дата последнего получения';

COMMENT ON COLUMN UPDATE_TRANSACTIONS.DATE_SEND IS 
'Дата последней отравки';

ALTER TABLE UPDATE_TRANSACTIONS ADD CONSTRAINT PK_UPDATE_TRANSACTIONS PRIMARY KEY (ID);

CREATE OR ALTER PROCEDURE GET_MINTIME_CONNECTIONS
RETURNS (
    MIN_TIME TIMESTAMP)
AS
declare variable usr varchar(12) = 'SYSDBA';
declare variable psw varchar(12) = 'masterkey';
begin
--select first 1 ib_user, ib_psw from userlst
--where ib_user = 'SYSDBA' and visible = 1 and sysregion in (0, gen_id(base_id, 0))
--into usr, psw;

  for execute statement ('select min(mon$timestamp) from mon$transactions where mon$read_only = 0 and mon$attachment_id <> current_connection')
  as user :usr password :psw
  into min_time do
    suspend;
end;

COMMENT ON PROCEDURE GET_MINTIME_CONNECTIONS IS 
'Репликация. Время старта самой старой активной транзакции';

CREATE OR ALTER PROCEDURE AFTER_LOADDELTA (
    HOST_ID INTEGER,
    STARTID INTEGER,
    ENDID INTEGER)
AS
declare variable m integer;
begin
  select min(ID_LASTRECEIVE) from update_transactions into m;
-- Удаление мусора из истории
  delete from update_log where id_transaction <= :m;
  delete from sys$transactions where id_transaction <= :m;

  execute procedure on_after_loaddelta(:host_id, :startid, :endid);
end;

COMMENT ON PROCEDURE AFTER_LOADDELTA IS
'Репликация. После приема информации';

CREATE OR ALTER PROCEDURE AFTER_SAVEDELTA (
    HOST_ID INTEGER,
    STARTID INTEGER,
    ENDID INTEGER)
AS
begin
  exit;
end;

COMMENT ON PROCEDURE AFTER_SAVEDELTA IS 
'Репликация. После формирования пакета информации';

CREATE OR ALTER PROCEDURE BEFORE_LOADDELTA (
    HOST_ID INTEGER,
    STARTID INTEGER,
    ENDID INTEGER)
AS
begin
--Блокировка от одновременных изменений
  update UPDATE_TRANSACTIONS set ID = ID where id = :HOST_ID;
  rdb$set_context('USER_TRANSACTION', 'FROMREGION', :HOST_ID);
end;

COMMENT ON PROCEDURE BEFORE_LOADDELTA IS
'Репликация. Перед началом приема пакета информации';


CREATE OR ALTER PROCEDURE BEFORE_SAVEDELTA (
    HOST_ID INTEGER,
    OLDEST_TR INTEGER,
    ENDID INTEGER)
RETURNS (
    END_ID INTEGER)
AS
declare variable max_tr integer;
declare variable id_table integer;
declare variable id_record integer;
declare variable min_id_trans integer;
declare variable max_id_trans integer;
declare variable ins_count integer;
declare variable upd_count integer;
declare variable del_count integer;
declare variable startid int;
declare variable min_time timestamp;
begin
--Блокировка от одновременных изменений
  update UPDATE_TRANSACTIONS set ID = ID where id = base_id();

  select ID_LASTRECEIVE from UPDATE_TRANSACTIONS where id = :host_id into startid;
  select min_time from get_mintime_connections into min_time;
  min_time = coalesce(min_time, current_timestamp - 3*3e0/(24*60));

  /* Если транзакция старая (до b/r) то обнуляем */
  update update_log set
    SYSTRANSACTION = 0
  where id_transaction <= :endid and (SYSTRANSACTION > current_transaction or SYSTRANSACTION is null);

  select max(id_transaction) from sys$transactions into max_tr;
  max_tr = coalesce(max_tr, 0);

  select max(id_transaction) from update_log
  where id_transaction <= :endid  and systransaction <= :oldest_tr and sysday < :min_time
  into end_id;

  if (end_id is null) then
  begin
    end_id = startid;
    exit;
  end

  /* Сжатие данных */
  for select id_table, id_record,
             sum(iif(oper = 2, 1, 0)),
             sum(iif(oper = 1, 1, 0)),
             sum(iif(oper = 3, 1, 0)),
             min(id_transaction)
      from update_log
      where id_transaction between :max_tr + 1 and :end_id
      group by 2, 1
      having count(*) > 1
  into id_table, id_record, ins_count, upd_count, del_count, min_id_trans
  do
  begin
    if (del_count > 0) then
      delete from update_log
      where id_table = :id_table and id_record = :id_record and id_transaction between :min_id_trans and :end_id and oper in (1, 2);
    else
    if (ins_count > 0) then
      delete from update_log
      where id_table = :id_table and id_record = :id_record and id_transaction between :min_id_trans and :end_id and oper = 1;
    else
    if (upd_count > 0) then
      delete from update_log
      where id_table = :id_table and id_record = :id_record and id_transaction between :min_id_trans+1 and :end_id and oper = 1;
  end

  /* Удаление обновлений записи сделанных ранее, оставляем только последнюю операцию */
  for select id_table, id_record, max(id_transaction)
      from update_log
      where id_transaction <= :end_id and oper = 1
      group by 2, 1
      having count(*) > 1
  into id_table, id_record, max_id_trans do
  begin
    delete from update_log
    where id_table = :id_table and id_record = :id_record and id_transaction < :max_id_trans and oper = 1;
  end

  update or insert into sys$transactions (id_transaction, id_host)
  values (:end_id, :host_id)
  matching (id_transaction, id_host);
end;

COMMENT ON PROCEDURE BEFORE_SAVEDELTA IS 
'Репликация. Перед началом формирования пакета информации';


CREATE OR ALTER PROCEDURE ON_AFTER_LOADDELTA (
    HOST_ID INTEGER,
    STARTID INTEGER,
    ENDID INTEGER)
AS
begin
  Exit;
end;

COMMENT ON PROCEDURE ON_AFTER_LOADDELTA IS 
'Репликация. После приема информации. Специфика конкретной БД';




Триггер на каждую таблицу выглядит так (текст формируется автоматически в триггере UPDATE_TABLES_BIU0)
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
CREATE OR ALTER TRIGGER R_LOG_REPORT_PARAMS FOR REPORT_PARAMS
ACTIVE AFTER INSERT OR UPDATE OR DELETE POSITION 9999
AS
BEGIN
  if (gen_id(base_id, 0) <> base_id()) then exit;
  if (rdb$get_context('USER_SESSION', 'REPLICATOR_OFF') = '1') then exit;
  if (CURRENT_USER <> 'REPLICATOR') then
  begin
    if ((updating or inserting) and (coalesce(new.sysregion,0) not in (0, gen_id(base_id,0)))) then exception regionerror;
    if (deleting and (coalesce(old.sysregion,0) not in (0, gen_id(base_id,0)))) then exception regionerror;
  end
  if (CURRENT_USER <> 'REPLICATOR' or gen_id(base_id, 0) = 1) then
  INSERT INTO UPDATE_LOG(ID_TABLE, ID_RECORD, OPER)
  VALUES(2860, iif(deleting, old.ID, new.ID), iif(updating, 1, iif(inserting, 2, 3)));
END;



Коротко как работает:
таблица UPDATE_TRANSACTIONS
Содержит список БД с которыми эта БД обменивается. Для основной base_id=1
ID_TRANSACTION соответствует последнему подтвержденному значению UPDATE_LOG.ID_TRANSACTION на текущей БД
В пакете обновления содержится информация о последнем полученном обновлении
ID_LASTRECEIVE последнее получение от удаленной БД. Соответствует UPDATE_LOG.ID_TRANSACTION удаленной БД.
При приеме информации игнорируются все данные в пакете с ID_TRANSACTION < ID_LASTRECEIVE

Секретное имя пользователяа кто ид генерит и в какой момент ?
длинные транзакции нормально реплицируются ?
меня смущает сравнение ид на больше-меньше
1) ID генерится в триггере UPDATE_LOG_BI0

2) Длинные транзакции: есть процедура get_mintime_connections, которая возвращает дату старта самой старой не RO-транзакции.
Отправляю только те данные, которые старше этой транзакции. За это отвечает процедура BEFORE_SAVEDELTA

Описывать много, если интересно отвечу.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986455
miwaonline
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MikeDDА нафига ТСу генерить селективные запросы в триггерах, если в них и так ясно что и как меняли? Ты читать умеешь? Я говорил про запросы insert/delete/update, которые ТС собирается генерить в виде текста в триггерах. И таки да, влияет, подробнее тут: http://www.sql.ru/forum/836300/tyazhelaya-procedura-v-triggere
В тригерах никто ничего селектить не будет, само собой. Выборки будет делать репликатор и я продемонстрировал, в каком случае они будут значительно быстрее.

О том что долгоиграющая процедура будет тормозить триггер никто не спорит, но генерация запроса в тригере... Скажем так, лично я не мерял, но говорить можно о единицах миллисекунд.

Секретное имя пользователяа кто ид генерит и в какой момент ?
длинные транзакции нормально реплицируются ?
меня смущает сравнение ид на больше-меньше
ID генерирует реплицируемая база в момент вставки очередного сгенерированного запроса в replication_log.
Длительность транзакции не принципиальна.
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986460
miwaonline
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakovmiwaonlineесть что-то в репликации, о чем ты не в курсе?
Да. Когда я был на докладе по работе Галеры, там упоминались какие-то академические работы
и изыскания в которых я не понял ни слова. Но говорилось о них с таким придыханием, что
это должен быть полный переворот в данной области. С другой стороны, с тех пор прошло
шесть лет, а Галера ещё не захватила мир...
Ты об этом ? Ну, нам, рабоче-крестьянскому классу кластеры не интересны.

С другой стороны нормально, что человек, рассказывающий о своем проекте, говорит о нем с воодушевлением. И нормально, что другие не видят причин для такого воодушевления :)
...
Рейтинг: 0 / 0
выбор модели репликации
    #38986701
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miwaonlineнам, рабоче-крестьянскому классу кластеры не интересны.
Тогда оно ещё называлось просто репликатором и на кластеры не замахивалось.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
21 сообщений из 21, страница 1 из 1
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / выбор модели репликации
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]