PostHeaderIcon

Синхронизация данных Drupal сайтов через dblink`и PstgreSQL

Развитие PostgreSQL идет огромными шагами, функциональный возможности нарастают как снежный ком, катящийся с горы. До Oracle постгрес может еще не дорос, но как фриварная и бесплатная база данных Постгрес успешно живет и конкурирует не только как "база для Друпал", но и как хранилище данных энтерпрайз уровня, например 1С. Такие технологии как: кластеризация, репликация, стендбай и горячие бэкапы успешно работают и развиваются. Большой вклад в развитие PostgreSQL внес всем известный Skype, разработав и успешно применив у себя технологию репикации данных. На основе данной технологии успешно реализуется резервирование баз на серверах с любой архитектурой(*BSD, Linux, AIX, Solaris, Winows и т.д.) и без привязки к версии базы данных, при этом не важно сколько схем имеется в конкретной базе - можно реплицировать только те данные, которые необходимо, что делает данную технологию незаменимой при реализации standby для сервера, на котором расположены несколько проектов. Синхронизация master со standby производится только для нужных схем(по простому баз), а не полностью всех данных, как это делается, если использовать технологию standby от PostgreSQL, что позволяет размещать на резервном сервере базы, отличные от баз на master сервере.

Про систему репликации данных от Skype - Londiste(skytools) написано достаточно много, тут же я хочу коснутся репликации выборочных данных на уровне таблиц. Данная технология использовалась в одном из проектов, при создании двух смежных сайтов, для синхронизации учетных записей новых и существующих пользователей их сессий и т.д.

За основу взята технология DBLink, позволяющая связывать несколько баз данных между собой, чтение и запись к связанной базе производится через стандартные select`ы, insert`ы и update`ы.

1. включение поддержки dblink`ов для postgresql < 9.1:(для postgresql >= 9.1 установка еще проще, необходимо выполнить CREATE EXTENSION dblink;)

# cd /usr/ports/databases/postgresql90-contrib; make install clean

# su pgsql$ psql <dbname> < /usr/local/share/postgresql/contrib/dblink.sql


2. создание dblink`а (для postgresql > 9.2 все еще проще)выставить параметры коннекта и выполнить скрипт create_dblink.sql в базе сайта-источника(site1) под админом

--\set C_USER 'site1'


-- site2 db conn params --

\set DB_NAME 'site2'

\set U_PASS '123123'
\set S_NAME 'site2link'

\set U_NAME :DB_NAME

--- create dblink mapping ---

CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;

CREATE SERVER :S_NAME FOREIGN DATA WRAPPER postgresql OPTIONS (dbname :'DB_NAME');
CREATE USER MAPPING FOR :C_USER SERVER :S_NAME OPTIONS (user :'U_NAME', password :'U_PASS');
GRANT USAGE ON FOREIGN SERVER :S_NAME TO :C_USER;

---

3. выполнить скрипт replication.sql на базе сайта-источника(site1) под владельцем схемы создается процедура перликации данных(пример) c site1 на site2 :

--

CREATE OR REPLACE FUNCTION drupal_users_session_replication() RETURNS trigger

LANGUAGE plpgsql    AS $

DECLARE

uid bigint;

ses_uid bigint;

db_name varchar(32);

BEGIN
PERFORM dblink_connect('site2link');
SELECT tableowner FROM pg_tables WHERE tablename='users' INTO db_name;

SELECT * FROM dblink('SELECT replication_uid FROM drupal_users_replication_list WHERE uid='||NEW.uid||'

AND db_name='||quote_literal((db_name))||';') AS t(uid bigint) INTO uid;

SELECT * FROM dblink('SELECT uid FROM sessions WHERE sid='||quote_literal((NEW.sid))||';') AS t(uid bigint) INTO ses_uid;
IF (ses_uid IS NULL) THEN

PERFORM dblink_exec('INSERT INTO sessions (uid,sid,hostname,timestamp,cache,session)                            VALUES('||uid||','||quote_literal((NEW.sid))||','||quote_literal((NEW.hostname))||','||NEW.timestamp||',                                '||quote_literal((NEW.cache))||','||quote_literal((NEW.session))||');');

ELSE

PERFORM dblink_exec('UPDATE sessions                            SET uid='||uid||', hostname='||quote_literal((NEW.hostname))||', timestamp='||NEW.timestamp||',                                cache='||quote_literal((NEW.cache))||', session='||quote_literal((NEW.session))||'

WHERE sid='||quote_literal((NEW.sid))||';');    END IF;
PERFORM dblink_disconnect();
RETURN NEW;
END;

$;

--

и на нужные таблицы навешиваются тригеры, например:

--

CREATE TRIGGER drupal_users_session_replication

AFTER INSERT OR UPDATE ON sessions

FOR EACH ROW EXECUTE PROCEDURE drupal_users_session_replication();

--

4. выполнить скрипт drupal_users_repl_dest.sql на базе сайта-приемника(site2) под владельцем схемы создается синхронизирующая таблица, где будут хранится связи:
CREATE TABLE drupal_users_replication_list (

uid bigint NOT NULL,

trainer_uid bigint NOT NULL,

db_name varchar(32) NOT NULL

);

ALTER TABLE drupal_users_replication_list

ADD CONSTRAINT drupal_users_replication_list_pkey PRIMARY KEY (uid, trainer_uid, db_name);
-------------
CREATE OR REPLACE FUNCTION drupal_users_repl_dest() RETURNS TRIGGER AS$

BEGIN

DELETE FROM drupal_users_replication_list WHERE trainer_uid=OLD.uid;
RETURN OLD;
END;

$ LANGUAGE plpgsql;

CREATE TRIGGER drupal_users_repl_dest

AFTER DELETE ON users    FOR EACH ROW EXECUTE PROCEDURE drupal_users_repl_dest();

---

В результате сего шаманства, при входе пользователя на site1 данные его сессии переносятся в базу site2, куда пользователь сможет зайти без введения каких-либо логинов-паролей.
Это только пример, ибо постить весь код не интересно и бесполезно, го на основании его можно реализовать доступ к данным между любым количеством баз.

В заключение:Кто то может сказать - "а зачем так сложно, ведь можно все сделать на php", возражать не стану - можно, так же как можно timestamp инсертить в базу, получая его в PHP, вместо того что бы использовать все возможности баз данных, но это приемлемо для небольших сайтов, а на серьезных сайтах(например ГИБДД) это повлечет непозвольтельные временные затраты.