Skip to content

Commit 2753768

Browse files
author
Commitfest Bot
committed
[CF 5585] v17 - Filter irrelevant change before reassemble transactions during logical decoding
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5585 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CAFPTHDaSn_-LuaE6v6s9+9HuvqpsJLbg-jz9FokZWekXydU39A@mail.gmail.com Author(s): Jie Li, Ajin Cherian, Zhijie Hou
2 parents f132815 + 957926f commit 2753768

File tree

10 files changed

+762
-71
lines changed

10 files changed

+762
-71
lines changed

doc/src/sgml/logicaldecoding.sgml

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ typedef struct OutputPluginCallbacks
560560
LogicalDecodeCommitCB commit_cb;
561561
LogicalDecodeMessageCB message_cb;
562562
LogicalDecodeFilterByOriginCB filter_by_origin_cb;
563+
LogicalDecodeFilterChangeCB filter_change_cb;
563564
LogicalDecodeShutdownCB shutdown_cb;
564565
LogicalDecodeFilterPrepareCB filter_prepare_cb;
565566
LogicalDecodeBeginPrepareCB begin_prepare_cb;
@@ -582,8 +583,8 @@ typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);
582583
and <function>commit_cb</function> callbacks are required,
583584
while <function>startup_cb</function>, <function>truncate_cb</function>,
584585
<function>message_cb</function>, <function>filter_by_origin_cb</function>,
585-
and <function>shutdown_cb</function> are optional.
586-
If <function>truncate_cb</function> is not set but a
586+
<function>shutdown_cb</function>, and <function>filter_change_cb</function>
587+
are optional. If <function>truncate_cb</function> is not set but a
587588
<command>TRUNCATE</command> is to be decoded, the action will be ignored.
588589
</para>
589590

@@ -871,6 +872,42 @@ typedef bool (*LogicalDecodeFilterByOriginCB) (struct LogicalDecodingContext *ct
871872
</para>
872873
</sect3>
873874

875+
<sect3 id="logicaldecoding-output-plugin-filter-change">
876+
<title>Change Filter Callback</title>
877+
878+
<para>
879+
The optional <function>filter_change_cb</function> is called before a
880+
change record is decoded to determine whether the change can be filtered
881+
out.
882+
<programlisting>
883+
typedef bool (*LogicalDecodeFilterChangeCB) (struct LogicalDecodingContext *ctx,
884+
Oid relid,
885+
ReorderBufferChangeType change_type,
886+
bool in_txn, bool *cache_valid);
887+
</programlisting>
888+
To indicate that decoding can be skipped for the given
889+
<parameter>change_type</parameter>, return <literal>true</literal>;
890+
<literal>false</literal> otherwise.
891+
The <parameter>in_txn</parameter> parameter indicates whether the
892+
callback is invoked within a transaction block.
893+
When <parameter>in_txn</parameter> is false, and if making a decision to filter a change requires being inside a
894+
transaction block, such as needing access to the catalog, set
895+
<parameter>*cache_valid</parameter> to <literal>false</literal>.
896+
This ensures that the callback will be reinvoked once a transaction block
897+
starts. If a decision can be made immediately, set
898+
<parameter>*cache_valid</parameter> to <literal>true</literal>.
899+
</para>
900+
<para>
901+
The primary purpose of this callback function is to optimize memory usage
902+
and processing efficiency by filtering out changes that are unnecessary for
903+
output plugins. It enables output plugins to selectively process relevant
904+
changes. Caching filtering decisions locally is recommended, as it enables
905+
the callback to provide cached results without repeatedly initiating
906+
transactions or querying the catalog. This approach minimizes overhead
907+
and improves efficiency during the decoding process.
908+
</para>
909+
</sect3>
910+
874911
<sect3 id="logicaldecoding-output-plugin-message">
875912
<title>Generic Message Callback</title>
876913

src/backend/replication/logical/decode.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,17 @@ FilterByOrigin(LogicalDecodingContext *ctx, RepOriginId origin_id)
588588
return filter_by_origin_cb_wrapper(ctx, origin_id);
589589
}
590590

591+
/*
592+
* Check if filtering changes before decoding is supported and we're not suppressing filter
593+
* changes currently.
594+
*/
595+
static inline bool
596+
FilterChangeIsEnabled(LogicalDecodingContext *ctx)
597+
{
598+
return (ctx->callbacks.filter_change_cb != NULL &&
599+
ctx->reorder->try_to_filter_change);
600+
}
601+
591602
/*
592603
* Handle rmgr LOGICALMSG_ID records for LogicalDecodingProcessRecord().
593604
*/
@@ -894,6 +905,16 @@ DecodeAbort(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
894905
UpdateDecodingStats(ctx);
895906
}
896907

908+
/* Function to determine whether to filter the change */
909+
static inline bool
910+
FilterChange(LogicalDecodingContext *ctx, XLogRecPtr origptr, TransactionId xid,
911+
RelFileLocator *target_locator, ReorderBufferChangeType change_type)
912+
{
913+
return (FilterChangeIsEnabled(ctx) &&
914+
ReorderBufferFilterByRelFileLocator(ctx->reorder, xid, origptr, target_locator,
915+
change_type));
916+
}
917+
897918
/*
898919
* Parse XLOG_HEAP_INSERT (not MULTI_INSERT!) records into tuplebufs.
899920
*
@@ -928,6 +949,11 @@ DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
928949
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
929950
return;
930951

952+
/* Can the relation associated with this change be skipped? */
953+
if (FilterChange(ctx, buf->origptr, XLogRecGetXid(r), &target_locator,
954+
REORDER_BUFFER_CHANGE_INSERT))
955+
return;
956+
931957
change = ReorderBufferAllocChange(ctx->reorder);
932958
if (!(xlrec->flags & XLH_INSERT_IS_SPECULATIVE))
933959
change->action = REORDER_BUFFER_CHANGE_INSERT;
@@ -978,6 +1004,11 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
9781004
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
9791005
return;
9801006

1007+
/* Can the relation associated with this change be skipped? */
1008+
if (FilterChange(ctx, buf->origptr, XLogRecGetXid(r), &target_locator,
1009+
REORDER_BUFFER_CHANGE_UPDATE))
1010+
return;
1011+
9811012
change = ReorderBufferAllocChange(ctx->reorder);
9821013
change->action = REORDER_BUFFER_CHANGE_UPDATE;
9831014
change->origin_id = XLogRecGetOrigin(r);
@@ -1044,6 +1075,11 @@ DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
10441075
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
10451076
return;
10461077

1078+
/* Can the relation associated with this change be skipped? */
1079+
if (FilterChange(ctx, buf->origptr, XLogRecGetXid(r), &target_locator,
1080+
REORDER_BUFFER_CHANGE_DELETE))
1081+
return;
1082+
10471083
change = ReorderBufferAllocChange(ctx->reorder);
10481084

10491085
if (xlrec->flags & XLH_DELETE_IS_SUPER)
@@ -1146,6 +1182,11 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
11461182
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
11471183
return;
11481184

1185+
/* Can the relation associated with this change be skipped? */
1186+
if (FilterChange(ctx, buf->origptr, XLogRecGetXid(r), &rlocator,
1187+
REORDER_BUFFER_CHANGE_INSERT))
1188+
return;
1189+
11491190
/*
11501191
* We know that this multi_insert isn't for a catalog, so the block should
11511192
* always have data even if a full-page write of it is taken.
@@ -1237,6 +1278,11 @@ DecodeSpecConfirm(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
12371278
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
12381279
return;
12391280

1281+
/* Can the relation associated with this change be skipped? */
1282+
if (FilterChange(ctx, buf->origptr, XLogRecGetXid(r), &target_locator,
1283+
REORDER_BUFFER_CHANGE_INSERT))
1284+
return;
1285+
12401286
change = ReorderBufferAllocChange(ctx->reorder);
12411287
change->action = REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM;
12421288
change->origin_id = XLogRecGetOrigin(r);

src/backend/replication/logical/logical.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ static void truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
7474
static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
7575
XLogRecPtr message_lsn, bool transactional,
7676
const char *prefix, Size message_size, const char *message);
77+
static bool filter_change_cb_wrapper(ReorderBuffer *cache, Oid relid,
78+
ReorderBufferChangeType change_type, bool in_txn,
79+
bool *cache_valid);
7780

7881
/* streaming callbacks */
7982
static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
@@ -220,6 +223,7 @@ StartupDecodingContext(List *output_plugin_options,
220223
/* wrap output plugin callbacks, so we can add error context information */
221224
ctx->reorder->begin = begin_cb_wrapper;
222225
ctx->reorder->apply_change = change_cb_wrapper;
226+
ctx->reorder->filter_change = filter_change_cb_wrapper;
223227
ctx->reorder->apply_truncate = truncate_cb_wrapper;
224228
ctx->reorder->commit = commit_cb_wrapper;
225229
ctx->reorder->message = message_cb_wrapper;
@@ -1224,6 +1228,43 @@ filter_by_origin_cb_wrapper(LogicalDecodingContext *ctx, RepOriginId origin_id)
12241228
return ret;
12251229
}
12261230

1231+
static bool
1232+
filter_change_cb_wrapper(ReorderBuffer *cache, Oid relid,
1233+
ReorderBufferChangeType change_type, bool in_txn,
1234+
bool *cache_valid)
1235+
{
1236+
LogicalDecodingContext *ctx = cache->private_data;
1237+
LogicalErrorCallbackState state;
1238+
ErrorContextCallback errcallback;
1239+
bool ret;
1240+
1241+
Assert(!ctx->fast_forward);
1242+
1243+
if (ctx->callbacks.filter_change_cb == NULL)
1244+
return false;
1245+
1246+
/* Push callback + info on the error context stack */
1247+
state.ctx = ctx;
1248+
state.callback_name = "filter_change";
1249+
state.report_location = InvalidXLogRecPtr;
1250+
errcallback.callback = output_plugin_error_callback;
1251+
errcallback.arg = (void *) &state;
1252+
errcallback.previous = error_context_stack;
1253+
error_context_stack = &errcallback;
1254+
1255+
/* set output state */
1256+
ctx->accept_writes = false;
1257+
ctx->end_xact = false;
1258+
1259+
/* do the actual work: call callback */
1260+
ret = ctx->callbacks.filter_change_cb(ctx, relid, change_type, in_txn, cache_valid);
1261+
1262+
/* Pop the error context stack */
1263+
error_context_stack = errcallback.previous;
1264+
1265+
return ret;
1266+
}
1267+
12271268
static void
12281269
message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
12291270
XLogRecPtr message_lsn, bool transactional,

0 commit comments

Comments
 (0)