The new error message is not great:
postgres=# select pg_replication_origin_session_setup('myorigin', 12345678);
ERROR: could not find replication state slot for replication origin
with OID 1 which was acquired by 12345678
Firstly, replication origin is not an OID. Secondly, it's a little
confusing because the "replication state slot" is in fact present.
However, it's currently inactive, i.e. not "acquired" by the given PID.
I propose to change that to:
postgres=# select pg_replication_origin_session_setup('myorigin', 12345678);
ERROR: replication origin with ID 1 is not active for PID 12345678
That's more in line with this neighboring message:
ERROR: replication origin with ID 1 is already active for PID 701228
I also wonder if the error code is appropriate. That error uses
ERRCODE_OBJECT_IN_USE, but if the problem is that the origin is
currently *not* active, that seems backwards. I didn't change that in
the attached patch, but it's something to think about.
The second patch rearranges the if-else statements to check those
conditions. I found the current logic hard to follow, this makes them
feel more natural, in my opinion at least. It has one user-visible
effect: If you call the function with acquired_pid != 0 and the origin
has no state slot, *and* there are no free slots, you previously got
this error:
postgres=# select pg_replication_origin_session_setup('other', 123);
ERROR: could not find free replication state slot for replication
origin with ID 2
HINT: Increase "max_active_replication_origins" and try again.
Now you get this:
postgres=# select pg_replication_origin_session_setup('other', 123);
ERROR: cannot use PID 123 for inactive replication origin with ID 2
Both error messages are more or less appropriate in that situation, but
I think the new behavior is slightly better. The fact that the origin is
inactive feels like the bigger problem here.
- Heikki
From 233e54b0b4dbdaa84910fb7cc453877391865839 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Tue, 3 Feb 2026 20:33:23 +0200
Subject: [PATCH 1/2] Improve error message
- Use the same wordings and terms as the neighboring messages
- Use 'curstate->roident' instead of 'node'. We've checked that they're
equal, but let's be consistent with the neighboring ereports().
---
src/backend/replication/logical/origin.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index c3271a6fd0e..ec040a039d3 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1197,8 +1197,8 @@ replorigin_session_setup(ReplOriginId node, int acquired_by)
{
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
- errmsg("could not find replication state slot for replication origin with OID %u which was acquired by %d",
- node, acquired_by)));
+ errmsg("replication origin with ID %d is not active for PID %d",
+ curstate->roident, acquired_by)));
}
/*
--
2.47.3
From 57b2e789b40297ba299af06882bd4076586300da Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Tue, 3 Feb 2026 20:29:54 +0200
Subject: [PATCH 2/2] Rearrange the checks in replorigin_session_setup()
Makes it easier to follow the logic.
---
src/backend/replication/logical/origin.c | 73 +++++++++++++-----------
1 file changed, 41 insertions(+), 32 deletions(-)
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index ec040a039d3..cb18b4b6686 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1185,55 +1185,64 @@ replorigin_session_setup(ReplOriginId node, int acquired_by)
if (curstate->roident != node)
continue;
- else if (curstate->acquired_by != 0 && acquired_by == 0)
+ if (acquired_by == 0)
{
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_IN_USE),
- errmsg("replication origin with ID %d is already active for PID %d",
- curstate->roident, curstate->acquired_by)));
+ /* With acquired_by == 0, we need the origin to be free */
+ if (curstate->acquired_by != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_IN_USE),
+ errmsg("replication origin with ID %d is already active for PID %d",
+ curstate->roident, curstate->acquired_by)));
+
+ if (curstate->refcount > 0)
+ {
+ /*
+ * The origin is in use, but PID is not recorded. This can
+ * happen if the process that originally acquired the origin
+ * exited without releasing it. To ensure correctness, other
+ * processes cannot acquire the origin until all processes
+ * currently using it have released it.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_IN_USE),
+ errmsg("replication origin with ID %d is already active in another process",
+ curstate->roident)));
+ }
}
-
- else if (curstate->acquired_by != acquired_by)
+ else
{
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_IN_USE),
- errmsg("replication origin with ID %d is not active for PID %d",
- curstate->roident, acquired_by)));
+ /*
+ * With acquired_by != 0, we need the origin to be active by the
+ * given PID
+ */
+ if (curstate->acquired_by != acquired_by)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_IN_USE),
+ errmsg("replication origin with ID %d is not active for PID %d",
+ curstate->roident, acquired_by)));
}
- /*
- * The origin is in use, but PID is not recorded. This can happen if
- * the process that originally acquired the origin exited without
- * releasing it. To ensure correctness, other processes cannot acquire
- * the origin until all processes currently using it have released it.
- */
- else if (curstate->acquired_by == 0 && curstate->refcount > 0)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_IN_USE),
- errmsg("replication origin with ID %d is already active in another process",
- curstate->roident)));
-
/* ok, found slot */
session_replication_state = curstate;
break;
}
-
- if (session_replication_state == NULL && free_slot == -1)
- ereport(ERROR,
- (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
- errmsg("could not find free replication state slot for replication origin with ID %d",
- node),
- errhint("Increase \"max_active_replication_origins\" and try again.")));
- else if (session_replication_state == NULL)
+ if (session_replication_state == NULL)
{
- if (acquired_by)
+ if (acquired_by != 0)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot use PID %d for inactive replication origin with ID %d",
acquired_by, node)));
/* initialize new slot */
+ if (free_slot == -1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+ errmsg("could not find free replication state slot for replication origin with ID %d",
+ node),
+ errhint("Increase \"max_active_replication_origins\" and try again.")));
+
session_replication_state = &replication_states[free_slot];
Assert(!XLogRecPtrIsValid(session_replication_state->remote_lsn));
Assert(!XLogRecPtrIsValid(session_replication_state->local_lsn));
--
2.47.3