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 1wrA9e-000BXC-2l for pgsql-hackers@arkaria.postgresql.org; Tue, 04 Aug 2026 08:05:43 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wrA9d-004UVZ-09 for pgsql-hackers@arkaria.postgresql.org; Tue, 04 Aug 2026 08:05:41 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wrA9c-004UVR-21 for pgsql-hackers@lists.postgresql.org; Tue, 04 Aug 2026 08:05:41 +0000 Received: from mail-pg1-x536.google.com ([2607:f8b0:4864:20::536]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wrA9a-000000008bO-2kSe for pgsql-hackers@postgresql.org; Tue, 04 Aug 2026 08:05:40 +0000 Received: by mail-pg1-x536.google.com with SMTP id 41be03b00d2f7-c9cf07d2df6so3250100a12.2 for ; Tue, 04 Aug 2026 01:05:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785830737; x=1786435537; darn=postgresql.org; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:content-type:from:to:cc :subject:date:message-id:reply-to:content-type; bh=x6rxcX8V+7WxwB/MxKd78EL4h/gmuG83ys+3EQwN83w=; b=G6clLeZQ8tRxavnKlNLLTVseslDJb2GsOtdwVlXzTZ745IRJ07jq5pRMewoOjyJO8g i42HlksHu+nRnNCLlBvWXdQVHiRu6MXfebkcLyE0E9lyF9y+ljlZLKU4zfUj3D1PpkJa nYmAxkV3XU2S5n3OeAAH/aO1aBZ3sQYKvQhohc8sgjPmU4cc909cMM4vBQrOqXzdumZ5 /QElWVRxdNof4YJay9RU/Zrba+nd29soLSpWTqClAYzjv428uI9tJREhJySwxwU4dOwW J6yRrHVCXufpn133uCIr2dIGCJEBJMNHFZjI7qvQlKNlVPcEBwfFMb1rt94/vMnALDSX SyTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785830737; x=1786435537; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:content-type:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=x6rxcX8V+7WxwB/MxKd78EL4h/gmuG83ys+3EQwN83w=; b=ruaX0H7G4TmOU44icFNpOMN+fJVpZM2QOM8BseCJfT+F2IU8JX9TOxRKyQVpxtnXRy HTSzaRFesXsaSwQUGp9jgaGDMFFIPz2WAPeIM75pvyEUyofGLjNB6+MS+gJ/P4RmB+ag g/RE7GecHmGlUYanTsSXF3Xbsyj3G1niFSIPtFsoX54c8XOKQmE7HxmLzOrFMsK+w+gi BKAyRDwF2E/uM2FGbSkyX3UlM1fwcTJ2PHARo1C+Q496TpiMlpmULucTpnqNvxAE2bZQ Hup0+bjtdY78WW+eZwyYMP9waPtiPuSkIVQdNPBY8iSHjNUfaQVsVq6VbPuO8+nCttgY JJHQ== X-Forwarded-Encrypted: i=1; AHgh+Rr/qbtwwx5hn2KVWbDIxkzwNupAlp5dtatycsQIUtCAPhTkaDzhd809nLwUTap8cZbmn63mjKayAZ7mUVc1@postgresql.org X-Gm-Message-State: AOJu0YyPI2SfxczEKkgSP47Nz5reOb1icp4qEd7hOUiCESiEbshkZjVZ NHWVLq7NObDTx4n96d826WIIx/ouL4SckCqrkhTxSUB+vVhUlcta8lww X-Gm-Gg: AR+sD12OfuMMJxDaOTT6H2/WuX+Vg9YbpT6TIxO+aO3faCDr1M/Rl6IbrUZYuC+eXp3 +YFOdKuIj/SdZAcNmCq9JRaAG6+FgtVNpu1XWIHPj6535jJaX9dhLAFlXdNYCJORd/dO7i2cd6B jR3WfemwlNEnOEN5Ikmk70uuvPo3jBK+3tohi4QVKMe011jgaEMu5umOUZP4OvCmrTaemcrAUPb AFkMfiZ+ET1KR45pA9xv0oZwNQdLEEnECG3hunPg7PJcR/nvltsyeqhGrytybYkxSpIZ4QM8CJb Nj/sBEyQOadG6ec4+rHo0SAMmnWVklrQ4ACGDd1Efql66NYF3BL+XHU6dZtH151AdK09qUUN56q 8r9PKq3IcJsIOg7c1zJjEeZKcdtcQOkTIynP1CRB1xR4tC1ECHpo3/jd4D3GR+7vvniknQPYRpw 3dibzWCJYPO8RLdtSNfqlvYlzPBRB1WxcRYX2LT2RELg9aZJn8z4i+E+AWEefNTZDhJCIIPlE0N h3l6wRZ X-Received: by 2002:a05:6a21:1fc2:b0:3bf:a638:4376 with SMTP id adf61e73a8af0-3c92a53eda0mr13770129637.21.1785830737153; Tue, 04 Aug 2026 01:05:37 -0700 (PDT) Received: from smtpclient.apple ([185.135.79.125]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-13fca6482e7sm887182c88.6.2026.08.04.01.05.34 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 04 Aug 2026 01:05:36 -0700 (PDT) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.600.51.1.1\)) Subject: Re: Add a hook for handling logical decoding messages on subscribers. From: Chao Li In-Reply-To: Date: Tue, 4 Aug 2026 16:05:00 +0800 Cc: Fujii Masao , Amit Kapila , Bharath Rupireddy , PostgreSQL-development Content-Transfer-Encoding: quoted-printable Message-Id: <875ACE0E-20ED-4ECC-AEBF-733BFA46D97D@gmail.com> References: To: Masahiko Sawada X-Mailer: Apple Mail (2.3864.600.51.1.1) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk > On Aug 4, 2026, at 08:41, Masahiko Sawada = wrote: >=20 > On Thu, Jul 9, 2026 at 5:14=E2=80=AFAM Fujii Masao = wrote: >>=20 >> On Wed, Jun 24, 2026 at 3:02=E2=80=AFAM Masahiko Sawada = wrote: >>> Right. I'm implementing a basic DDL replication solution using this >>> hook as a sample implementation. Other than that, this feature can = be >>> used to add additional information for the replicated transaction = that >>> is not replicated via logical replication protocol, such as the >>> executed user or context-specific information, which can then be >>> dispatched to a CDC system connected to the subscriber. >>=20 >> +1 for the proposed hook, although I haven't read the patch yet. >>=20 >> I know of a system that uses logical decoding messages to propagate = data >> to external systems. In that system, the application writes the data = to >> be propagated as logical decoding messages, and a CDC pipeline = consumes >> them via logical decoding and sends them to other systems, for = example >> through Kafka. >>=20 >> In that system, when the remote site is maintained by physical = replication, >> the same logical decoding messages can be decoded on the standby >> at the remote site to deliver them to external systems there. = However, >> if the remote site uses logical replication instead, those messages = are >> currently neither delivered to nor processed on the subscriber at >> the remote site. As a result, the CDC pipeline at the remote site = cannot >> consume the data they carry. >>=20 >> The proposed hook might be useful for this use case. An extension = could >> process the incoming logical decoding messages on the subscriber and >> forward them to the local CDC pipeline, or store or re-emit them in a = form >> that local consumers can process. >=20 > Thank you for sharing the concrete use case. This is one of the use > cases I initially imagined. >=20 > I've rebased and updated the patch. Please review it. >=20 > Regards, >=20 > --=20 > Masahiko Sawada > Amazon Web Services: https://aws.amazon.com > I played with this feature today. I have a few comments from a design = perspective: 1. Would it make sense to pass the receiving subscription=E2=80=99s = identity explicitly to the hook? Otherwise, a hook that needs to = distinguish messages received by different subscriptions has to obtain = it from a global variable such as MySubscription->oid. 2. A hook is invoked by an apply worker, and since a message has no = target relation, the hook runs as the subscription owner regardless of = run_as_owner. I think it would be useful to document this explicitly. 3. I=E2=80=99m thinking out loud here. Would it be useful to provide an = opt-in default hook that re-emits received messages, so that they are = written to the subscriber-side WAL and can be consumed by a local = logical-decoding client? I understand that a re-emitted message would = have a different LSN, just as logically replicated row changes generate = new local WAL records. For the use case Fujii-san described, this might = allow an existing decoder to continue working after the remote site = switches from physical to logical replication. And a few comment for the code changes: 1 - worker_internal.h ``` +typedef void (*LogicalRepMessageHandle_hook_type) = (LogicalRepMessageData *msg); +extern PGDLLIMPORT LogicalRepMessageHandle_hook_type = LogicalRepMessageHandle_hook; ``` I guess we don=E2=80=99t expect a hook function to mutate the message, = so maybe make the pointer const. 2 - worker.c ``` + /* + * Logical messages are relation-agnostic, so they don't map = cleanly onto + * the tablesync worker of a particular relation, and there = would be no + * well-defined ordering between a message and the initial copy. = Leave + * them to the (parallel) apply workers. Logical messages are = handled only + * the (parallel) apply workers + */ ``` The last sentence looks duplicate and incomplete. 3 - proto.c ``` + /* read message length */ + len =3D pq_getmsgint(in, 4); + msg_data->message_size =3D len; + + /* and data */ + msg =3D palloc(len + 1); + pq_copymsgbytes(in, msg, len); + + msg[len] =3D '\0'; + msg_data->message =3D msg; ``` logicalrep_read_message() allocates one extra byte for NULL terminator, = which is unnecessary, as the contract is to use msg_data->message_size = to decide the message length, and a message contain contains 0 in the = middle. But I agree it may be useful for debugging and logging, so maybe = add a comment to explain why using this extra byte. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/