Resume data replication in Postgres and Node.js
How to resume replication from the point where the Node.js application was stopped
This article is a continuation of Real-time data Replication in Postgres and Node.js. Before reading this article, I recommend you read the previous one because it provides essential context to the points I cover.
In our previous article, we discussed how to replicate data from a Postgres database to a Node.js application in real-time using logical replication. However, if the Node.js application crashes or stops for some reason, the replication will cease, and we risk losing the data that our system produces in the meantime via another microservice or application.
In this article, I discuss how to resume replication from the last point where the Node.js application stopped by using a persistent replication slot in the Postgres database. This ensures that our application doesn't lose events produced by other microservices or applications during downtime.
Editor’s note: This is a cross-post written by Senior Software Developer, Manuel Spigolon. Manuel has his own blog at backend.cafe where you can subscribe for updates and find more great posts. Some of the links in the article point to Manuel’s personal GitHub account.
Creating a replication slot
To resume replication, we need to create a replication slot in the Postgres database. A replication slot is a logical entity that keeps track of changes happening in the database and sends them to the subscriber. The postgres
package we used in the previous article automatically created a replication slot for us, but it was not persistent, it was a `TEMPORARY` replication slot that was removed when the subscriber disconnected.
Since we want to resume replication from the last point where the Node.js application was stopped, we need to create a persistent replication slot. Let's create one in a new setup-replication.js
file:
We are using the `pgreplicationslots` view to check whether a replication slot with the given name already exists. If it doesn't exist then we create a new replication slot using the `pgcreatelogicalreplicationslot` function.
Note that we specified the `pgoutput` plugin in the function to decode the changes in the replication slot. This is the default plugin for logical replication, and it ships with Postgres. Be aware that there are other plugins, such as:
`test_decoding` plugin is the simplest plugin that ships with Postgres to start building your own custom plugin.
`wal2json`, which must be installed separately in the Postgres database, allowing you to use them in the
pgcreatelogicalreplicationslot
function.
Note that each plugin has its own advantages and disadvantages, so choose the one that best fits your use case. The biggest difference if you try to use test_decoding
versus pgoutput
is that the former does not accept a publication name as a parameter while the latter does. This means that you can use pgoutput
to filter the changes you want to replicate, while test_decoding
will replicate all changes in the database without filtering them!
Now, run the setup-replication.js
file to create a replication slot!
Configuring the consumer
In the previous article, we already created the setup-consumer.js
that creates the publications that our application is interested in. So, we can reuse the same file and just run it — if we haven't already.
As a reminder: you will first need to start the Postgres server and create the foo
database.
Resuming the replication
We are ready to create a new consumer-resume.js
file that will resume replication from the last point where the Node.js application was stopped, so let's jump into it:
This time, we are using the `pg-logical-replication package` to demonstrate the resuming of replication. Its low-level API provides us more control over the replication process, otherwise we would not be able to configure the Plugin to receive only the changes we are interested in.
The code can be explained as follows:
1: We are creating a new
LogicalReplicationService
instance and passing the connection options to it. Note that we are setting theacknowledge.auto
option tofalse
to manually acknowledge the changes; otherwise, they would be automatically acknowledged. By setting this option tofalse
, we gain even more control over the process.2: We are listening to the
data
event to receive changes from the replication slot.At this point, you should process the
log
and apply your business logic. In this case, we're just logging the changes to the console.
After processing the changes, you must acknowledge them using the
acknowledge
method; otherwise, the slot will not advance. Thelsn
(Log Sequence Number) is the unique identifier for each change in the database and is used to track changes in the replication slot.
3: We are creating a new
PgoutputPlugin
instance and passing it to thesubscribe
method to establish a connection with the replication slot.
To start the application, run the node consumer-resume.js
file, and it will begin receiving changes from the replication slot. If we did all the steps correctly, you can start the node producer.js
file that we wrote in the previous article to produce changes in the database and see the changes in the consumer application.
If you stop the consumer application by pressing Ctrl+C
, the replication will stop, and the slot will not move forward. However, if you start the consumer-resume.js
application again, it will resume replication from the last point where it was stopped! 🎉
Moreover, we can see that the output shows only the changes from the foo_odd
and fooupdateonly
publications, which we configured in the PgoutputPlugin
instance so we will see updates and inserts with odd id
numbers only:
Conclusion
In this article, we discussed how to resume replication from the last point where the Node.js application was stopped.
We created a persistent replication slot in the Postgres database and used the pg-logical-replication
package to demonstrate resuming replication. This ensures that our application doesn't lose data produced by other microservices or applications during downtime.
In doing so, we did not change the producer.js
file, which means that the producer can continue to produce changes in the database without any issues and the previous Publications setup is still valid: we just configured manually the replication slot and the new consumer.
Remember, the replication slot retains changes in the database until the slot is dropped or the changes are acknowledged by the subscriber. If not managed properly, this can lead to high disk usage because Postgres will keep the changes in the WAL logs indefinitely instead of removing them.
I hope you enjoyed this article and learned something new! Does it deserve a comment and a share? 🚀
Insight, imagination and expertly engineered solutions to accelerate and sustain progress.
Contact