Descripción General

PyReplica es un replicador asincrónico maestro-esclavo simple para PostgreSQL basado en Python, usando un disparador maestro en plpython, señales, secuencias, y un script cliente en python (influenciado por slony & londiste, pero mucho más simple y fácil).

Soporta además una configuración de multi-maestro asincrónica "perezosa" (replicación en ambos sentidos, cada base de datos es un maestro y un esclavo al mismo tiempo). Cuidado: ud., o su aplicación, deberá tener en cuenta el manejo de secuencias y la resolución de conflictos.

Ficha técnica

Implementación

PyReplica LEEME (wiki format):

PyReplica es un replicador asincrónico maestro-esclavo simple para PostgreSQL basado en Python, usando un disparador maestro en plpython, señales, secuencias, y un script cliente en python (influenciado por slony & londiste, pero mucho más simple y fácil).

Soporta además una configuración de multi-maestro asincrónica "perezosa" (replicación en ambos sentidos, cada base de datos es un maestro y un esclavo al mismo tiempo). Cuidado: ud., o su aplicación, deberá tener en cuenta el manejo de secuencias y la resolución de conflictos.

Recientemente se ha agregado soporte para servicio básico en windows, múltiples esclavos y replicación condicional mediante filtros escritos en Python (actualmente en fase de pruebas).

Esta programado en Python, apuntando a ser simple y flexible, permitiendo:

  • Fácil instalación (simplemente ejecutar un script sql en el servidor, y copiar un script demonio en el cliente, no se requiere compilación)
  • Fácil administración (en general no necesita administración para el uso normal, no es necesario aprender un nuevo conjunto de comandos o marco de trabajo)
  • Fácil adaptación (manual) (scripts python simple y extensibles, permitiendo filtrar y/o transformar los datos replicados)
  • Eficiencia (bajo impacto de uso de memoria y red, sin polling)
  • Multiplataforma: corre bajo linux y windos. Probado en Debian (disparador y demonio) y Windows XP (solo disparador)

No hace:

  • Fail over automático
  • Resolución de conflictos (pero advierte al detectar conflictos de actualización/eliminación y falla en conflictos de inserción o errores de integridad de datos)
  • Replicación de cambios de esquema (los comandos CREATE/ALTER/etc. deben ejecutarse manualmente en todos los servidores, igualmente la tabla replica_log puede ser usada para propagarlos)
  • No hay soporte para objetos grandes (replica basada en el oid podría ser soportada en próximas versiones)

Consiste en un disparador plpythonu de registro (py_log_replica) y un script demonio esclavo (pyreplica.py y daemon.py) El disparador almacena un registro de replicación (instrucciones de manipulación de datos INSERT,UPDATE,DELETE sobre las tablas afectadas en la tabla replica_log) y señala con un mensaje NOTIFY a las replicas. El script esclavo cliente se conecta a ambas bases de datos (maestra y esclava), escucha las señales NOTIFY en la base maestra, y re-ejecuta el registro de replica en la base esclava cuando llega dicha señal. Utiliza secuencias y transacciones para evitar perdida de datos.

El disparador detecta los cambios y los almacena usando la clave primaria de la base de datos. Por lo cual, si la tabla no tiene clave primaria, no puede ser replicada.

Como usa señales NOTIFY, la replicación es practicamente instantánea y más eficiente (sin polling). Si el cliente esta caido y las señales NOTIFY se pierden, cuando el cliente se conecta nuevamanete, automaticamente reejecuta los datos replicados "perdidos".

El script demonio de replicación usa transacciones de commit en dos fases (TPC), para asegurarse que ambos servidores se actualizaron correctamente. Puede configurarse para enviar notificaciones via correo electrónico (cuando el demonio de replicación inicia, se detiene o tiene algún error o advertencia)

Pyreplica soporta todos los tipos de datos soportados por plpythonu que pueden ser representados como cadenas (incluyendo bytea). Los tipos Numeric sin precisión definida parecen tener una perdida menor de precisión.

Mediciones de rendimiento simple muestran que el disparador solo es un 50% más lento que uno basado en C (como en slony-I), con los beneficios de que puede ser facilmente instalado, portado, mantenido y adaptado. (ver benchmarks.txt)

Ver INSTALAR.txt para el procedimiento de instalación.

Instalación

Como instalar PyReplica (wiki format):

Instalación Maestra

Instalar este sistema de replicación en la base maestra es tan simple como ejecutar el script master-install.sql incluido.

Se recomienda:

  1. Cerrar todos los clientes
  2. Realizar un respaldo (para recrear la base esclava y prevenir pérdidas de información en el proceso)
  3. Ejecutar el script de instalación del disparador
  4. Realizar pruebas simples para probar el disparador y permisos (algunas consultas INSERT, UPDATE, DELETE)
  5. Reiniciar todos los clientes (en este punto, la replicación estará funcionando!)

La función py_log_create_tg('nombre tabla') automagicamente detectará la clave primaria y creará el disparador de registro. Para cada tabla procesada, mostrará si el disparador fue instalado satisfactoriamente:

test=# SELECT py_log_create_tg(relname::text) FROM pg_class WHERE relname !~ '^(pg_|sql_)' AND relkind = 'r' AND relname != 'replica_log' ;
                    py_log_create_tg
--------------------------------------------------------
 table t has no pk constraint (couldn't be replicated)!
 created trigger on tt (id1,id2)
 created trigger on student (id)
(3 rows)
  • La Tabla t no puede ser replicada porque no tiene clave primaria, por lo que el disparador no fue instalado.
  • El disparador fue correctamente instalado en la tabla tt y student

Para instalar el disparador manualmente en una tabla, se puede ejecutar py_log_create_tg('tablename') o usar directamente la instrucción CREATE TRIGGER (ver el archivo sql).

IMPORTANTE: Leer master-install.sql antes de ejecutarlo, puede que deba descomentar algunas lineas para crear el lenguaje plpythonu o conceder privilegios.

Instalación Maestra en Debian

  • Asegurarse que el paquete plpythonu este instalado
  • Abrir la base de datos y ejecutar master-install.sql

Ejemplo de linea de comando:

apt-get install postgresql-plpython-8.1
psql somedb -U someuser < master-install.sql

Instalación Maestra en Windows

  • Verificar la opción PlPython? en el instalador de PostgreSQL de Windows (python24.dll and plpython.dll deben existir en C:\Archivos de programa\PostgreSQL\8.1\lib\ or equivalent)
  • Double-click en master-install.sql y completar el servidor, base de datos y nombre de usario, o abrir PgAdmin? III y copiar y pegar manualmente en una ventana de consulta.

Instalación Esclava

Aunque el script python podría ejecutarse en windows, actualmente solo se soporta linux (debian) para el demonio de replicación esclavo. Note: psycopg en windows no viene con el soporte para select por defecto, y después de haberlo compilado, parece no funcionar correctamente (la conexión es cerrada inesperadamente cuando se usan transacciones y listen) Hay un servicio muy básico que puede ser usado en windows (service.py), pero como no se soporta signal/select, usa polling.

Instalación Esclava en Debian

  • Restaurar el resguardo de la base de datos maestra
  • Instalar los paquetes requeridos (python2.4 y psycopg2)
  • Bajar los archivos de script a una ubicación bien definida (ej. /usr/local/pyreplica):
  • Copiar y modificar la configuración por cada base de datos esclava (ej. /etc/pyreplica/somedb.conf)
  • Hacer un enlace al script init.d
  • Agregar el usuario pyreplica (chequear uid/gid en su sistema y actualizar daemon.py)
  • Instalar el script init.d con update-rc.d (asi se inicia cuando la maquina se prende)
  • Iniciar pyreplica

Ejemplo de linea de comando:

apt-get install python2.4 python-psycopg2
svn co http://www.nsis.com.ar/svn/pyreplica /usr/local/pyreplica
cp /usr/local/pyreplica/sample.conf /etc/pyreplica/somedb.conf
vi /etc/pyreplica/somedb.conf
ln /usr/local/pyreplica/pyreplica.sh /etc/init.d/pyreplica -sf
useradd pyreplica -g 103 -u 103
update-rc.d pyreplica defaults
/etc/init.d/pyreplica start

En el archivo de registro (usualmente /var/log/pyreplica.log), se ver que esta sucediendo:

somedb Sun Jun  1 14:52:32 2008 Opening origin (master) connection
somedb Sun Jun  1 14:52:34 2008 Opening replica (slave) connection
somedb Sun Jun  1 14:52:34 2008 Encoding for this connections are UTF8 UTF8
somedb Sun  Jun  1 14:52:34 2008 Executing: UPDATE "test" SET "a"='123' WHERE "id2"='1' AND "id2"='2'
somedb Sun Jun  1 18:50:34 2008 Waiting for 'NOTIFY'
somedb Sun Jun  1 18:51:34 2008 Timeout(''keepalive'')!
somedb Sun Jun  1 18:51:34 2008 Waiting for 'NOTIFY'
somedb Sun Jun  1 18:52:34 2008 Timeout(''keepalive'')!

Si falla, se puede ejecutar pyreplica.py directamente para ver la salida directamente en la consola:

python pyreplica.py "dbname=master user=postgres host=remotehost" "dbname=slave user=postgres host=localhost"

(cambiar dbname, user, password y host)!

Configuración Multi-maestro

  • Se debe instalar el disparador y el demonio para las dos bases de datos.
  • En ambos archivos de configuración, setear el parámetro SKIP_USER al nombre del usuario postgresql que se conecta y procesa los registros de replicación (para prevenir re-ejecutar los registros propios replicados)
  • Debe tener cuidado con el manejo de secuencias (no se replican). Consejo: en cada base de datos, usar intervalos diferentes que no se solapen.
  • Debe evitar los conflictos o realizar un esquema de resolución de conflictos propio

Configuración Multi-esclavo

Para configurar multiples esclavos, agregar un campo booleano por cada esclavo en la tabla replica_log, y configurar SLAVE_FIELD con el nombre de dicho campo. Esta configuración actualmente no esta probada (observar los mecanismos de señales y bloqueos).

Configuración de Filtros

Pyreplica ahora puede replicar solo los eventos (INSERT, UPDATE, DELETE) que ud. elige. Puede filtrar los datos a ser replicados usando condiciones (expresiones python), evaluando los valores de los registros en new y old (diccionarios), etc.

  • Por ahora, ejecutar master-install-filter.sql
  • Agregar las condiciones de filtro:
    INSERT INTO py_log_filter (relname, event, condition) VALUES ('mytable','INSERT','new["field1"].startswith("Something") or new["field2"]==123');