Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wQ4YI-001GYB-1R for pgsql-hackers@arkaria.postgresql.org; Thu, 21 May 2026 14:39:10 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wQ4YG-00AMum-0G for pgsql-hackers@arkaria.postgresql.org; Thu, 21 May 2026 14:39:09 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wQ4YF-00AMue-2N for pgsql-hackers@lists.postgresql.org; Thu, 21 May 2026 14:39:08 +0000 Received: from mail-ua1-x933.google.com ([2607:f8b0:4864:20::933]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wQ4YE-00000000kFy-2tL9 for pgsql-hackers@lists.postgresql.org; Thu, 21 May 2026 14:39:08 +0000 Received: by mail-ua1-x933.google.com with SMTP id a1e0cc1a2514c-9568bae58f7so4087988241.3 for ; Thu, 21 May 2026 07:39:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1779374344; cv=none; d=google.com; s=arc-20240605; b=AY55s1aAkyuCQU8DjIp2dfsZCSj9d+bDGPchH5c2HWLWlVZHHTmIgGJ+SS1mU2i9PV /mZ9jZQH1Dw1JSxoF7PW0G9NWb+31e3Mzt1M3b8PsAc0kGB7CQeTiQBCnzuk1z5V+FiK WMPgBFuxOyt8HS/nKcfrvIvyqNcletnbNcQF47rU2H5+wrX03GJO7wtpm146iYWJHjle 2/+B5jO/scQi7fdnDNOUPRQkbXxSGoV0eqlPBLNYTmAJXm/vAVyOMlMaikVLwXeRvAS9 PKis+XHVbS33EyAbTNsPKO+asr/X2XrCwXX3+q/iRzLrVDLYlmv5S+UIXnaU9ElbDKXO QKFg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=fc6+HDC8ORAXRG4cVtPH6mNGOtffWpTXQCW2g4HiMpw=; fh=Hs7hiJ6OHYijheHx3wGop/zNgF5jzksZl/9dpq2fBxc=; b=iXsj91h8FbZwz3KHg7eZke2z2U6gBOA+BFkrHR/j/DIEugVyDls9IQJZBJhsYhkxIu ILrq7tUJ85KtuMDFUk7/RjfFsN25omBk9EYPW/KrlNhYhMYPfwp3EZ+FvLDXERQf0hau 0pK188LoPelMXtf/SQIn1mKkXWFE4WK6pZyKkgblBZdJ5Uio/7g/X1ovpym5TPRJILgS zQURJ0EeXWIz4S3ClQj8XKsjEtgV0dpLMB6W9I3l7VnabmHSiXaU4TQjGoIWqHYz03gl zzz4fEhtzCcmu8caJEY3m034jXOYz6A6ACuipqPTDZVjy22Fs0o3Y4iF4+TpXEXEaohc VqoQ==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779374344; x=1779979144; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=fc6+HDC8ORAXRG4cVtPH6mNGOtffWpTXQCW2g4HiMpw=; b=tBDKPBc4oWf7uXQGSO7uVXfVJG4ZfwC//MVFXj59U48c9QhVcwtrVgTy+LvKEWN/Pk 4WnvJMTjtcPeXV7nHWDj7FihoSkeZrINHq4innEfbunbA9RfYvty2qMMCbm8zwoYmxtA xDGz9pYNrccZGPwmCB8L1OZ9mnBrKpbIv6p7aSDF3u/8AKgXlQdaCJkIFfInijOku5F1 vMlHT6SkIfzFzzQHawMUXomPkEacBx6tkNx96kP+ANtqSTxq9E7MgLQjV3r+/x7bnE1Y BIL1KAacxp9ETQ3Qt454QhmEL+A3jx19Xrtri2p+cId5DmNsetNg29G/6yfP7Sou6iM6 kzjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779374344; x=1779979144; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=fc6+HDC8ORAXRG4cVtPH6mNGOtffWpTXQCW2g4HiMpw=; b=IXs9YhZIuf3E1S2RK2qj8CH8+b1T3AnF8m1T6EFfqzBalkOKs1O4QtPMWh+Hc0rBvT gxQ3Ivyq6Xvl28nnJRkQ+jja69bI25LqBq8WrAjcnZJS73i1IR6TewVJSxtpyll83WeU QzYjsMAbm7CFLOJZGWqzRtlVJdKe2Cc7q3IFVuom7SQggdDd2KufYJSgZXeFEwwQ4FyA WEbt2CvnxHgOW+ISIiYwKHEGpefjGscO4jhp4UjtgNHEDnFoz/ax/NEIGYxg3x34BC4D SEMHCnfPqNAdGFFfru87ZiNddnDMf7pGUUpK9k3O1DAyuZ7gVQdKwYyJr4GDK2QJ3Rbw VLww== X-Forwarded-Encrypted: i=1; AFNElJ+t1CX7K/kkSa9jqSRsMxWc3hYbQF3aVHGI2zPJrynMdqDKaRQ0TaUwBF5BGKxZMfEI5iNKVOD63sCBPPpj@lists.postgresql.org X-Gm-Message-State: AOJu0YyT5qe4QkaNOF/Rx0EzlCk4RLKcNyJN+xH2k1/X1P9LJpFtmOHS pajzv3wye+vIjF0ZV1yCv7j/Fwh/xooYCrEVAzok6qpnzceMpkXAvthEsA5x9jFN2ksMjbDxNEg VGW1IADNY3PgU8JTo91CQaNIAcys06Oc= X-Gm-Gg: Acq92OHyKAMX0HHwEcEkc+zf8sjlX3diwH6NUVIcRK8IjD+3ZFgHIk0zD9A91LfTOyW iGo5wZU8dVStydlTF2PUyF001UV0LOXo7g4cBRBIs6o9FA2xu2UNl8vjA/CHAIznIPI6vS+hF+8 tCEp+HPoe0WGNlMhiFVlsSPzEcSG66RqOLW1ChXhApMi7J5NI+kCTdvXLlHUzoK4iNV2PRyqd3M GBcCoYSZt1hQgbyr9X/8/Vc5zjpDmDkkXdOOjAPFeGYlN5lzplCpPQdlyGHYKywelU6eIuhoBPm eq94eNw= X-Received: by 2002:a05:6102:a46:b0:632:425e:85ea with SMTP id ada2fe7eead31-67396ab2367mr1879603137.16.1779374344305; Thu, 21 May 2026 07:39:04 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: SATYANARAYANA NARLAPURAM Date: Thu, 21 May 2026 07:38:53 -0700 X-Gm-Features: AVHnY4LgCNAksv7nse3QUdaxTiL7FFUpOpXuxfLWK_ugwZn8dVM51K6tFe4vlEo Message-ID: Subject: Re: [PATCH] Release replication slot on error in SQL-callable slot functions To: vignesh C Cc: Fujii Masao , PostgreSQL Hackers Content-Type: multipart/alternative; boundary="000000000000b700be065254dfd6" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000b700be065254dfd6 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi On Wed, May 20, 2026 at 11:49=E2=80=AFPM vignesh C wr= ote: > On Mon, 11 May 2026 at 08:31, Fujii Masao wrote: > > > > On Sun, May 10, 2026 at 5:45=E2=80=AFAM SATYANARAYANA NARLAPURAM > > wrote: > > > > > > Hi Hackers, > > > > > > SQL-callable replication slot functions acquire a slot (setting > > > the process-global MyReplicationSlot) but can then ERROR before > reaching > > > ReplicationSlotRelease(). If such an error is caught by a PL/pgSQL > > > EXCEPTION block (which uses a subtransaction), MyReplicationSlot > remains > > > set because there is no subtransaction-level cleanup hook for > replication > > > slots. > > > > > > Any subsequent slot operation in the same session then hits > > > Assert(MyReplicationSlot =3D=3D NULL) and crashes the backend on asse= rt > > > enabled builds. In release builds the stale MyReplicationSlot is > silently overwritten, > > > permanently orphaning the old slot as "active." The orphaned slot > blocks any other > > > session from acquiring it, vacuum and WAL deletion. > > > > > > Repro: > > > > > > SELECT pg_create_logical_replication_slot('adv_test', 'test_decoding'= ); > > > > > > DO $$ BEGIN > > > PERFORM pg_replication_slot_advance('adv_test', '0/1'::pg_lsn); > > > EXCEPTION WHEN others THEN > > > RAISE NOTICE 'caught: %', SQLERRM; > > > END $$; > > > > > > SELECT count(*) FROM pg_logical_slot_get_changes('adv_test', NULL, > NULL); > > > > > > 2026-05-09 19:45:06.619 UTC [1096805] STATEMENT: SELECT > pg_create_logical_replication_slot('adv_test', 'test_decoding'); > > > TRAP: failed Assert("MyReplicationSlot =3D=3D NULL"), File: "slot.c", > Line: 638, PID: 1096805 > > > > > > > > > Attached a patch to address this by wrapping error-prone paths in > PG_TRY/PG_CATCH blocks > > > and call ReplicationSlotRelease(). > > > > Thanks for the report and the patch! > > > > I think wrapping the slot-processing code with PG_TRY()/PG_CATCH() seem= s > > a good direction for addressing the issue you reported. > > > > > > + PG_CATCH(); > > + { > > + ReplicationSlotRelease(); > > > > When create_logical_replication_slot() is called with temporary =3D tru= e, > > the created logical replication slot has RS_TEMPORARY persistency. Such > a slot > > is not dropped by ReplicationSlotRelease(), whereas an RS_EPHEMERAL slo= t > is > > dropped via ReplicationSlotDropAcquired(). > > > > So even with the v1 patch, a temporary logical replication slot can > remain > > unexpectedly if pg_create_logical_replication_slot() throws an error. > > In this case, should create_logical_replication_slot() explicitly drop > the slot > > with ReplicationSlotDropAcquired(), or temporarily change the slot > persistency > > to RS_EPHEMERAL before calling ReplicationSlotRelease()? > > > > > > Does a newly created logical replication slot created by > > pg_copy_logical_replication_slot() have the same issue? > > Additionally pg_logical_slot_get_changes also has the same issue, it > can be reproduced by the following: > SELECT pg_create_logical_replication_slot('test_slot_1', 'test_decoding')= ; > > DO $$ > BEGIN > -- This will ERROR if the slot_get changes fails for the slot. > PERFORM 1 FROM pg_logical_slot_get_changes('test_slot_1', NULL, > NULL, 'nonexistent-option', 'val'); > EXCEPTION WHEN others THEN > RAISE NOTICE 'caught: %', SQLERRM; > END $$; > > SELECT count(*) FROM pg_logical_slot_get_changes('test_slot_1', NULL, > NULL); > > TRAP: failed Assert("MyReplicationSlot =3D=3D NULL"), File: "slot.c", > Line: 638, PID: 80308 > postgres: vignesh postgres [local] SELECT(ExceptionalCondition+0xba) > [0x642e7b2ebae1] > postgres: vignesh postgres [local] SELECT(ReplicationSlotAcquire+0x6e) > [0x642e7b00d732] Thank you for letting me know. Fixing these cases in the next update, will send it shortly. Thanks, Satya > > > --000000000000b700be065254dfd6 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi

On Wed, May 20, 2026 at 11:4= 9=E2=80=AFPM vignesh C <vignesh21= @gmail.com> wrote:
On Mon, 11 May 2026 at 08:31, Fujii Masao <masao.fujii@gmail.com= > wrote:
>
> On Sun, May 10, 2026 at 5:45=E2=80=AFAM SATYANARAYANA NARLAPURAM
> <sat= yanarlapuram@gmail.com> wrote:
> >
> > Hi Hackers,
> >
> > SQL-callable replication slot functions acquire a slot (setting > > the process-global MyReplicationSlot) but can then ERROR before r= eaching
> > ReplicationSlotRelease(). If such an error is caught by a PL/pgSQ= L
> > EXCEPTION block (which uses a subtransaction), MyReplicationSlot = remains
> > set because there is no subtransaction-level cleanup hook for rep= lication
> > slots.
> >
> > Any subsequent slot operation in the same session then hits
> > Assert(MyReplicationSlot =3D=3D NULL) and crashes the backend on = assert
> > enabled builds. In release builds the stale MyReplicationSlot is = silently overwritten,
> > permanently orphaning the old slot as "active." The orp= haned slot blocks any other
> > session from acquiring it, vacuum and WAL deletion.
> >
> > Repro:
> >
> > SELECT pg_create_logical_replication_slot('adv_test', = 9;test_decoding');
> >
> > DO $$ BEGIN
> >=C2=A0 =C2=A0 =C2=A0PERFORM pg_replication_slot_advance('adv_t= est', '0/1'::pg_lsn);
> > EXCEPTION WHEN others THEN
> >=C2=A0 =C2=A0 =C2=A0RAISE NOTICE 'caught: %', SQLERRM;
> > END $$;
> >
> > SELECT count(*) FROM pg_logical_slot_get_changes('adv_test= 9;, NULL, NULL);
> >
> > 2026-05-09 19:45:06.619 UTC [1096805] STATEMENT:=C2=A0 SELECT pg_= create_logical_replication_slot('adv_test', 'test_decoding'= );
> > TRAP: failed Assert("MyReplicationSlot =3D=3D NULL"), F= ile: "slot.c", Line: 638, PID: 1096805
> >
> >
> > Attached a patch to address this by wrapping error-prone paths in= PG_TRY/PG_CATCH blocks
> > and call ReplicationSlotRelease().
>
> Thanks for the report and the patch!
>
> I think wrapping the slot-processing code with PG_TRY()/PG_CATCH() see= ms
> a good direction for addressing the issue you reported.
>
>
> +=C2=A0 =C2=A0PG_CATCH();
> +=C2=A0 =C2=A0{
> +=C2=A0 =C2=A0 =C2=A0 =C2=A0ReplicationSlotRelease();
>
> When create_logical_replication_slot() is called with temporary =3D tr= ue,
> the created logical replication slot has RS_TEMPORARY persistency. Suc= h a slot
> is not dropped by ReplicationSlotRelease(), whereas an RS_EPHEMERAL sl= ot is
> dropped via ReplicationSlotDropAcquired().
>
> So even with the v1 patch, a temporary logical replication slot can re= main
> unexpectedly if pg_create_logical_replication_slot() throws an error.<= br> > In this case, should create_logical_replication_slot() explicitly drop= the slot
> with ReplicationSlotDropAcquired(), or temporarily change the slot per= sistency
> to RS_EPHEMERAL before calling ReplicationSlotRelease()?
>
>
> Does a newly created logical replication slot created by
> pg_copy_logical_replication_slot() have the same issue?

Additionally pg_logical_slot_get_changes also has the same issue, it
can be reproduced by the following:
SELECT pg_create_logical_replication_slot('test_slot_1', 'test_= decoding');

DO $$
BEGIN
=C2=A0 =C2=A0 -- This will ERROR if the slot_get changes fails for the slot= .
=C2=A0 =C2=A0 PERFORM 1 FROM pg_logical_slot_get_changes('test_slot_1&#= 39;, NULL,
NULL, 'nonexistent-option', 'val');
EXCEPTION WHEN others THEN
=C2=A0 =C2=A0 RAISE NOTICE 'caught: %', SQLERRM;
END $$;

SELECT count(*) FROM pg_logical_slot_get_changes('test_slot_1', NUL= L, NULL);

TRAP: failed Assert("MyReplicationSlot =3D=3D NULL"), File: "= ;slot.c",
Line: 638, PID: 80308
postgres: vignesh postgres [local] SELECT(ExceptionalCondition+0xba)
[0x642e7b2ebae1]
postgres: vignesh postgres [local] SELECT(ReplicationSlotAcquire+0x6e)
[0x642e7b00d732]

= Thank you for letting me know. Fixing these cases in the next update, will = send it shortly.

Thanks,=
Satya


--000000000000b700be065254dfd6--