Setting up replication in PostgreSQL with pglogical
By: Naveed Ahmed
Tags: Replication, Postgres, Postgresql

To get high-availability with PostgreSQL, you definitely want to set up replication. My preferred approach is to set up the database instances in geographically apart locations, to minimize the probability of them going down simultaneously.

There are built-in options like logical replication and streaming replication. Streaming replication doesn’t give you 2-way replication or mirroring, so that’s out for me. This article doesn’t get into 2-way replication yet. I’ll add it later or write another article to add 2-way replication. Here is a detailed comparison between pglogical and logical replication. The biggest plus with pglogical for me is conflict resolution.

I had some challenges setting this up. The provided pglogical documentation is terse and makes a lot of assumptions about your knowledge.

My environment:

PostgreSQL 12 (I don’t think pglogical has official support for 13 yet)

Centos 8

2 instances in different locations for more separation and better redundancy. I’m going to use IP addresses 1.1.1.1 for instance 1 and 2.2.2.2 for instance 2. The machine you connect from is 9.9.9.9. Please change these accordingly.

pglogical requires a superuser. I don’t like this, but it is what it is. The user I’m using is called appuser.

One major problem I had was trying to get pglogical running with the default PostgreSQL installation. You cannot use the RPM install for pglogical if you have PostgreSQL installed. You’ll have to install from source. Since my setup was new, I chose to reinstall PostgreSQL from the from the recommended repos. Here’s a line about this from official documentation.

To be able to install pglogical, these RPMs all require the PGDG PostgreSQL releases from http://yum.postgresql.org/. You cannot use them with stock PostgreSQL releases included in Fedora and RHEL.

sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm yum install postgresql12-server postgresql12-contrib curl https://access.2ndquadrant.com/api/repository/dl/default/release/12/rpm | bash yum -y install postgresql12-pglogical

This is the default data path for this install.

/var/lib/pgsql/12/data/

Add these lines in this file: /var/lib/pgsql/12/data/postgresql.conf

wal_level = ‘logical’ max_worker_processes = 10 # one per database needed on provider node max_replication_slots = 10 # one per node needed on provider node max_wal_senders = 10 # one per node needed on provider node shared_preload_libraries = ‘pglogical’ track_commit_timestamp = on pglogical.conflict_resolution = 'last_update_wins'

Enable trust login

This step is not essential, but it makes it simpler to manage locally. Change the following in /var/lib/pgsql/12/data/pg_hba.conf

local all all trust host all 127.0.0.1/32 trust

Allow external access

In /var/lib/pgsql/12/data/postgresql.conf set

listen_addresses = ‘*’

In /var/lib/pgsql/12/data/pg_hba.conf set

On instance 1:

host all all 2.2.2.2/32 md5 # other machine’s IP host all all 1.1.1.1/32 md5 # this machine’s IP host all all 9.9.9.9/32 md5 # your dev machine’s IP

On instance 2:

host all all 1.1.1.1/32 md5 # other machine’s IP host all all 2.2.2.2/32 md5 # this machine’s IP host all all 9.9.9.9/32 md5 # your dev machine’s IP

For some reason, the own machine’s IP is also required for this to work

Create user and database

On both instances, log in and create the user and databases.

psql -U postgres CREATE USER appuser WITH PASSWORD 'apppass' WITH superuser; CREATE DATABASE appdb WITH OWNER appuser;

Install CITEXT & pglogical

You can skip CITEXT — it allows for a case-insensitive text datatype, which I find very useful.

On both instances

psql -U postgres \c appdb CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; CREATE EXTENSION pglogical;

Set up provider

Instance 1

select pglogical.create_node(node_name := 'provider1', dsn := 'host=1.1.1.1 port=5432 dbname=appdb user=appuser password=apppass'); SELECT pglogical.replication_set_add_all_tables('default', ARRAY['public']); grant usage on schema pglogical to appdb;

Fix pg_current_xlog_location bug

On my setup, on instance 2, I encountered a bug where pglogical would call pg_current_xlog_location(). This has been replaced by pg_current_wal_lsn() since PostgreSQL 10. You can skip this step if you don’t run in to this bug.

CREATE FUNCTION pg_catalog.pg_current_xlog_location() RETURNS pg_lsn AS $$ BEGIN RETURN pg_current_wal_lsn(); END; $$ LANGUAGE plpgsql; CREATE FUNCTION pg_catalog.pg_last_xlog_receive_location() RETURNS pg_lsn AS $$ BEGIN RETURN pg_last_wal_receive_lsn(); END; $$ LANGUAGE plpgsql;

Set up subscriber

Make sure you set up the table schema on instance 2 identical to instance 1 before you do the following. It will not work otherwise.

Instance 2

SELECT pglogical.create_node( node_name := ‘subscriber1’, dsn := ‘host=2.2.2.2 port=5432 dbname=appdb user=appuser password=apppass’ ); SELECT pglogical.create_subscription( subscription_name := ‘subscription1’, provider_dsn := ‘host=1.1.1.1 port=5432 dbname=appdb user=appuser password=apppass’ );

Check status

select subscription_name, status FROM pglogical.show_subscription_status();

Check replication completed

SELECT pglogical.wait_for_subscription_sync_complete(‘subscription1’);

Drop subscription

During the process of getting this running, I had to drop and recreate the subscription and nodes several times. Here are the commands for that, if you need to do it.

SELECT pglogical.drop_subscription(‘subscription1’);

Drop node