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 1wc6BK-0035Vu-2E for pgsql-hackers@arkaria.postgresql.org; Tue, 23 Jun 2026 18:49: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 1wc6BJ-00DOem-1t for pgsql-hackers@arkaria.postgresql.org; Tue, 23 Jun 2026 18:49:09 +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 1wc6BJ-00DOeX-0G for pgsql-hackers@lists.postgresql.org; Tue, 23 Jun 2026 18:49:09 +0000 Received: from mail-pf1-x434.google.com ([2607:f8b0:4864:20::434]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wc6BH-00000001mmV-1zHP for pgsql-hackers@postgresql.org; Tue, 23 Jun 2026 18:49:08 +0000 Received: by mail-pf1-x434.google.com with SMTP id d2e1a72fcca58-8423f236418so181014b3a.1 for ; Tue, 23 Jun 2026 11:49:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1782240546; cv=none; d=google.com; s=arc-20240605; b=G1YbQi0GPXeOedZLb98bj0+hMqa6zzajfRDBKaddIOy254VWEK9/FeRGm1nAGh31vt F5SOWYIpT4O54Yt1lGGQTsecqKirfc1NejOUb7hFNbUZ8dkU1+iSsceKFi5FnusbGHV5 MMrl29U3m+o1yvOWeyhpJVBUhoXJwpTE/Imql+OsiSYFeEiPtgYqJstE4A4DJ030bkrw Qq7UBsfPePANmMJovJYuG5JbkxXrYbGEGztOMboP8N51XzbawWCpVdEc4srS4Oodr0p6 xv6Root2aJryvU0IbJpMSR+IS9uaoM8R8enIVYjD+wdXhdt+ju6gPhjIEPCVX2nEXtOo 51MQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=bri9qzbSPFWjHFUJ3uX6g7XCKd8cuGTwFGa5giTFXWM=; fh=pl5EhsOoHNeGjH2xh8bWp+xPJ6+oOlBwusPKZZ+MhAk=; b=ICVT+Xoj2bsVlnYTVB4n+yr/BCaFKldCjo+xDxhAygu6LcAwi0TJoXaOAAx8teu1H3 qDiqn4ZQhCYclA9YwQ36aZZIXsx3crfWDrlkP7pOXVkawVIv6sV/Gt2KnfauAlk7ImEG FF9qsWc7b/cGrbUP1v1K12rygmflGQy9ftMxbb+anirkdYE/5tiCrnGK9tfUk1rM8obU 6YMCHedX+7S/9+L03PD5strJEfMWRYRpmmyxdZqi5gVKk22UJCb+GQdOvE+RkPx1drES RKdlqESG9IEIQLHrNA+Ri2B9QiQkN38+22ZF6ahpiFvh3vXYDganwjR2nTIxxjoncrhr P+Hg==; darn=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=1782240546; x=1782845346; darn=postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=bri9qzbSPFWjHFUJ3uX6g7XCKd8cuGTwFGa5giTFXWM=; b=RPOd1iyUtHhIP8x0n4NpIyg0uZlCjaTOnZoZc/jXW+8W7XyzliGHCJ3pdkPx9nyFU4 PHAL5ND/43q5t/MRV+uZrJhKMXoPRc0waR/U857VO3NTKpIqTTIamjs9I/YlXqV9IM9v ECfXPDnW0tgBbqMqI3RekP+zz+60+7o4mu0uScW8OQXplc/nP+K+Wx/6QzjhaQSvIqW/ AITPxl9ElkI5z2+WYSRswYxCXqnbROj2dqlD4fJ9+feMaYT0E9kjwV9CypYW/y6sVEL0 2ATtNHEDNJYhUyVojn7v/cD1B/207ki8VgCemwYrOraLsh1fZ4q/eoHpTC4uLQYAndnH BOkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782240546; x=1782845346; h=content-transfer-encoding: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=bri9qzbSPFWjHFUJ3uX6g7XCKd8cuGTwFGa5giTFXWM=; b=O9OauA/uwytkYTZutIlYnxy8PshruZI981BB1mOgKtI2+Vwt3Dq9fvgbyctTRbg7kv G2HRPm8YrveLygplUmHktWYcZMxZfXDuvkf63fv1ARG+FSXDqLVIzh/6jojiRYW2RI/C uvPXosuRK080t9Z4g3FPtzu2xqJTiSeBqAnG/HtRA6eYVSA2MPT7PnL/YgxZKQhbdD0l taQm+0GSSCFmEy56zcw61EsTbGFEzP/rKLF83D8dimxuWaYXFTilgGvPwVRSG28TNXgm ZW3bYZLfhnQfDiO3Oiflfn4I1stV3lQMIsGbj6BPJicgT5RB+HKvzeIf/oKFpXTCBSIo 9F5Q== X-Gm-Message-State: AOJu0Yy/gXMeEILiWiGEsoQqsBHagf/fbbixzi1O+1QwVijvtVz2s1oj +zQLWqg7jfuABhj0VbkVb3P3ZCGZdnQ/FkU4gavaboGfdKt01ORMh8JRWn+CEBaAyKay5graggt 83tXZ2tunpEdzEBH6EQWC5CSKVJyZSYo= X-Gm-Gg: AfdE7cly2Iq4wLJyIzvYePSkv3KxEPN8CDwg0iX1JstZfXMByd5QKVZk4WgTGUfDOWe o0p4IxRledUbQ7oJPP8cBQF+uxSPp0qd2CH6mV8OrTlCK4xEpfXxK+w6C0aq2t1cARtIL0/cOPF iJUM4m2xFACzeMSIZTxsnGJ6DXtjNF2sE0BIFDsI69i0zkzjuzwicg2Uz5+Bl4Gs7uc5kQkJRh3 PerIMKQOH9bdU7nByNweHj8MmBS6jaFgTjH4/JEtx36mWMW5vVg7PGu/BHrztxNUTKQVnjabqJ/ n81Uke+F9PcDY8CW6jp+dtTkYkRw2Q== X-Received: by 2002:aa7:8881:0:b0:845:286d:4675 with SMTP id d2e1a72fcca58-845a2abc3d0mr367622b3a.10.1782240546023; Tue, 23 Jun 2026 11:49:06 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Masahiko Sawada Date: Tue, 23 Jun 2026 11:48:28 -0700 X-Gm-Features: AVVi8CfjMsdJQ76PrYbKTYxoOoYCj38eeU3cZrpsHR0ViYTGGQkt8lX9InZm1nU Message-ID: Subject: Re: Add a hook for handling logical decoding messages on subscribers. To: Bharath Rupireddy Cc: PostgreSQL-development Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Mon, Jun 22, 2026 at 3:39=E2=80=AFPM Bharath Rupireddy wrote: > > Hi, > > On Fri, Jun 19, 2026 at 3:34=E2=80=AFPM Masahiko Sawada wrote: > > > > Hi all, > > > > Commit ac4645c015 allows pgoutput to send logical decoding messages, > > but it's limited to applications that use the pgoutput plugin -- the > > built-in logical replication doesn't use it. I'd like to propose > > introducing a hook to the logical replication message handling so that > > extensions can plug in their own handling routine. This feature can be > > used for extensions to implement DDL replication, function > > replication, or trigger user-specific routines on the subscriber side. > > Thanks for working on this! > > > I've attached the PoC patch; it adds a hook function, and adds a new > > 'message' subscription option that allows the user to request the > > publisher to send logical decoding messages. Therefore, users need to > > enable the 'message' option and set up the hook function at server > > startup in order to receive the messages and trigger the hook > > function. > > I understand the intent of the proposal, but I'd like to get the > bigger picture first. > > Do we have any external modules that actually implement DDL > replication (or any of the listed use-cases) with a similar hook? Or > any existing discussion? I could be missing something because I > haven't looked at all the DDL replication related threads. Not that I'm aware of. That said, in past DDL replication discussions several approaches were proposed (e.g. sending the DDL query text vs. sending schema diffs in some form of intermediate representation), each with its own pros and cons. So even once we have an in-core DDL replication implementation, I think it's still plausible that an extension would want to implement a different approach, and this hook would let it do so without patching core. > > Another thing I'm curious about - why a hook? Is the plan to implement > DDL replication as an external module rather than in core? If DDL > replication eventually gets into core, I'd expect it to be apply-side > logic executing the decoded DDL messages directly, not something going > through a hook. I'm working on in-core DDL replication too, and I agree that the in-core version would be apply-side logic executing the decoded DDL directly, not something going through this hook. The hook isn't meant to be to implement the in-core DDL replication. It's there for things core wouldn't cover: auditing replayed DDLs, propagating context-specific information to the subscriber, or an extension implementing a different DDL replication approach than the in-core one. > Why not a hook at apply_dispatch to give external modules more freedom > with the pgoutput plugin? It would let an extension intercept every message type (INSERT/UPDATE/DELETE, COMMIT, etc.) and could easily break apply consistency. At the same time, the incoming data is just the standard logical replication protocol, so there isn't much an extension could usefully do with the non-MESSAGE types beyond what core already does. > > > I I went with a hook function in the patch. While it lets you chain > > the multiple hook functions, providing the registration API might be > > better, or other types of registry can also be considered. > > It's hard to tell how many external modules would make use of this > hook (rather, how many external modules implementing this hook one > would allow to be installed in a production database requiring > chaining), but my first thought is that a registration-based API along > the lines of RegisterXactCallback would be cleaner and work better. Yeah, it would be better. > > > Feedback is very welcome. > > A few comments on the patch: > > 1/ > + bool message; /* True if the subscription wants to receive > + * logical messages */ > } Subscription; > > Nit: I'd call these logical decoding messages or generic logical > messages - something to match the docs and pg_logical_emit_message. Will fix. > 2/ > +void > +test_logical_message_handler(LogicalRepMessageData *msg) > +{ > + ereport(LOG, > + (errmsg("received message: LSN %X/%08X, prefix: %s, message: %s", > + LSN_FORMAT_ARGS(msg->lsn), > + msg->prefix, > + msg->message))); > +} > > Why not have the test extension do a simple DDL/function/event-trigger > based replication? It doesn't need to be a full-blown implementation, > but to show the usefulness of this hook, it's better to have one > demonstrating the listed use-cases. I'd prefer to keep this test module minimal, since its purpose is to improve coverage of the newly added code. A worked example that demonstrates the usefulness of the hook is valuable, but I think it belongs in a separate contrib module rather than in the test module, and that's probably a separate discussion. > > 3/ > + if (subinfo->submessage) > + appendPQExpBufferStr(query, ", message =3D true"); > > Why a subscription-level option? Why not leave the decision of whether > or not to act on the message to the external module implementers? This is because not to affect the existing logical replication users. Since the logical decoding messages are not sent today, I think that it should be an explcit opt-in feature for users who don't want to allow the publisher to send logical decoding messages. > > 4/ > + /* > + * Logical messages are handled only the (parallel) apply workers > + */ > + if (am_tablesync_worker() || am_sequencesync_worker()) > + return; > > Why these restrictions? Why not leave the decision to external module > implementers? Isn't this limiting - what if someone wants to use this > hook for the schema sync during the initial table sync phase? Logical decoding messages are relation-agnostic, so they don't map cleanly onto the table sync phase. If every tablesync worker processed the messages they'd be handled multiple times, and if only one did, there'd be no well-defined ordering between a message and the initial copy progress. So I think letting tablesync workers run the hook seems more confusing than useful. > > 5/ With this change, pg_logical_emit_message does affect the logical > replication apply if the subscriber has defined this hook. I think > it's worth mentioning in the docs for pg_logical_emit_message. Agreed. Regards, -- Masahiko Sawada Amazon Web Services: https://aws.amazon.com