<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Logical-Decoding on Tarragon</title><link>https://tarrragon.github.io/blog/tags/logical-decoding/</link><description>Recent content in Logical-Decoding on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 22 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/logical-decoding/index.xml" rel="self" type="application/rss+xml"/><item><title>PostgreSQL Logical Decoding Plugins</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/logical-decoding-plugins/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/logical-decoding-plugins/</guid><description>&lt;p>PostgreSQL logical decoding plugins 的核心責任是把 WAL 中的變更轉成外部消費者可理解的事件格式。PostgreSQL 官方 logical decoding 文件說明，logical decoding 透過 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/replication-slot/" data-link-title="Replication Slot" data-link-desc="說明邏輯複製如何用 slot 追蹤消費進度，並對來源端造成保留壓力">replication slot&lt;/a> 將 WAL 變更解碼成 plugin output；output plugin 決定外部看到的是 PostgreSQL protocol、JSON、測試文字或自訂格式。&lt;/p>
&lt;p>本文的判讀錨點是：plugin 選型是 CDC contract 決策。它影響 schema evolution、事件欄位、delete 表示、transaction boundary、consumer compatibility、slot lag 與故障復原。&lt;/p>
&lt;h2 id="plugin-boundary">Plugin Boundary&lt;/h2>
&lt;p>Plugin boundary 的核心責任是定義 database 變更如何離開 PostgreSQL。常見選項包含內建 &lt;code>pgoutput&lt;/code>、測試用 &lt;code>test_decoding&lt;/code>、JSON-oriented plugin，以及 Debezium connector 支援的 plugin / protocol。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Plugin / path&lt;/th>
 &lt;th>主要責任&lt;/th>
 &lt;th>適合情境&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>pgoutput&lt;/code>&lt;/td>
 &lt;td>PostgreSQL logical replication protocol&lt;/td>
 &lt;td>built-in logical replication、Debezium 常見路線&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>test_decoding&lt;/code>&lt;/td>
 &lt;td>人類可讀測試 output&lt;/td>
 &lt;td>lab、debug、教育用途&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>wal2json&lt;/code>&lt;/td>
 &lt;td>JSON change event&lt;/td>
 &lt;td>自訂 consumer、legacy CDC&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>decoderbufs&lt;/td>
 &lt;td>Protobuf event&lt;/td>
 &lt;td>強 schema contract 的 pipeline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Native subscription&lt;/td>
 &lt;td>DB-to-DB replication&lt;/td>
 &lt;td>PostgreSQL 之間 table replication&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;code>pgoutput&lt;/code> 適合標準化 CDC。它與 publication / subscription model 對齊，能保留 PostgreSQL logical replication 的主路線。&lt;/p>
&lt;p>&lt;code>test_decoding&lt;/code> 適合教學與排錯。它讓人看到 transaction 裡發生的 insert / update / delete，但它的定位是測試與理解，不應作為正式 event contract。&lt;/p>
&lt;h2 id="replication-slot-responsibility">Replication Slot Responsibility&lt;/h2>
&lt;p>Replication slot responsibility 的核心責任是保護 consumer 進度，同時管理 WAL retention。Logical slot 會讓 PostgreSQL 保留尚未被 consumer 確認的 WAL；consumer 停住時，slot lag 會轉成 disk pressure。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Signal&lt;/th>
 &lt;th>意義&lt;/th>
 &lt;th>操作反應&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>confirmed_flush_lsn&lt;/code>&lt;/td>
 &lt;td>consumer 已確認的位置&lt;/td>
 &lt;td>用來判斷 CDC 進度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>retained WAL size&lt;/td>
 &lt;td>slot 造成的 WAL 保留量&lt;/td>
 &lt;td>alert、調整 consumer、drop / advance&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>inactive slot&lt;/td>
 &lt;td>consumer 離線&lt;/td>
 &lt;td>檢查 connector、暫停 release&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>publication table diff&lt;/td>
 &lt;td>CDC scope 與 schema 不一致&lt;/td>
 &lt;td>review publication / table ownership&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Slot 是 production resource。每個 logical slot 都要有 owner、consumer、SLO、drop condition、backfill plan 與 alert。&lt;/p>
&lt;h2 id="event-contract">Event Contract&lt;/h2>
&lt;p>Event contract 的核心責任是讓 downstream 知道每個變更代表什麼。CDC 事件至少要說明 key、before/after image、operation、commit timestamp、transaction ordering、schema version 與 delete representation。&lt;/p></description><content:encoded><![CDATA[<p>PostgreSQL logical decoding plugins 的核心責任是把 WAL 中的變更轉成外部消費者可理解的事件格式。PostgreSQL 官方 logical decoding 文件說明，logical decoding 透過 <a href="/blog/backend/knowledge-cards/replication-slot/" data-link-title="Replication Slot" data-link-desc="說明邏輯複製如何用 slot 追蹤消費進度，並對來源端造成保留壓力">replication slot</a> 將 WAL 變更解碼成 plugin output；output plugin 決定外部看到的是 PostgreSQL protocol、JSON、測試文字或自訂格式。</p>
<p>本文的判讀錨點是：plugin 選型是 CDC contract 決策。它影響 schema evolution、事件欄位、delete 表示、transaction boundary、consumer compatibility、slot lag 與故障復原。</p>
<h2 id="plugin-boundary">Plugin Boundary</h2>
<p>Plugin boundary 的核心責任是定義 database 變更如何離開 PostgreSQL。常見選項包含內建 <code>pgoutput</code>、測試用 <code>test_decoding</code>、JSON-oriented plugin，以及 Debezium connector 支援的 plugin / protocol。</p>
<table>
  <thead>
      <tr>
          <th>Plugin / path</th>
          <th>主要責任</th>
          <th>適合情境</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>pgoutput</code></td>
          <td>PostgreSQL logical replication protocol</td>
          <td>built-in logical replication、Debezium 常見路線</td>
      </tr>
      <tr>
          <td><code>test_decoding</code></td>
          <td>人類可讀測試 output</td>
          <td>lab、debug、教育用途</td>
      </tr>
      <tr>
          <td><code>wal2json</code></td>
          <td>JSON change event</td>
          <td>自訂 consumer、legacy CDC</td>
      </tr>
      <tr>
          <td>decoderbufs</td>
          <td>Protobuf event</td>
          <td>強 schema contract 的 pipeline</td>
      </tr>
      <tr>
          <td>Native subscription</td>
          <td>DB-to-DB replication</td>
          <td>PostgreSQL 之間 table replication</td>
      </tr>
  </tbody>
</table>
<p><code>pgoutput</code> 適合標準化 CDC。它與 publication / subscription model 對齊，能保留 PostgreSQL logical replication 的主路線。</p>
<p><code>test_decoding</code> 適合教學與排錯。它讓人看到 transaction 裡發生的 insert / update / delete，但它的定位是測試與理解，不應作為正式 event contract。</p>
<h2 id="replication-slot-responsibility">Replication Slot Responsibility</h2>
<p>Replication slot responsibility 的核心責任是保護 consumer 進度，同時管理 WAL retention。Logical slot 會讓 PostgreSQL 保留尚未被 consumer 確認的 WAL；consumer 停住時，slot lag 會轉成 disk pressure。</p>
<table>
  <thead>
      <tr>
          <th>Signal</th>
          <th>意義</th>
          <th>操作反應</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>confirmed_flush_lsn</code></td>
          <td>consumer 已確認的位置</td>
          <td>用來判斷 CDC 進度</td>
      </tr>
      <tr>
          <td>retained WAL size</td>
          <td>slot 造成的 WAL 保留量</td>
          <td>alert、調整 consumer、drop / advance</td>
      </tr>
      <tr>
          <td>inactive slot</td>
          <td>consumer 離線</td>
          <td>檢查 connector、暫停 release</td>
      </tr>
      <tr>
          <td>publication table diff</td>
          <td>CDC scope 與 schema 不一致</td>
          <td>review publication / table ownership</td>
      </tr>
  </tbody>
</table>
<p>Slot 是 production resource。每個 logical slot 都要有 owner、consumer、SLO、drop condition、backfill plan 與 alert。</p>
<h2 id="event-contract">Event Contract</h2>
<p>Event contract 的核心責任是讓 downstream 知道每個變更代表什麼。CDC 事件至少要說明 key、before/after image、operation、commit timestamp、transaction ordering、schema version 與 delete representation。</p>
<table>
  <thead>
      <tr>
          <th>Contract 面向</th>
          <th>審查問題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Key</td>
          <td>table 是否有 replica identity / primary key</td>
      </tr>
      <tr>
          <td>Update image</td>
          <td>是否需要 before value</td>
      </tr>
      <tr>
          <td>Delete</td>
          <td>tombstone、key-only delete、soft delete</td>
      </tr>
      <tr>
          <td>Ordering</td>
          <td>transaction order 是否要保留</td>
      </tr>
      <tr>
          <td>Schema evolution</td>
          <td>新欄位、rename、drop 欄位如何通知</td>
      </tr>
      <tr>
          <td>Backfill</td>
          <td>initial snapshot 與 streaming 如何銜接</td>
      </tr>
  </tbody>
</table>
<p><a href="/blog/backend/knowledge-cards/replica-identity/" data-link-title="Replica Identity" data-link-desc="說明 row-level 變更事件如何帶穩定 key，讓下游能正確套用 update 與 delete">Replica identity</a> 是 CDC 的核心設定。沒有穩定 key 的 table 會讓 update / delete event 難以被 downstream 正確套用；這類 table 要先補 primary key 或明確設定 replica identity。</p>
<h2 id="connector-patterns">Connector Patterns</h2>
<p>Connector patterns 的核心責任是把 plugin output 接到實際 pipeline。Debezium、custom consumer、DB native subscription 的維運責任不同。</p>
<table>
  <thead>
      <tr>
          <th>Pattern</th>
          <th>優點</th>
          <th>風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Debezium connector</td>
          <td>成熟 snapshot + streaming workflow</td>
          <td>connector state、Kafka / offset operation</td>
      </tr>
      <tr>
          <td>Native subscription</td>
          <td>PostgreSQL 原生 DB-to-DB</td>
          <td>schema drift、DDL coordination</td>
      </tr>
      <tr>
          <td>Custom consumer</td>
          <td>可客製 event contract</td>
          <td>slot management 與 error handling 自行負責</td>
      </tr>
      <tr>
          <td>Batch export + CDC</td>
          <td>backfill 與 streaming 分開</td>
          <td>cutover LSN 與 duplication handling</td>
      </tr>
  </tbody>
</table>
<p>Connector 要定義 backfill 與 streaming 的接點。最常見的事故是 snapshot 還沒完成就開始消費、或 cutover LSN 沒有被記錄，導致 downstream 重複或漏資料。</p>
<h2 id="failure-modes">Failure Modes</h2>
<p>Failure modes 的核心責任是把 CDC 事故分成 database、connector、schema 與 downstream 四層。</p>
<table>
  <thead>
      <tr>
          <th>Failure mode</th>
          <th>判讀訊號</th>
          <th>第一反應</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Slot lag growth</td>
          <td>retained WAL 持續增加</td>
          <td>暫停重型寫入、修 connector、評估 drop</td>
      </tr>
      <tr>
          <td>Schema break</td>
          <td>connector 解析失敗</td>
          <td>停止 DDL rollout、補 schema evolution</td>
      </tr>
      <tr>
          <td>Missing key</td>
          <td>update / delete 缺少可套用 key</td>
          <td>修 replica identity / key contract</td>
      </tr>
      <tr>
          <td>Duplicate event</td>
          <td>consumer 重啟或 offset 回退</td>
          <td>idempotent consumer</td>
      </tr>
      <tr>
          <td>Downstream slow</td>
          <td>Kafka / sink lag 增加</td>
          <td>擴 sink、調 batch、保護 slot</td>
      </tr>
  </tbody>
</table>
<p>Slot lag 是最高優先訊號，因為它會占用 PostgreSQL WAL storage。Runbook 要有「何時暫停 producer」、「何時 drop slot」、「如何重建 snapshot」的明確門檻。</p>
<h2 id="selection-checklist">Selection Checklist</h2>
<p>Selection checklist 的核心責任是讓 plugin 選型可審查。</p>
<ol>
<li>Downstream 需要 DB-to-DB replication、JSON event、Protobuf event 還是 connector-managed event。</li>
<li>每張 table 是否有 stable key 與 replica identity。</li>
<li>Initial snapshot 如何銜接 streaming。</li>
<li>Schema evolution 如何通知 consumer。</li>
<li>Slot lag、connector lag、sink lag 如何告警。</li>
<li>Consumer 是否 idempotent。</li>
<li>Disaster recovery 後 slot / offset 如何重建。</li>
</ol>
<p>完成這份 checklist 後，再決定 plugin 與 connector。CDC 的成功標準是 downstream 能長期維持正確資料，而不只是成功建立 slot。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>Logical decoding plugins 完成後，實作 CDC pipeline 讀 <a href="../logical-replication-debezium/">Logical Replication / Debezium</a>；slot 維運讀 <a href="../replication-slot-management/">Replication Slot Management</a>；跨資料庫搬遷讀 <a href="/blog/backend/01-database/database-migration-playbook/" data-link-title="1.6 資料庫轉換實作：雙寫、回填、切流與回滾" data-link-desc="同 DB 內 schema 演進與資料變更的可分段驗證流程、跟 1.12 cross-DB migration 分工">Database Migration Playbook</a>。</p>
]]></content:encoded></item></channel></rss>