logical replication: could not create file "state.tmp": File exists

Grigory Smolkin <[email protected]>
Newsgroups gmane.comp.db.postgresql.bugs
Message-ID <[email protected]>
Hello!

One of my colleagues encountered an out of space condition, which broke 
his logical replication setup.
It`s manifested with the following errors:

ERROR:  could not receive data from WAL stream: ERROR:  could not create 
file "pg_replslot/some_sub/state.tmp": File exists

I`ve digged a bit into this problem, and it`s turned out that in 
SaveSlotToPath() temp file for replication slot is opened with 'O_CREAT 
| O_EXCL' flags, which makes this routine as not very reentrant.

Since an exclusive lock is taken before temp file creation, I think it 
should be safe to replace O_EXCL with O_TRUNC.
Script to reproduce and patch are attached.

-- 
Grigory Smolkin
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
replication_bug.sh (application/x-shellscript, 1.4 KB)
#!/bin/bash

set -exu
set -o errexit

PREFIX=/home/gsmol/task/13_devel
PGDATA_PUB=${PREFIX}/data_pub
PGDATA_SUB=${PREFIX}/data_sub
PORT_PUB=15431
PORT_SUB=15430

${PREFIX}/bin/pg_ctl stop -m immediate -D ${PGDATA_PUB} || echo "all is well"
${PREFIX}/bin/pg_ctl stop -m immediate -D ${PGDATA_SUB} || echo "all is well"
rm -rf ${PGDATA_PUB} ${PGDATA_SUB}

${PREFIX}/bin/initdb -k -D ${PGDATA_PUB}
echo "port = ${PORT_PUB}" >> ${PGDATA_PUB}/postgresql.auto.conf
echo "wal_level = logical" >> ${PGDATA_PUB}/postgresql.auto.conf

${PREFIX}/bin/initdb -k -D ${PGDATA_SUB}
echo "port = ${PORT_SUB}" >> ${PGDATA_SUB}/postgresql.auto.conf

# start publisher cluster
${PREFIX}/bin/pg_ctl start -D ${PGDATA_PUB}

# start subscriber cluster
${PREFIX}/bin/pg_ctl start -D ${PGDATA_SUB}

# generate some data
${PREFIX}/bin/pgbench -i -s 10 -p ${PORT_PUB} postgres

# PUBLICATION
${PREFIX}/bin/psql -p ${PORT_PUB} -d postgres -c "CREATE PUBLICATION mypub FOR table pgbench_accounts"

# SUBSCRIPTION
${PREFIX}/bin/psql -p ${PORT_SUB} -d postgres -c "CREATE TABLE pgbench_accounts(aid int not null PRIMARY KEY, bid int,abalance int,filler char(84))"
${PREFIX}/bin/psql -p ${PORT_SUB} -d postgres -c "CREATE SUBSCRIPTION mysub CONNECTION 'host=localhost port=${PORT_PUB} dbname=postgres' PUBLICATION mypub"

# Imitate out_of_space/write_operation_error
touch ${PGDATA_PUB}/pg_replslot/mysub/state.tmp

# generate some write load
${PREFIX}/bin/pgbench -T100 -P1 -p ${PORT_PUB} postgres
0001-logical_replication_fix.patch (text/x-patch, 543 B)
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 21ae8531b3..ef415b44c8 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -1253,7 +1253,7 @@ SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel)
 	sprintf(tmppath, "%s/state.tmp", dir);
 	sprintf(path, "%s/state", dir);
 
-	fd = OpenTransientFile(tmppath, O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
+	fd = OpenTransientFile(tmppath, O_CREAT | O_TRUNC | O_WRONLY | PG_BINARY);
 	if (fd < 0)
 	{
 		ereport(elevel,
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.