<?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>Drop-In on Tarragon</title><link>https://tarrragon.github.io/blog/tags/drop-in/</link><description>Recent content in Drop-In on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 22 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/drop-in/index.xml" rel="self" type="application/rss+xml"/><item><title>DragonflyDB → Redis / Valkey：回退到標準生態的遷移路徑</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/migrate-to-redis/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/migrate-to-redis/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。反向路徑見 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &amp;#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB&lt;/a>。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type B drop-in&lt;/strong>（RESP 協定相容），但 HA 和持久化有差異需要處理。&lt;/p>&lt;/blockquote>
&lt;h2 id="為什麼從-dragonflydb-遷回">為什麼從 DragonflyDB 遷回&lt;/h2>
&lt;p>DragonflyDB 遷回 Redis/Valkey 的 driver 跟正向遷移互為鏡像：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Redis Modules 需求&lt;/strong>：業務開始需要 RedisJSON、RediSearch 或 RedisTimeSeries，DragonflyDB 不支援 Redis Modules 生態&lt;/li>
&lt;li>&lt;strong>Cluster mode 需求&lt;/strong>：DragonflyDB 設計為單機 scale-up，當資料量超過單機記憶體上限（數 TB）或需要跨 node sharding 時，Redis Cluster 或 Valkey Cluster 是成熟選擇&lt;/li>
&lt;li>&lt;strong>Sentinel / HA 生態&lt;/strong>：DragonflyDB 的 HA 用自家 replication，不支援 Sentinel。若團隊已有 Sentinel 或 Operator 基礎設施，回到 Redis/Valkey 整合成本更低&lt;/li>
&lt;li>&lt;strong>BSL 授權疑慮&lt;/strong>：DragonflyDB 是 BSL 1.1（4 年後轉 Apache 2.0），部分組織偏好 BSD（Valkey）或即使是 RSALv2（Redis）的已知授權&lt;/li>
&lt;/ul>
&lt;h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>RESP 相容、data types 一致&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>DragonflyDB replication → Sentinel/Cluster；snapshotting → RDB+AOF&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>相同（key-value cache）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>DragonflyDB 1-2 nodes → Redis primary + replica + Sentinel（或 Cluster 6 nodes）&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>endpoint 換、client config 微調（無 API 差異）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>DragonflyDB snapshot → Redis RDB 相容&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>全域 Low-Medium → &lt;strong>Type B drop-in&lt;/strong>，工作重心在 HA 架構切換和持久化模式對齊。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。反向路徑見 <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a>。跑 6 維 diff dimension audit 後判定為 <strong>Type B drop-in</strong>（RESP 協定相容），但 HA 和持久化有差異需要處理。</p></blockquote>
<h2 id="為什麼從-dragonflydb-遷回">為什麼從 DragonflyDB 遷回</h2>
<p>DragonflyDB 遷回 Redis/Valkey 的 driver 跟正向遷移互為鏡像：</p>
<ul>
<li><strong>Redis Modules 需求</strong>：業務開始需要 RedisJSON、RediSearch 或 RedisTimeSeries，DragonflyDB 不支援 Redis Modules 生態</li>
<li><strong>Cluster mode 需求</strong>：DragonflyDB 設計為單機 scale-up，當資料量超過單機記憶體上限（數 TB）或需要跨 node sharding 時，Redis Cluster 或 Valkey Cluster 是成熟選擇</li>
<li><strong>Sentinel / HA 生態</strong>：DragonflyDB 的 HA 用自家 replication，不支援 Sentinel。若團隊已有 Sentinel 或 Operator 基礎設施，回到 Redis/Valkey 整合成本更低</li>
<li><strong>BSL 授權疑慮</strong>：DragonflyDB 是 BSL 1.1（4 年後轉 Apache 2.0），部分組織偏好 BSD（Valkey）或即使是 RSALv2（Redis）的已知授權</li>
</ul>
<h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>RESP 相容、data types 一致</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>DragonflyDB replication → Sentinel/Cluster；snapshotting → RDB+AOF</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>相同（key-value cache）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>DragonflyDB 1-2 nodes → Redis primary + replica + Sentinel（或 Cluster 6 nodes）</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>endpoint 換、client config 微調（無 API 差異）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>DragonflyDB snapshot → Redis RDB 相容</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>全域 Low-Medium → <strong>Type B drop-in</strong>，工作重心在 HA 架構切換和持久化模式對齊。</p>
<h2 id="相容性確認">相容性確認</h2>
<p>DragonflyDB → Redis 的相容方向跟 Redis → DragonflyDB 相反 — Redis 是 superset，回到 Redis 不會有功能缺失。但有幾個操作面差異需要處理：</p>
<table>
  <thead>
      <tr>
          <th>DragonflyDB 行為</th>
          <th>Redis 行為</th>
          <th>處理方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Multi-threaded 吞吐量</td>
          <td>單主線程（I/O threads 輔助）</td>
          <td>回到 Redis 後 throughput 下降是預期行為；若單機不夠需要 Cluster 分片</td>
      </tr>
      <tr>
          <td>Fork-less snapshot</td>
          <td>BGSAVE fork + COW</td>
          <td>關注 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence fork latency</a>，大 dataset 的 fork 會造成延遲 spike</td>
      </tr>
      <tr>
          <td>自家 replication</td>
          <td>Redis replication + Sentinel 或 Cluster</td>
          <td>需要重建 HA 架構，見下方階段二</td>
      </tr>
      <tr>
          <td>無 AOF</td>
          <td>AOF + RDB 混合持久化</td>
          <td>依需求決定是否開 AOF；純 cache 場景可只用 RDB</td>
      </tr>
      <tr>
          <td>無 Cluster mode</td>
          <td>Redis Cluster 或 Valkey Cluster</td>
          <td>資料量大時需要規劃 sharding</td>
      </tr>
  </tbody>
</table>
<h2 id="階段一資料匯出">階段一：資料匯出</h2>
<p>DragonflyDB 支援 <code>SAVE</code> / <code>BGSAVE</code> 產生 RDB 格式 snapshot，跟 Redis RDB 相容。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 在 DragonflyDB 觸發 snapshot</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-cli -h dragonfly-host BGSAVE
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 等 BGSAVE 完成</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">redis-cli -h dragonfly-host LASTSAVE
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 複製 snapshot 檔案到 Redis 資料目錄</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">cp /dragonfly-data/dump.rdb /redis-data/dump.rdb</span></span></code></pre></div><p>RDB 載入驗證：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 啟動 Redis 載入 RDB</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-server --dbfilename dump.rdb --dir /redis-data
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 驗證 key count</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">redis-cli DBSIZE</span></span></code></pre></div><p>若 DragonflyDB 跑的是較新版本產出的 RDB，先在測試環境驗證 Redis 能正常載入。DragonflyDB 的 RDB 基於 Redis 6.x 格式，Redis 7.x 和 Valkey 8.x 向下相容無問題。</p>
<h2 id="階段二ha-架構重建">階段二：HA 架構重建</h2>
<p>DragonflyDB 回到 Redis/Valkey 後，HA 需要從 DragonflyDB replication 切換到 Sentinel 或 Cluster。</p>
<h3 id="sentinel-路徑適合非分片場景">Sentinel 路徑（適合非分片場景）</h3>
<p>1 primary + N replica + 3 Sentinel nodes。配置見 <a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a>。</p>
<h3 id="cluster-路徑適合需要分片的場景">Cluster 路徑（適合需要分片的場景）</h3>
<p>最小 3 primary + 3 replica。配置見 <a href="/blog/backend/02-cache-redis/vendors/redis/cluster-resharding/" data-link-title="Redis Cluster Re-sharding：source = target，但 topology 重劃的 5 段流程" data-link-desc="Redis cluster re-sharding 是 5 type migration 漏類實證 — source / target 同 cluster、無 schema / paradigm 差、但 16384 slot 重分配是核心；本文涵蓋 4 種 re-sharding driver、slot migration 機制、redis-cli --cluster rebalance / reshard 工具、5 個 production 踩雷（cluster busy / replica lag / client cache stale / cross-slot transaction / monitor gap）">Redis Cluster Resharding</a>。</p>
<p>選擇依據：資料量 &lt; 單機記憶體的 70% 用 Sentinel，需要水平擴展用 Cluster。</p>
<h2 id="階段三client-切換">階段三：Client 切換</h2>
<p>Application 的 Redis client 不需要改 API — DragonflyDB 跟 Redis 用同一套 RESP 協定。需要改的只有：</p>
<ol>
<li><strong>Endpoint</strong>：從 DragonflyDB host:port 改為 Redis primary（或 Sentinel/Cluster endpoint）</li>
<li><strong>認證</strong>：若 DragonflyDB 用 <code>requirepass</code>，Redis 同參數；若要升級到 ACL 趁此機會配置</li>
<li><strong>Sentinel/Cluster 配置</strong>：client library 需要啟用 Sentinel discovery 或 Cluster mode</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 切換前：直連 DragonflyDB</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="n">redis</span><span class="o">.</span><span class="n">Redis</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s2">&#34;dragonfly-host&#34;</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">6379</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s2">&#34;secret&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 切換後：Sentinel 模式</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">sentinel</span> <span class="o">=</span> <span class="n">redis</span><span class="o">.</span><span class="n">Sentinel</span><span class="p">([(</span><span class="s2">&#34;sentinel-1&#34;</span><span class="p">,</span> <span class="mi">26379</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;sentinel-2&#34;</span><span class="p">,</span> <span class="mi">26379</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;sentinel-3&#34;</span><span class="p">,</span> <span class="mi">26379</span><span class="p">)])</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="n">sentinel</span><span class="o">.</span><span class="n">master_for</span><span class="p">(</span><span class="s2">&#34;mymaster&#34;</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s2">&#34;secret&#34;</span><span class="p">)</span></span></span></code></pre></div><h2 id="階段四效能-baseline-與回退">階段四：效能 baseline 與回退</h2>
<h3 id="效能預期">效能預期</h3>
<p>回到 Redis 後，單機 throughput 會低於 DragonflyDB（Redis 單主線程 vs DragonflyDB 多線程）。建立 baseline 時要跟 Redis 的歷史數據比，不是跟 DragonflyDB 比。</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>預期變化</th>
          <th>應對</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>吞吐量</td>
          <td>下降（單線程限制）</td>
          <td>Cluster 分片或 read replica 分散</td>
      </tr>
      <tr>
          <td>Latency p99</td>
          <td>BGSAVE 期間可能有 spike</td>
          <td>調整 BGSAVE 排程避開高峰</td>
      </tr>
      <tr>
          <td>記憶體使用</td>
          <td>上升 ~30%（Redis 記憶體效率較低）</td>
          <td>預先調整 maxmemory 和 eviction policy</td>
      </tr>
  </tbody>
</table>
<h3 id="回退路徑">回退路徑</h3>
<p>回退到 DragonflyDB：把 Redis 的 RDB dump 回 DragonflyDB 載入，endpoint 改回。Cache 資料可重建，即使 RDB 不搬，DragonflyDB 重啟後 cache miss 回源到 DB 即可。</p>
<p>DragonflyDB 在遷移完成後保留 7 天再下線。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>反向路徑：<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a></li>
<li>HA 重建：<a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/cluster-resharding/" data-link-title="Redis Cluster Re-sharding：source = target，但 topology 重劃的 5 段流程" data-link-desc="Redis cluster re-sharding 是 5 type migration 漏類實證 — source / target 同 cluster、無 schema / paradigm 差、但 16384 slot 重分配是核心；本文涵蓋 4 種 re-sharding driver、slot migration 機制、redis-cli --cluster rebalance / reshard 工具、5 個 production 踩雷（cluster busy / replica lag / client cache stale / cross-slot transaction / monitor gap）">Cluster Resharding</a></li>
<li>持久化注意：<a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">Persistence Fork Latency</a></li>
</ul>
]]></content:encoded></item><item><title>KeyDB → Redis / Valkey：從多線程 fork 回歸主線的遷移路徑</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/keydb/migrate-to-redis/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/keydb/migrate-to-redis/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/keydb/" data-link-title="KeyDB" data-link-desc="Redis multi-threaded fork、active-replication、Snap 採用">KeyDB&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type B drop-in&lt;/strong>（KeyDB 是 Redis fork、RESP 相容、RDB/AOF 相容），但 active-active replication 跟 multi-threading 特性回退需要額外處理。&lt;/p>&lt;/blockquote>
&lt;h2 id="為什麼從-keydb-遷回">為什麼從 KeyDB 遷回&lt;/h2>
&lt;p>KeyDB 是 Snap 維護的 Redis fork，主要差異化在多線程和 active-active replication。遷回的 driver：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>維護活躍度疑慮&lt;/strong>：KeyDB 的 release cadence 跟 Redis/Valkey 主線比較慢，部分組織擔心長期維護與安全 patch 的及時性&lt;/li>
&lt;li>&lt;strong>Valkey 生態收斂&lt;/strong>：Valkey 在 Linux Foundation 治理下快速演進（8.x 多線程改進），KeyDB 的多線程優勢逐漸縮小&lt;/li>
&lt;li>&lt;strong>Active-active 不再需要&lt;/strong>：業務不再需要跨 region active-active、或改用 application 層處理衝突解析&lt;/li>
&lt;li>&lt;strong>社群與工具生態&lt;/strong>：Redis/Valkey 的 client library、monitoring exporter、Operator 支援度更廣&lt;/li>
&lt;/ul>
&lt;h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>完全相容（fork 自 Redis 6.x）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>active-active → Sentinel/Cluster；multi-thread config 移除&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>相同&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>相近（1 primary + N replica + HA）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>endpoint 換、client config 微調&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>RDB/AOF 完全相容&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Type B drop-in，工作重心在 active-active replication 拆除和效能 baseline 對齊。&lt;/p>
&lt;h2 id="keydb-特有功能的處理">KeyDB 特有功能的處理&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>KeyDB 特有功能&lt;/th>
 &lt;th>Redis/Valkey 對應&lt;/th>
 &lt;th>遷移處理&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Multi-threading（&lt;code>server-threads&lt;/code>）&lt;/td>
 &lt;td>Redis I/O threads / Valkey 8 async I/O&lt;/td>
 &lt;td>回到 Redis 後吞吐量下降是預期，需要 benchmark 建立新 baseline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Active-active replication&lt;/td>
 &lt;td>無原生等價。Redis 需要 application 層解衝突或用 CRDTs（社群方案）&lt;/td>
 &lt;td>遷移前確認業務是否仍需 multi-master。不需要則直接切 Sentinel/Cluster&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>FLASH storage（&lt;code>storage-provider flash&lt;/code>）&lt;/td>
 &lt;td>無原生等價。Redis 純記憶體&lt;/td>
 &lt;td>遷移前把 FLASH 資料回收到記憶體，或接受遷移後記憶體需求上升。調整 &lt;code>maxmemory&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Subkey expires&lt;/td>
 &lt;td>Redis 無 subkey expire（只有 top-level key TTL）&lt;/td>
 &lt;td>檢查 application 是否依賴 subkey expire；若有需要改寫為 top-level key 或用 sorted set 模擬&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>EXPIREMEMBER&lt;/code> 命令&lt;/td>
 &lt;td>Redis 無此命令&lt;/td>
 &lt;td>grep application code 確認未使用；若有需改寫&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>FLASH storage 的處理取決於冷資料比例。如果多數資料在 FLASH 上（用 &lt;code>OBJECT FREQ&lt;/code> 確認），遷移後的 Redis 記憶體需求會大幅上升 — 要提前計算純記憶體所需容量，調整 instance 規格或改用更積極的 eviction policy。Subkey expires 和 &lt;code>EXPIREMEMBER&lt;/code> 的影響範圍通常較小，但一旦 application 依賴就需要重構資料結構（用 top-level key + TTL 或 sorted set 模擬過期）。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/keydb/" data-link-title="KeyDB" data-link-desc="Redis multi-threaded fork、active-replication、Snap 採用">KeyDB</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。跑 6 維 diff dimension audit 後判定為 <strong>Type B drop-in</strong>（KeyDB 是 Redis fork、RESP 相容、RDB/AOF 相容），但 active-active replication 跟 multi-threading 特性回退需要額外處理。</p></blockquote>
<h2 id="為什麼從-keydb-遷回">為什麼從 KeyDB 遷回</h2>
<p>KeyDB 是 Snap 維護的 Redis fork，主要差異化在多線程和 active-active replication。遷回的 driver：</p>
<ul>
<li><strong>維護活躍度疑慮</strong>：KeyDB 的 release cadence 跟 Redis/Valkey 主線比較慢，部分組織擔心長期維護與安全 patch 的及時性</li>
<li><strong>Valkey 生態收斂</strong>：Valkey 在 Linux Foundation 治理下快速演進（8.x 多線程改進），KeyDB 的多線程優勢逐漸縮小</li>
<li><strong>Active-active 不再需要</strong>：業務不再需要跨 region active-active、或改用 application 層處理衝突解析</li>
<li><strong>社群與工具生態</strong>：Redis/Valkey 的 client library、monitoring exporter、Operator 支援度更廣</li>
</ul>
<h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>完全相容（fork 自 Redis 6.x）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>active-active → Sentinel/Cluster；multi-thread config 移除</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>相同</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>相近（1 primary + N replica + HA）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>endpoint 換、client config 微調</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>RDB/AOF 完全相容</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>Type B drop-in，工作重心在 active-active replication 拆除和效能 baseline 對齊。</p>
<h2 id="keydb-特有功能的處理">KeyDB 特有功能的處理</h2>
<table>
  <thead>
      <tr>
          <th>KeyDB 特有功能</th>
          <th>Redis/Valkey 對應</th>
          <th>遷移處理</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Multi-threading（<code>server-threads</code>）</td>
          <td>Redis I/O threads / Valkey 8 async I/O</td>
          <td>回到 Redis 後吞吐量下降是預期，需要 benchmark 建立新 baseline</td>
      </tr>
      <tr>
          <td>Active-active replication</td>
          <td>無原生等價。Redis 需要 application 層解衝突或用 CRDTs（社群方案）</td>
          <td>遷移前確認業務是否仍需 multi-master。不需要則直接切 Sentinel/Cluster</td>
      </tr>
      <tr>
          <td>FLASH storage（<code>storage-provider flash</code>）</td>
          <td>無原生等價。Redis 純記憶體</td>
          <td>遷移前把 FLASH 資料回收到記憶體，或接受遷移後記憶體需求上升。調整 <code>maxmemory</code></td>
      </tr>
      <tr>
          <td>Subkey expires</td>
          <td>Redis 無 subkey expire（只有 top-level key TTL）</td>
          <td>檢查 application 是否依賴 subkey expire；若有需要改寫為 top-level key 或用 sorted set 模擬</td>
      </tr>
      <tr>
          <td><code>EXPIREMEMBER</code> 命令</td>
          <td>Redis 無此命令</td>
          <td>grep application code 確認未使用；若有需改寫</td>
      </tr>
  </tbody>
</table>
<p>FLASH storage 的處理取決於冷資料比例。如果多數資料在 FLASH 上（用 <code>OBJECT FREQ</code> 確認），遷移後的 Redis 記憶體需求會大幅上升 — 要提前計算純記憶體所需容量，調整 instance 規格或改用更積極的 eviction policy。Subkey expires 和 <code>EXPIREMEMBER</code> 的影響範圍通常較小，但一旦 application 依賴就需要重構資料結構（用 top-level key + TTL 或 sorted set 模擬過期）。</p>
<h3 id="active-active-拆除">Active-active 拆除</h3>
<p>若 KeyDB 的 active-active replication 正在使用，遷移前需要先收斂為單主寫入：</p>
<ol>
<li>選定一個 region 的 KeyDB 為 primary，其他 region 停止寫入</li>
<li>等資料同步完成（replica 追上 primary offset）</li>
<li>從 primary 做 RDB export</li>
<li>用 RDB 建立 Redis/Valkey instance</li>
<li>各 region 的 application 切到新的 Redis/Valkey（Sentinel 或 Cluster）</li>
</ol>
<h2 id="資料搬遷">資料搬遷</h2>
<p>KeyDB 的 RDB 和 AOF 與 Redis 格式相容，搬遷流程跟 DragonflyDB 回退類似：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># KeyDB 端觸發 BGSAVE</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-cli -h keydb-host BGSAVE
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 複製 RDB 到 Redis/Valkey 資料目錄</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">scp keydb-host:/data/dump.rdb redis-host:/data/dump.rdb
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># Redis/Valkey 載入</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">redis-server --dbfilename dump.rdb --dir /data</span></span></code></pre></div><p>如果使用了 FLASH storage，RDB 只包含記憶體中的資料。FLASH 上的冷資料需要先用 <code>OBJECT FREQ</code> 確認存取頻率，決定是要 warm up 到記憶體再 export，還是接受遷移後冷資料 cache miss 回源。</p>
<h2 id="效能差異預期">效能差異預期</h2>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>KeyDB → Redis 變化</th>
          <th>應對</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>吞吐量</td>
          <td>下降（KeyDB multi-thread → Redis single-thread）</td>
          <td>評估是否需要 Cluster 分片補償。Valkey 8 的 async I/O 可部分彌補</td>
      </tr>
      <tr>
          <td>記憶體</td>
          <td>上升（若使用了 FLASH storage 被移除）</td>
          <td>提前計算純記憶體所需容量，調整 instance 規格</td>
      </tr>
      <tr>
          <td>Latency p99</td>
          <td>BGSAVE fork spike 可能出現</td>
          <td>KeyDB 的多線程降低了 fork 影響，回到 Redis 需要關注 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence fork latency</a></td>
      </tr>
      <tr>
          <td>Active-active latency</td>
          <td>不適用（已拆除）</td>
          <td>N/A</td>
      </tr>
  </tbody>
</table>
<h2 id="回退路徑">回退路徑</h2>
<p>Cache 資料可重建，回退方式：</p>
<ol>
<li>Application endpoint 改回 KeyDB</li>
<li>若 KeyDB 已下線，重啟 KeyDB 載入 Redis 的 RDB（格式相容）</li>
<li>Cache miss 回源到 DB 自然 warm up</li>
</ol>
<p>KeyDB 保留 7 天再下線。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/keydb/" data-link-title="KeyDB" data-link-desc="Redis multi-threaded fork、active-replication、Snap 採用">KeyDB</a>、<a href="/blog/backend/02-cache-redis/vendors/keydb/active-active-replication/" data-link-title="KeyDB active-active 多主複製：last-write-wins 會默默吃掉哪一筆寫入" data-link-desc="KeyDB 的 active-active 讓兩個 master 都能寫、互相同步，聽起來解決了跨區寫入的所有問題——直到兩邊同時寫同一個 key，last-write-wins 默默丟掉其中一筆。本文展開 active-active 的複製機制與衝突語意、實機驗證雙向同步、5 個把多主複製寫成資料遺失與迴圈的 production 踩坑，以及哪些資料能放 active-active、哪些不能的邊界">KeyDB Active-Active Replication</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>HA 重建：<a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a></li>
<li>效能參考：<a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">Persistence Fork Latency</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/connection-pipeline-latency/" data-link-title="Redis 連線與 pipeline：RTT 稅、連線池與一次往返打包多命令" data-link-desc="Redis 單命令通常微秒級執行，但 application 端量到的延遲是毫秒級——差距全在網路往返（RTT）。pipelining 的本質不是『批次發命令』，是把 N 次 RTT 壓成 1 次。本文展開 RTT 會計、連線池配置、pipeline 與 MULTI 的差異、5 個把連線與往返寫成延遲與正確性問題的 production 踩坑，以及連線模型撞牆的邊界">Connection Pipeline Latency</a></li>
</ul>
]]></content:encoded></item><item><title>Redis → Valkey：同一份程式碼、不同授權的 drop-in 遷移</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-valkey/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-valkey/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type B drop-in&lt;/strong>（全維度 Low），結構走 6-section + 相容性 audit 前置。實機驗證於 valkey/valkey:8（valkey_version 8.1.8、redis_version 7.2.4）、最後檢查日 2026-06-16。&lt;/p>&lt;/blockquote>
&lt;h2 id="同一份程式碼不同授權">同一份程式碼、不同授權&lt;/h2>
&lt;p>多數 migration 的工作量在「source 跟 target 不一樣」——schema 要翻譯、API 要改、資料要轉。Redis → Valkey 幾乎沒有這個問題：Valkey 是 2024 年從 Redis 7.2.4 直接 fork 出來的，那一刻它跟 Redis 是 bit-for-bit 同一份程式碼。RDB 與 AOF 檔案格式相同（可以直接把 Redis 的資料目錄拷給 Valkey 載入）、RESP 協定相同、所有 Redis client library 不改一行就能連。技術上，這是 cache 領域最容易的遷移。&lt;/p>
&lt;p>那為什麼要寫一篇 playbook？因為這個遷移的工作量不在資料層，在兩個別的地方。第一是&lt;strong>授權&lt;/strong>——Redis 2024 改成 RSALv2 / SSPL（非 OSI 認可），Valkey 是 BSD 3-clause（OSI、Linux Foundation 治理），這個遷移的整個 driver 是授權合規，而合規驗證有它自己的流程。第二是&lt;strong>fork 後的分歧&lt;/strong>——fork 那一刻兩者相同，但之後各自演進：Redis 加了 7.4+ 的新功能、Valkey 加了自己的（如 8.x 多執行緒），用到 fork 之後 Redis 新功能的部署會有相容缺口。&lt;/p>
&lt;p>&lt;code>INFO server&lt;/code> 上看得到這個「同源但分歧」的事實：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">valkey-cli INFO server &lt;span class="p">|&lt;/span> grep -E &lt;span class="s2">&amp;#34;redis_version|valkey_version&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># redis_version:7.2.4 ← fork 點、client 以此判斷相容性（裝成 Redis 7.2.4）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># valkey_version:8.1.8 ← Valkey 自己的演進線&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>redis_version:7.2.4&lt;/code> 是相容性的保證（client 看到就以 Redis 7.2.4 行為運作）；&lt;code>valkey_version&lt;/code> 是分歧的證據。這篇 playbook 處理的就是「資料層幾乎零工作、工作在授權與分歧盤點」的 drop-in 遷移。&lt;/p>
&lt;h2 id="6-維-diff-dimension-audit為什麼是-type-b">6 維 diff dimension audit：為什麼是 Type B&lt;/h2>
&lt;p>跑 diff dimension audit，Redis → Valkey 全維度 Low：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>同 Redis 7.2.4（fork 同源）、RESP 協定一致&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>同 redis.conf、同監控指標、同 CLI 命令&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>完全相同（同一份 code base 演進）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>1 → 1（單服務換單服務）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>零（所有 Redis client library 直接相容）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>RDB / AOF 檔案相容、可直接拷資料目錄&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>全 Low → &lt;strong>Type B drop-in&lt;/strong>（6-section + 相容性 audit 前置、週期 1-4 週）。跟同模組的 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &amp;#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB&lt;/a> 對照：DragonflyDB 是 C++ 重寫（drop-in 但 Lua / encoding / module 有差異），Valkey 是 fork（同源、連 RDB 檔都相容）——Valkey 的相容度比 DragonflyDB 更高，是 Type B 裡最純粹的一端。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。跑 6 維 diff dimension audit 後判定為 <strong>Type B drop-in</strong>（全維度 Low），結構走 6-section + 相容性 audit 前置。實機驗證於 valkey/valkey:8（valkey_version 8.1.8、redis_version 7.2.4）、最後檢查日 2026-06-16。</p></blockquote>
<h2 id="同一份程式碼不同授權">同一份程式碼、不同授權</h2>
<p>多數 migration 的工作量在「source 跟 target 不一樣」——schema 要翻譯、API 要改、資料要轉。Redis → Valkey 幾乎沒有這個問題：Valkey 是 2024 年從 Redis 7.2.4 直接 fork 出來的，那一刻它跟 Redis 是 bit-for-bit 同一份程式碼。RDB 與 AOF 檔案格式相同（可以直接把 Redis 的資料目錄拷給 Valkey 載入）、RESP 協定相同、所有 Redis client library 不改一行就能連。技術上，這是 cache 領域最容易的遷移。</p>
<p>那為什麼要寫一篇 playbook？因為這個遷移的工作量不在資料層，在兩個別的地方。第一是<strong>授權</strong>——Redis 2024 改成 RSALv2 / SSPL（非 OSI 認可），Valkey 是 BSD 3-clause（OSI、Linux Foundation 治理），這個遷移的整個 driver 是授權合規，而合規驗證有它自己的流程。第二是<strong>fork 後的分歧</strong>——fork 那一刻兩者相同，但之後各自演進：Redis 加了 7.4+ 的新功能、Valkey 加了自己的（如 8.x 多執行緒），用到 fork 之後 Redis 新功能的部署會有相容缺口。</p>
<p><code>INFO server</code> 上看得到這個「同源但分歧」的事實：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">valkey-cli INFO server <span class="p">|</span> grep -E <span class="s2">&#34;redis_version|valkey_version&#34;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># redis_version:7.2.4    ← fork 點、client 以此判斷相容性（裝成 Redis 7.2.4）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># valkey_version:8.1.8   ← Valkey 自己的演進線</span></span></span></code></pre></div><p><code>redis_version:7.2.4</code> 是相容性的保證（client 看到就以 Redis 7.2.4 行為運作）；<code>valkey_version</code> 是分歧的證據。這篇 playbook 處理的就是「資料層幾乎零工作、工作在授權與分歧盤點」的 drop-in 遷移。</p>
<h2 id="6-維-diff-dimension-audit為什麼是-type-b">6 維 diff dimension audit：為什麼是 Type B</h2>
<p>跑 diff dimension audit，Redis → Valkey 全維度 Low：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>同 Redis 7.2.4（fork 同源）、RESP 協定一致</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>同 redis.conf、同監控指標、同 CLI 命令</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>完全相同（同一份 code base 演進）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>1 → 1（單服務換單服務）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>零（所有 Redis client library 直接相容）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>RDB / AOF 檔案相容、可直接拷資料目錄</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>全 Low → <strong>Type B drop-in</strong>（6-section + 相容性 audit 前置、週期 1-4 週）。跟同模組的 <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a> 對照：DragonflyDB 是 C++ 重寫（drop-in 但 Lua / encoding / module 有差異），Valkey 是 fork（同源、連 RDB 檔都相容）——Valkey 的相容度比 DragonflyDB 更高，是 Type B 裡最純粹的一端。</p>
<p>這個遷移的特殊之處是 driver 在資料層之外：它是<strong>授權 / 合規驅動</strong>。依 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration 方法論</a> 的漏類處理，政策 / 合規驅動的遷移資料層仍走 Type B，但 audit 重點多一塊<strong>授權驗證與證據收集</strong>。</p>
<h2 id="相容性-auditcutover-前要確認的清單">相容性 audit：cutover 前要確認的清單</h2>
<p>Valkey 號稱 100% 相容 Redis 7.2.4，但「100%」的邊界在 fork 之後的分歧。Pre-migration 必跑的 audit：</p>
<table>
  <thead>
      <tr>
          <th>Redis feature</th>
          <th>Valkey 相容程度</th>
          <th>Action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Core data types / commands / RESP</td>
          <td>完全相容（fork 自 7.2.4）</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>RDB / AOF 檔案格式</td>
          <td>完全相容（可直接拷資料目錄）</td>
          <td>無需轉檔</td>
      </tr>
      <tr>
          <td>Eviction / persistence / pub-sub</td>
          <td>完全相容</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>Client libraries</td>
          <td>完全相容（透過 redis_version 協商）</td>
          <td>無需改 code</td>
      </tr>
      <tr>
          <td>Cluster / Sentinel</td>
          <td>完全相容（同 Redis 模型）</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>Redis 7.4+ 新功能（fork 後新增）</td>
          <td>Valkey 不一定跟進</td>
          <td>盤點是否用到、確認 Valkey 對應</td>
      </tr>
      <tr>
          <td>Redis Stack 商業 module（JSON/Search）</td>
          <td>不相容（Valkey 有 valkey-search / valkey-bloom）</td>
          <td>盤點 module 使用、確認替代或改寫</td>
      </tr>
      <tr>
          <td>RedisInsight 等 Redis Inc 監控工具</td>
          <td>部分 vendor-specific 命令缺</td>
          <td>改通用工具（valkey-cli / redis_exporter）</td>
      </tr>
  </tbody>
</table>
<p><strong>audit 的關鍵 output</strong>：兩份清單——(1) 用到的 Redis 7.4+ 功能（fork 後新增、Valkey 可能沒有）、(2) 載入的 Redis Stack module。這兩塊是僅有的相容風險，其餘資料層零工作。盤點方法：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 盤點載入的 module（最大相容風險）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-cli MODULE LIST
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 盤點是否用到 7.4+ 功能（抓 production traffic 對照 Redis 7.4 changelog）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">redis-cli MONITOR    <span class="c1"># 限時抓樣、grep 可疑的新命令</span></span></span></code></pre></div><h2 id="step-by-step-cutover">Step-by-step cutover</h2>
<p>因為 RDB 檔案相容，cutover 比 DragonflyDB 更簡單（無版本轉換風險）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 部署 Valkey（同 Redis 配置、可直接沿用 redis.conf）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">docker run -d --name valkey -p 6380:6379 <span class="se">\
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="se"></span>  -v /data/valkey:/data <span class="se">\
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="se"></span>  valkey/valkey:8 valkey-server /etc/valkey/valkey.conf
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 2. Redis 端 BGSAVE 產生 RDB</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">redis-cli -h redis-primary BGSAVE
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">redis-cli -h redis-primary INFO Persistence <span class="p">|</span> grep rdb_last_save_time
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 3. 把 dump.rdb 拷給 Valkey（檔案格式相容、無需轉換）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">scp redis-primary:/var/lib/redis/dump.rdb valkey-host:/data/valkey/
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 4. 重啟 Valkey 載入 RDB</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">docker restart valkey
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 5. 驗證資料一致 + 版本</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">valkey-cli -h valkey-host -p <span class="m">6380</span> DBSIZE          <span class="c1"># 對齊 Redis DBSIZE</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">valkey-cli -h valkey-host -p <span class="m">6380</span> INFO server <span class="p">|</span> grep redis_version  <span class="c1"># 7.2.4</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"># 6. 替代方案（零停機）：用 replicaof 讓 Valkey 當 Redis 的 replica、即時同步後 promote</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1">#    valkey-cli -h valkey-host REPLICAOF redis-primary 6379</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1">#    重要邊界：此路徑只在 source 是 Redis 7.2 或更早版本時成立。</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1">#    Redis 7.4+（Community Edition）改了複製格式、Valkey 無法當其 replica</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1">#    → source 為 7.4+ 時改走上面的 RDB 拷貝路徑（步驟 2-4）。</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"># 7. Cutover：client 配置切到 Valkey endpoint、Redis 留 standby</span></span></span></code></pre></div><p>關鍵時間點：</p>
<ul>
<li><strong>RDB 拷貝 + load</strong>：100GB 約 5-15 分鐘（無版本轉換、比 DragonflyDB 少一道風險）</li>
<li><strong>replicaof 路徑</strong>：要零停機可讓 Valkey 當 Redis replica 即時同步、確認 lag 趨零後 promote + 切 client（僅限 source 為 Redis 7.2 或更早；7.4+ 複製格式已分歧、不適用、改走 RDB 拷貝）</li>
<li><strong>Cutover</strong>：client 配置切換（單次完成、硬邊界）、Redis 留 standby 1-2 週</li>
<li><strong>Decom</strong>：無相容問題後關閉 Redis</li>
</ul>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1用到-redis-74-功能valkey-沒有">Case 1：用到 Redis 7.4+ 功能、Valkey 沒有</h3>
<p><strong>徵兆</strong>：cutover 後某功能報 <code>unknown command</code> 或行為不同，命令是 Redis 在 7.4 之後（fork 點之後）才加的。</p>
<p><strong>根因</strong>：Valkey fork 自 Redis 7.2.4，Redis 7.4+ 新增的功能 Valkey 不一定跟進。pre-migration audit 漏掉了這些 fork 後的新功能。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>pre-migration 對照 Redis 7.4+ changelog 盤點用到的新功能（audit 清單第一項）</li>
<li>Valkey 有對應就確認版本、沒有就評估改寫或留在 Redis 商業版</li>
<li>多數標準 cache 用法不碰 7.4+ 新功能，這個風險集中在用了較新進階功能的部署</li>
<li>Valkey 自己的 roadmap（valkey.io）會逐步補上 Redis 新功能，可追蹤</li>
</ol>
<h3 id="case-2載入了-redis-stack-商業-module">Case 2：載入了 Redis Stack 商業 module</h3>
<p><strong>徵兆</strong>：cutover 後 <code>JSON.SET</code> / <code>FT.SEARCH</code> 報 <code>unknown command</code>，application 部分功能失效。</p>
<p><strong>根因</strong>：用了 Redis Stack 的商業 module（RedisJSON / RedisSearch），這些不在 fork 範圍。Valkey 有自己的 valkey-search / valkey-bloom，但不是同一套命令、要另外安裝。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>pre-migration <code>MODULE LIST</code> 盤點所有載入的 module（audit 清單第二項）</li>
<li>確認 Valkey 對應替代（valkey-search 對 RedisSearch）、確認命令相容度</li>
<li>沒有對應的評估改 module-free 設計（JSON 操作拉回 application 層）或留在 Redis Inc 商業版</li>
<li>對應 <a href="/blog/backend/02-cache-redis/vendors/valkey/redis-compatibility-and-io-threads/" data-link-title="Valkey 相容性驗證與 io-threads 調校：drop-in 切換與多執行緒的實機判讀" data-link-desc="Valkey 跟 Redis 100% 相容這句話要怎麼驗證、切換才敢上線。本文用 INFO server 的雙版本回報拆解相容性的真實邊界、展開 Valkey 8 的 io-threads 多執行緒調校、5 個把 drop-in 切換或執行緒配置寫成事故的 production 踩坑，以及相容性撞牆該怎麼判斷的邊界">Valkey 相容性 deep article</a> 的三層相容邊界</li>
</ol>
<h3 id="case-3以為換-valkey-解決了記憶體--fork-問題">Case 3：以為換 Valkey 解決了記憶體 / fork 問題</h3>
<p><strong>徵兆</strong>：因為 Redis 的 OOM 或 fork 延遲尖峰而遷 Valkey，遷完發現同樣問題還在。</p>
<p><strong>根因</strong>：Valkey fork 自 Redis 7.2.4，繼承了完全相同的記憶體模型、eviction 演算法、AOF/RDB fork 機制。這些行為在 Valkey 上一模一樣——遷移沒有改變它們。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>記憶體 / fork 調校在 Valkey 上跟 Redis 完全相同，直接套用 <a href="/blog/backend/02-cache-redis/vendors/redis/memory-eviction-tuning/" data-link-title="Redis 記憶體與淘汰調校：maxmemory-policy、LFU 與碎片化的實戰判讀" data-link-desc="Redis 的記憶體是一條會在半夜爆掉的曲線：maxmemory 設多少、policy 選 LRU 還 LFU、碎片化什麼時候開始吃掉 30% RAM、OOM 時 noeviction 怎麼讓寫入全部失敗。本文展開 Redis 記憶體會計模型、eviction policy 的選型判讀、5 個把記憶體配置寫成 production 事故的踩坑，以及單機記憶體撞牆後該往 cluster 還是 DragonflyDB 走的邊界">Redis 記憶體調校</a> 與 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence / fork latency</a></li>
<li>遷 Valkey 的理由應是授權合規 / 多執行緒吞吐 / managed 成本，不是記憶體問題</li>
<li>fork 尖峰要根治走 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a> 的 fork-less，不是換 Valkey</li>
<li>遷移前釐清痛點是授權（Valkey 解）還是架構（Valkey 不解）</li>
</ol>
<h3 id="case-4授權合規驗證沒做完整合規卡關">Case 4：授權合規驗證沒做完整、合規卡關</h3>
<p><strong>徵兆</strong>：技術遷移完成、但法務 / 合規 review 要求證明「不再使用 RSALv2 / SSPL 授權的軟體」，缺少證據。</p>
<p><strong>根因</strong>：這個遷移的 driver 是授權合規，但團隊只做了技術 cutover、沒收集合規證據。Redis 的 binary / image / 相依套件若還殘留在某些環境，合規目標沒真正達成。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>盤點所有環境（dev / staging / prod / CI）的 Redis binary / image / 相依，確認全部換成 Valkey</li>
<li>收集合規證據：image SBOM、套件清單、部署 manifest 顯示 Valkey BSD 授權</li>
<li>把「不再使用非 OSI 授權 cache」寫成可驗證的 CI 檢查（掃 image / 依賴）</li>
<li>依 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration 方法論</a> 的合規驅動漏類，audit 重點就是 evidence collection</li>
</ol>
<h3 id="case-5監控-dashboard-部分指標斷掉">Case 5：監控 dashboard 部分指標斷掉</h3>
<p><strong>徵兆</strong>：cutover 後 RedisInsight 或某監控 dashboard 部分面板空白、vendor-specific 命令回錯。</p>
<p><strong>根因</strong>：RedisInsight 等 Redis Inc 工具有部分偏商業版的命令，Valkey 不一定實作。核心指標通用，但進階面板可能缺。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>監控改用通用工具：valkey-cli INFO、Prometheus + redis_exporter（相容 Valkey）、Grafana</li>
<li>核心指標（used_memory / keyspace_hits / connected_clients）在 Valkey 完全相容、覆蓋不受影響</li>
<li>把監控相容性納入 cutover 前驗證、不要遷完才發現面板空白</li>
<li>RedisInsight 連 Valkey 多數仍可用、只是部分 vendor 進階面板缺</li>
</ol>
<h2 id="capacity--cost-對照">Capacity / cost 對照</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Redis（self-managed）</th>
          <th>Valkey（self-managed）</th>
          <th>取捨</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>授權</td>
          <td>RSALv2 / SSPL（非 OSI）</td>
          <td>BSD 3-clause（OSI、Linux Foundation）</td>
          <td>Valkey 對合規敏感場景是決定性優勢</td>
      </tr>
      <tr>
          <td>核心效能</td>
          <td>baseline</td>
          <td>同 Redis 7.2.4 + 8.x 多執行緒選項</td>
          <td>Valkey 多核 workload 可更高（依 workload）</td>
      </tr>
      <tr>
          <td>相容度</td>
          <td>原生</td>
          <td>100%（fork、檔案相容）</td>
          <td>平手（同源）</td>
      </tr>
      <tr>
          <td>記憶體 / fork</td>
          <td>baseline</td>
          <td>完全相同（同源）</td>
          <td>平手（遷移不改變這層）</td>
      </tr>
      <tr>
          <td>7.4+ 新功能</td>
          <td>有</td>
          <td>不一定跟進</td>
          <td>Redis 領先（用到才在意）</td>
      </tr>
      <tr>
          <td>Redis Stack module</td>
          <td>RedisJSON / Search / Graph</td>
          <td>valkey-search / valkey-bloom（不同套）</td>
          <td>Redis 商業 module 較全</td>
      </tr>
      <tr>
          <td>managed 選項</td>
          <td>ElastiCache for Redis（legacy）</td>
          <td>ElastiCache for Valkey（AWS default、約低 20%）</td>
          <td>Valkey 在 AWS 生態成本優勢</td>
      </tr>
      <tr>
          <td>遷移成本</td>
          <td>—</td>
          <td>極低（drop-in + 檔案相容）</td>
          <td>Valkey 是最容易的遷移目標</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：合規敏感（公部門 / 企業 OSI 政策）或想降 managed 成本 → 遷 Valkey（drop-in、風險集中在 module / 7.4+ 盤點）；重度依賴 Redis Stack 商業 module → 留 Redis Inc 商業版。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-elasticache-for-valkey-對位">跟 <a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">ElastiCache for Valkey</a> 對位</h3>
<p>AWS 已把 ElastiCache default engine 設為 Valkey（約低 Redis 20%）。自管 Redis → ElastiCache for Valkey 是「換授權 + 轉 managed」一次到位，但要同時處理 <a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/managed-responsibility-boundary/" data-link-title="AWS ElastiCache 的責任邊界：managed 接手了什麼、又默默留下什麼" data-link-desc="ElastiCache 把 failover、patching、snapshot、跨 AZ 複製接走，但 cache stampede、client 重連、key 設計、eviction policy 還是你的事。本文用 shared responsibility 拆解 managed 的真實邊界、展開 engine 選擇與 cluster mode 配置、5 個把『以為 AWS 全包』寫成事故的 production 踩坑，以及 ElastiCache 到 MemoryDB 的 durability 邊界">managed 責任邊界</a>（failover / cluster mode / client 重連）。</p>
<h3 id="跟-client--監控整合">跟 client / 監控整合</h3>
<p>client library 零改（透過 redis_version 協商）；監控把 exporter 指向 Valkey 即可（redis_exporter 相容）、RedisInsight 部分面板需換通用工具。</p>
<h3 id="跟-valkey-8-多執行緒對位">跟 Valkey 8 多執行緒對位</h3>
<p>遷移後可評估開 Valkey 8 的 io-threads 榨多核吞吐（Redis 7.2.4 沒有的能力），見 <a href="/blog/backend/02-cache-redis/vendors/valkey/redis-compatibility-and-io-threads/" data-link-title="Valkey 相容性驗證與 io-threads 調校：drop-in 切換與多執行緒的實機判讀" data-link-desc="Valkey 跟 Redis 100% 相容這句話要怎麼驗證、切換才敢上線。本文用 INFO server 的雙版本回報拆解相容性的真實邊界、展開 Valkey 8 的 io-threads 多執行緒調校、5 個把 drop-in 切換或執行緒配置寫成事故的 production 踩坑，以及相容性撞牆該怎麼判斷的邊界">Valkey 相容性與 io-threads deep article</a>。</p>
<h3 id="下一步議題">下一步議題</h3>
<ul>
<li><strong>反向遷移</strong>（Valkey → Redis）：僅在重度依賴 Redis 7.4+ 功能或 Stack 商業 module 時需要、同樣 drop-in</li>
<li><strong>跨雲 managed Valkey</strong>：GCP Memorystore / Azure Cache 的 Valkey 支援陸續推出、評估 vendor boundary</li>
<li><strong>授權合規 CI 化</strong>：把「不使用非 OSI 授權 cache」寫成持續檢查</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>平行 migration playbook：<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a>（重寫型 drop-in）、<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-memcached/" data-link-title="Redis → Memcached：Memcached 不是 simpler Redis、是 cache paradigm" data-link-desc="Redis → Memcached 是 Type E paradigm reduction migration — 從 multi-paradigm（KV &#43; 資料結構 &#43; pub/sub &#43; Lua &#43; streams）退到 pure cache；不是「remove Redis features」、是「重新分配 Redis-specific feature 到對應 specialized 服務」；5 個 production 踩雷 &#43; paradigm reduction 路線">Redis → Memcached</a></li>
<li>Methodology：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a>（Type B drop-in + 合規驅動漏類）</li>
</ul>
]]></content:encoded></item><item><title>Terraform → OpenTofu：HCL 跟 state file 級 drop-in、CI runner 切 binary 完成</title><link>https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/terraform/migrate-to-opentofu/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/terraform/migrate-to-opentofu/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/terraform/" data-link-title="Terraform / OpenTofu" data-link-desc="Infrastructure as Code 主流工具">Terraform&lt;/a>（source）跟 OpenTofu（target）。Type B drop-in migration 標準形態、跑 &lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration-playbook-methodology 6 維 audit&lt;/a> 後對映 &lt;em>6 維皆 Low → Type B drop-in&lt;/em>；本文驗證 skill 的 Type B anatomy 在 IaC 領域成立。&lt;/p>&lt;/blockquote>
&lt;h2 id="hcl--state-file--provider-三層-diff-sample">HCL / state file / provider 三層 diff sample&lt;/h2>
&lt;p>跟前批 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &amp;#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB&lt;/a> 同為 Type B drop-in、本文用 code-led entry — 直接給 3 種 diff sample 證明「真 drop-in」：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. HCL syntax: 完全相同 (Terraform 1.5.x baseline)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_s3_bucket&amp;#34; &amp;#34;logs&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="n"> bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;myapp-logs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="n"> tags&lt;/span> &lt;span class="o">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="n"> Env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="err">#&lt;/span> &lt;span class="k">兩家&lt;/span> &lt;span class="k">binary&lt;/span> &lt;span class="k">都接受&lt;/span>&lt;span class="err">、&lt;/span>&lt;span class="k">執行結果一致&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. State file: 完全相同 schema&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">$ cat terraform.tfstate &lt;span class="p">|&lt;/span> jq &lt;span class="s1">&amp;#39;.version, .terraform_version&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="m">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="s2">&amp;#34;1.5.7&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 切 OpenTofu 後 re-init、state 保留&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">$ tofu init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">$ cat terraform.tfstate &lt;span class="p">|&lt;/span> jq &lt;span class="s1">&amp;#39;.version, .terraform_version&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="m">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="s2">&amp;#34;1.6.0&amp;#34;&lt;/span> &lt;span class="c1"># tool version 標記變、其他不變&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. Provider: registry 路徑唯一明顯差異
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">terraform&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="k">required_providers&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="n"> aws&lt;/span> &lt;span class="o">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="n"> source&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;hashicorp/aws&amp;#34;&lt;/span>&lt;span class="c1"> # 兩家共用 source 字串
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n"> version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;~&amp;gt; 5.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">}&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># Terraform 從 registry.terraform.io 拉
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="err">#&lt;/span> &lt;span class="k">OpenTofu&lt;/span> &lt;span class="k">預設從&lt;/span> &lt;span class="k">registry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">opentofu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">org&lt;/span> &lt;span class="k">拉&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">fallback&lt;/span> &lt;span class="k">到&lt;/span> &lt;span class="k">terraform&lt;/span> &lt;span class="k">registry&lt;/span>&lt;span class="p">)&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>3 層 diff sample 顯示：HCL / state schema / 主流 provider 配置完全相容；唯一明顯差異在 &lt;em>registry routing&lt;/em>。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link <a href="/blog/backend/05-deployment-platform/vendors/terraform/" data-link-title="Terraform / OpenTofu" data-link-desc="Infrastructure as Code 主流工具">Terraform</a>（source）跟 OpenTofu（target）。Type B drop-in migration 標準形態、跑 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration-playbook-methodology 6 維 audit</a> 後對映 <em>6 維皆 Low → Type B drop-in</em>；本文驗證 skill 的 Type B anatomy 在 IaC 領域成立。</p></blockquote>
<h2 id="hcl--state-file--provider-三層-diff-sample">HCL / state file / provider 三層 diff sample</h2>
<p>跟前批 <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a> 同為 Type B drop-in、本文用 code-led entry — 直接給 3 種 diff sample 證明「真 drop-in」：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. HCL syntax: 完全相同 (Terraform 1.5.x baseline)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket&#34; &#34;logs&#34;</span> {
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="s2">&#34;myapp-logs&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">  tags</span> <span class="o">=</span> {
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">    Env</span> <span class="o">=</span> <span class="s2">&#34;production&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  }
</span></span><span class="line"><span class="ln">7</span><span class="cl">}
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="err">#</span> <span class="k">兩家</span> <span class="k">binary</span> <span class="k">都接受</span><span class="err">、</span><span class="k">執行結果一致</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 2. State file: 完全相同 schema</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">$ cat terraform.tfstate <span class="p">|</span> jq <span class="s1">&#39;.version, .terraform_version&#39;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="m">4</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="s2">&#34;1.5.7&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 切 OpenTofu 後 re-init、state 保留</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">$ tofu init
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">$ cat terraform.tfstate <span class="p">|</span> jq <span class="s1">&#39;.version, .terraform_version&#39;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="m">4</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s2">&#34;1.6.0&#34;</span>  <span class="c1"># tool version 標記變、其他不變</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 3. Provider: registry 路徑唯一明顯差異
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">terraform</span> {
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="k">required_providers</span> {
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">    aws</span> <span class="o">=</span> {
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">      source</span>  <span class="o">=</span> <span class="s2">&#34;hashicorp/aws&#34;</span><span class="c1">     # 兩家共用 source 字串
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span><span class="n">      version</span> <span class="o">=</span> <span class="s2">&#34;~&gt; 5.0&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    }
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  }
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">}<span class="c1">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># Terraform 從 registry.terraform.io 拉
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="err">#</span> <span class="k">OpenTofu</span> <span class="k">預設從</span> <span class="k">registry</span><span class="p">.</span><span class="k">opentofu</span><span class="p">.</span><span class="k">org</span> <span class="k">拉</span> <span class="p">(</span><span class="k">fallback</span> <span class="k">到</span> <span class="k">terraform</span> <span class="k">registry</span><span class="p">)</span></span></span></code></pre></div><p>3 層 diff sample 顯示：HCL / state schema / 主流 provider 配置完全相容；唯一明顯差異在 <em>registry routing</em>。</p>
<p>跑 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">6 維 diff dimension audit</a>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>HCL 完全相容、CLI command 對映 (terraform → tofu)</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>同 workflow (init / plan / apply)</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Paradigm</td>
          <td>同 IaC declarative</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Components</td>
          <td>同 single binary</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>無（不是 application、是 infrastructure tool）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>同 single state file backend</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>6 維皆 Low → Type B drop-in。</p>
<h2 id="為什麼遷license--governance--community-三條-driver">為什麼遷：license / governance / community 三條 driver</h2>
<p>跟前批 <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a> 不同（cost / performance driver）、Terraform → OpenTofu 主要 driver 在 governance：</p>
<table>
  <thead>
      <tr>
          <th>Driver</th>
          <th>觸發場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>License</strong></td>
          <td>Terraform 在 2023-08 改 BSL（Business Source License）、商業使用限制；OpenTofu 維持 MPL 2.0 開源</td>
      </tr>
      <tr>
          <td><strong>Vendor neutrality</strong></td>
          <td>多雲 / 多客戶情境想避免 HashiCorp lock-in、用 Linux Foundation 治理的 OpenTofu</td>
      </tr>
      <tr>
          <td><strong>Community / feature</strong></td>
          <td>OpenTofu 1.6+ 加 state encryption、跟 Terraform 商業版差異化、社群驅動 feature</td>
      </tr>
  </tbody>
</table>
<p>反向 driver（OpenTofu → Terraform）：</p>
<ul>
<li>Terraform Cloud / Enterprise 特定 feature 依賴（policy as code 用 Sentinel、跟 OpenTofu 自家 OPA 不對等）</li>
<li>既有 module 在 Terraform registry 維護、未同步 OpenTofu registry</li>
</ul>
<h2 id="相容性-audit">相容性 audit</h2>
<p>Pre-cutover 必跑：</p>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>處理方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Terraform version pin（<code>required_version = &quot;&gt;= 1.5.0, &lt; 1.6.0&quot;</code>）</td>
          <td>改 <code>&gt;= 1.6.0</code> 涵蓋 OpenTofu / 移除 upper bound</td>
      </tr>
      <tr>
          <td>Provider 來源 (registry path)</td>
          <td>主流 provider（aws / azurerm / gcp / k8s）都同源、自家 / 第三方 provider 確認 OpenTofu registry mirror</td>
      </tr>
      <tr>
          <td>Terraform Cloud / Enterprise feature</td>
          <td>Sentinel policy → OpenTofu OPA / Conftest；workspace API 對等性逐項 check</td>
      </tr>
      <tr>
          <td>CLI binary name 在 CI pipeline</td>
          <td><code>terraform plan</code> → <code>tofu plan</code>、或 alias <code>terraform=tofu</code> 保留兼容</td>
      </tr>
      <tr>
          <td>State backend (S3 / GCS / Azure / Consul / Terraform Cloud)</td>
          <td>S3/GCS/Azure 完全相容；Consul backend 兩家都支援；Terraform Cloud 走自家 remote backend、不直通</td>
      </tr>
      <tr>
          <td>Module source</td>
          <td>git-based module 完全相容；registry module 確認 OpenTofu registry 有 mirror</td>
      </tr>
  </tbody>
</table>
<p>Audit output：列「100% drop-in」block + 「需處理」block；後者通常 &lt; 5% 範圍。</p>
<h2 id="step-by-step-cutover">Step-by-step cutover</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. Install OpenTofu (跨 OS)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">brew install opentofu                <span class="c1"># macOS</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">snap install --classic opentofu      <span class="c1"># Ubuntu</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># https://opentofu.org/docs/intro/install/</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 2. 在 workspace 跑 tofu init</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">$ <span class="nb">cd</span> terraform-workspace/
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">$ tofu init -upgrade
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 升級 provider / module、re-init backend、保留 state</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 3. Plan diff（應該 = 0 changes）</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">$ tofu plan
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># Plan: 0 to add, 0 to change, 0 to destroy.</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># 如果有 diff、表示 provider version 不對齊、檢查 lock file</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 4. Apply（保險起見、staging 先跑）</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">$ tofu apply
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># 5. CI / CD pipeline 切 binary</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"># Before</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">terraform init
</span></span><span class="line"><span class="ln">22</span><span class="cl">terraform plan -out<span class="o">=</span>tfplan
</span></span><span class="line"><span class="ln">23</span><span class="cl">terraform apply tfplan
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"># After</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">tofu init
</span></span><span class="line"><span class="ln">27</span><span class="cl">tofu plan -out<span class="o">=</span>tfplan
</span></span><span class="line"><span class="ln">28</span><span class="cl">tofu apply tfplan
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1"># 或保留 terraform 字面、用 alias / symlink</span></span></span></code></pre></div><p>整個 cutover 通常 &lt; 1 天（單 workspace）；多 workspace organization 視規模 1-4 週逐個切。</p>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1provider-version-driftstaging-plan-出現意外-diff">Case 1：Provider version drift、staging plan 出現意外 diff</h3>
<p><strong>徵兆</strong>：<code>tofu plan</code> 顯示 100+ resource 有 in-place update、實際業務沒改任何 config。</p>
<p><strong>根因</strong>：<code>.terraform.lock.hcl</code> 鎖的 provider version 在 Terraform / OpenTofu registry 不一致（同 version 但 binary checksum 微差）；OpenTofu 在 init 時拉新 checksum、視為「provider 變了」。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>預先對齊</strong>：<code>tofu init -upgrade</code> 重建 lock file、把 OpenTofu 端 checksum 寫進去</li>
<li><strong>CI lockfile commit</strong>：lock file 進版控、不同 binary 端跑前先 lockfile 對齊</li>
<li><strong>若 plan 仍有差異</strong>：通常是 provider 內部 schema 對 nil 值處理不同、用 <code>lifecycle.ignore_changes</code> 暫忽略、後續逐項 fix</li>
</ol>
<h3 id="case-2state-file-lock-機制微差">Case 2：State file lock 機制微差</h3>
<p><strong>徵兆</strong>：兩個 CI pipeline 同時跑 <code>tofu apply</code>、其中一個應該 lock 拒絕、實際兩個都跑、production 端 race condition。</p>
<p><strong>根因</strong>：Terraform DynamoDB lock 跟 OpenTofu lock 用相同 schema 但 lock_id 規則略不同；舊 lock entry 殘留時 OpenTofu 端解析失敗、視為「無 lock」繼續跑。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>DynamoDB lock table 手動清舊 entry</strong>：cutover 期間先 <code>aws dynamodb delete-item</code> 清舊 lock</li>
<li><strong>單向流量切換</strong>：cutover 期間 freeze 所有 CI、只一個 pipeline 跑、避免 race</li>
<li><strong>架構</strong>：用 <em>fully replicated lock backend</em>（如 Consul）avoid backend-specific lock 怪異</li>
</ol>
<h3 id="case-3terraform-cloud-workspace-不能直接搬">Case 3：Terraform Cloud workspace 不能直接搬</h3>
<p><strong>徵兆</strong>：team 已用 Terraform Cloud workspace 跑 100+ pipeline、想切 OpenTofu、發現 <code>terraform login</code> / workspace API / VCS integration 全 HashiCorp-specific。</p>
<p><strong>根因</strong>：OpenTofu 沒對等 Terraform Cloud 服務；自家 backend 用 S3 + Atlantis / Spacelift / env0 等第三方 platform 對接、不是 1:1 替代。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>保留 Terraform Cloud 跑 production</strong>（OpenTofu 不替代）、用 OpenTofu 跑 dev / sandbox</li>
<li><strong>遷出 Terraform Cloud</strong>：state 遷 S3 + 用 Atlantis 跑 PR-based plan/apply（mature open source）</li>
<li><strong>評估 Spacelift / env0</strong> 商業替代、支援 OpenTofu + 對等 workspace feature</li>
</ol>
<h3 id="case-4ci-pipeline-寫死-terraform-binary-name">Case 4：CI pipeline 寫死 <code>terraform</code> binary name</h3>
<p><strong>徵兆</strong>：cutover 後 CI 跑 <code>terraform plan</code> 報「command not found」；team 100+ pipeline / GitHub Action / GitLab CI / shell script 都寫死 <code>terraform</code>。</p>
<p><strong>根因</strong>：rollout 計畫沒 grep 全 organization 找 binary name 引用。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Alias 策略</strong>：CI image 內 <code>ln -s /usr/local/bin/tofu /usr/local/bin/terraform</code>、保留兼容 1-3 個月</li>
<li><strong>逐步改 <code>tofu</code></strong>：跟著 IaC team 修 pipeline file、target 100% 改完才 remove alias</li>
<li><strong>架構</strong>：避免在 pipeline / script 寫死 binary、用 env variable <code>IAC_BINARY=${IAC_BINARY:-tofu}</code></li>
</ol>
<h3 id="case-5registry-routing自家-module-拉不到">Case 5：Registry routing、自家 module 拉不到</h3>
<p><strong>徵兆</strong>：cutover 後 <code>tofu init</code> 對自家 private module 報「not found」；同 module 在 Terraform 端跑得好好的。</p>
<p><strong>根因</strong>：private module 註冊在 <em>Terraform Cloud private registry</em>、OpenTofu 預設不知道這個 endpoint；需要顯式設 registry source URL。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>顯式 source URL</strong>：<code>source = &quot;app.terraform.io/myorg/myapp/aws&quot;</code> 改 git source 或自架 module registry</li>
<li><strong>架構</strong>：用 git-based module source（<code>source = &quot;git::ssh://git@github.com/myorg/myapp.git&quot;</code>）、避開 registry lock-in</li>
<li><strong>長期</strong>：自家 module 同時 publish 到 OpenTofu registry / Terraform Cloud / git、跨 tool 兼容</li>
</ol>
<h2 id="capacity--cost">Capacity / cost</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Terraform</th>
          <th>OpenTofu</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Binary cost</td>
          <td>免費 (community edition)</td>
          <td>免費（永遠）</td>
      </tr>
      <tr>
          <td>Terraform Cloud cost</td>
          <td>$20 / user / month、enterprise 高</td>
          <td>無對等服務（用 Atlantis / Spacelift / env0）</td>
      </tr>
      <tr>
          <td>State storage</td>
          <td>S3 / 自家 backend、低</td>
          <td>S3 / 自家 backend、低</td>
      </tr>
      <tr>
          <td>Migration cost</td>
          <td>-</td>
          <td>1-5 person-day（含 audit + cutover + CI 改）</td>
      </tr>
      <tr>
          <td>License risk</td>
          <td>BSL 限制商業使用</td>
          <td>MPL 2.0 開源、無 license risk</td>
      </tr>
      <tr>
          <td>Long-term governance</td>
          <td>HashiCorp 單一供應商</td>
          <td>Linux Foundation + 多廠商貢獻</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：純 IaC 用戶切 OpenTofu 風險低 + 省 license 風險；重度依賴 Terraform Cloud feature 的 organization 保留或評估 commercial alternatives（Spacelift / env0）。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-atlantis--spacelift--env0-整合">跟 <a href="https://www.runatlantis.io/">Atlantis / Spacelift / env0</a> 整合</h3>
<p>OpenTofu 沒對等 Terraform Cloud、需要 third-party orchestrator：</p>
<ul>
<li><strong>Atlantis</strong>：自架、開源、輕量、適合中小型 team</li>
<li><strong>Spacelift</strong>：SaaS、policy as code、支援 OpenTofu first-class</li>
<li><strong>env0</strong>：SaaS、cost estimation、workflow 完整</li>
</ul>
<h3 id="跟-terragrunt-整合">跟 <a href="https://terragrunt.gruntwork.io/">Terragrunt</a> 整合</h3>
<p>Terragrunt（OpenTofu / Terraform 共用 wrapper）已支援 OpenTofu 1.6+；多環境配置抽象保留、底層 binary 切換無感。</p>
<h3 id="反向-migrationopentofu--terraform">反向 migration（OpenTofu → Terraform）</h3>
<p>罕見、通常是 organization 走商業合約綁 HashiCorp Enterprise 才會做；流程鏡像對稱、注意 OpenTofu 1.6+ 自家 feature（state encryption / provider for_each）在 Terraform 端可能缺。</p>
<h3 id="下一步議題">下一步議題</h3>
<ul>
<li><strong>State encryption（OpenTofu 1.7+）</strong>：sensitive state 加密、Terraform 商業版才有對等 feature</li>
<li><strong>跨 IaC tool（Pulumi / CDK）</strong>：Pulumi / AWS CDK 是不同 paradigm（imperative）、不在本 migration scope</li>
<li><strong>Provider ecosystem 長期分裂</strong>：兩家 registry 自我演化、需要 quarterly review provider compat</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/05-deployment-platform/vendors/terraform/" data-link-title="Terraform / OpenTofu" data-link-desc="Infrastructure as Code 主流工具">Terraform</a></li>
<li>平行 migration playbook（Type B）：<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a></li>
<li>Methodology：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a> / <a href="/blog/report/content-structure-by-max-diff-dimension/" data-link-title="Process content 結構由最大差異維度決定、不是 universal phased" data-link-desc="跨 X process content（migration / upgrade / rollout / playbook）的結構由 source / target 之間 *差異維度組合* 決定、不存在 universal phased 模板；6 種 migration / process type 實證（schema 差 / drop-in / operational / multi-tool / paradigm / topology re-layout）跑出 6 種不同結構；寫作前必須做 *6 維 diff dimension audit* 才能決定結構、跳過會套錯模板">#127 Process content 結構由最大差異維度決定</a></li>
</ul>
]]></content:encoded></item><item><title>Redis → DragonflyDB：drop-in 相容下的容量躍升 + 5 個踩雷</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB&lt;/a>（target）。跟前一篇 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/splunk/migrate-to-elastic-security/" data-link-title="Splunk → Elastic Security Detection Rule Migration：6 段 phased playbook 跟 5 大踩雷" data-link-desc="從 Splunk Enterprise Security 遷到 Elastic Security 的 detection rule translation playbook：SPL ↔ KQL/ES|QL schema 對位、AI-assisted translation pipeline、parallel run 比對、cutover routing、5 個 production 踩雷（macro 沒對應 / time zone 差異 / summary index 不對位 / alert dedup key 衝突 / 過早 decommission）、capacity / cost 對照">Splunk → Elastic Security&lt;/a> 的 6-phase playbook 對照、Redis → DragonflyDB 是 &lt;em>drop-in 相容&lt;/em> 形態的 migration、結構更接近 &lt;a href="https://tarrragon.github.io/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">vendor deep article methodology&lt;/a> 的 6-section flow + 一段「相容性驗證」前置。&lt;/p>&lt;/blockquote>
&lt;h2 id="為什麼遷cost--single-thread--multi-tenancy-三條-driver">為什麼遷：cost / single-thread / multi-tenancy 三條 driver&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Driver&lt;/th>
 &lt;th>觸發場景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>Memory cost&lt;/strong>&lt;/td>
 &lt;td>Redis 6.x cluster 跑 1-10 TB 時、機器成本爆；DragonflyDB 記憶體效率提升 ~30%、相同 dataset 少 30% RAM&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Single-thread bottleneck&lt;/strong>&lt;/td>
 &lt;td>Redis 主執行緒在單一 hot key 寫入時是瓶頸、scale-up 受限；DragonflyDB 多執行緒 + shared-nothing 設計、單機 throughput 號稱 25x&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Multi-tenancy&lt;/strong>&lt;/td>
 &lt;td>Redis Cluster 多 namespace 需要 cluster-per-tenant、運維成本爆；DragonflyDB 設計上 namespace 隔離成本低&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>反向 driver（DragonflyDB → Redis）也存在 — 主要是 &lt;em>Redis Modules 依賴&lt;/em>（RedisJSON / RedisSearch / RedisGraph）DragonflyDB 不支援、或 &lt;em>Lua script 用了 redis.call 進階 API&lt;/em>。&lt;/p>
&lt;h2 id="跟-phased-migration-的對照drop-in-不需要-phased">跟 phased migration 的對照：drop-in 不需要 phased&lt;/h2>
&lt;p>跟前一篇 Splunk → Elastic 的 6-phase playbook 不同、Redis → DragonflyDB 的 migration &lt;em>結構接近 standard deep article&lt;/em>：&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a>（target）。跟前一篇 <a href="/blog/backend/07-security-data-protection/vendors/splunk/migrate-to-elastic-security/" data-link-title="Splunk → Elastic Security Detection Rule Migration：6 段 phased playbook 跟 5 大踩雷" data-link-desc="從 Splunk Enterprise Security 遷到 Elastic Security 的 detection rule translation playbook：SPL ↔ KQL/ES|QL schema 對位、AI-assisted translation pipeline、parallel run 比對、cutover routing、5 個 production 踩雷（macro 沒對應 / time zone 差異 / summary index 不對位 / alert dedup key 衝突 / 過早 decommission）、capacity / cost 對照">Splunk → Elastic Security</a> 的 6-phase playbook 對照、Redis → DragonflyDB 是 <em>drop-in 相容</em> 形態的 migration、結構更接近 <a href="/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">vendor deep article methodology</a> 的 6-section flow + 一段「相容性驗證」前置。</p></blockquote>
<h2 id="為什麼遷cost--single-thread--multi-tenancy-三條-driver">為什麼遷：cost / single-thread / multi-tenancy 三條 driver</h2>
<table>
  <thead>
      <tr>
          <th>Driver</th>
          <th>觸發場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Memory cost</strong></td>
          <td>Redis 6.x cluster 跑 1-10 TB 時、機器成本爆；DragonflyDB 記憶體效率提升 ~30%、相同 dataset 少 30% RAM</td>
      </tr>
      <tr>
          <td><strong>Single-thread bottleneck</strong></td>
          <td>Redis 主執行緒在單一 hot key 寫入時是瓶頸、scale-up 受限；DragonflyDB 多執行緒 + shared-nothing 設計、單機 throughput 號稱 25x</td>
      </tr>
      <tr>
          <td><strong>Multi-tenancy</strong></td>
          <td>Redis Cluster 多 namespace 需要 cluster-per-tenant、運維成本爆；DragonflyDB 設計上 namespace 隔離成本低</td>
      </tr>
  </tbody>
</table>
<p>反向 driver（DragonflyDB → Redis）也存在 — 主要是 <em>Redis Modules 依賴</em>（RedisJSON / RedisSearch / RedisGraph）DragonflyDB 不支援、或 <em>Lua script 用了 redis.call 進階 API</em>。</p>
<h2 id="跟-phased-migration-的對照drop-in-不需要-phased">跟 phased migration 的對照：drop-in 不需要 phased</h2>
<p>跟前一篇 Splunk → Elastic 的 6-phase playbook 不同、Redis → DragonflyDB 的 migration <em>結構接近 standard deep article</em>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Splunk → Elastic（phased）</th>
          <th>Redis → DragonflyDB（drop-in）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema 對位</td>
          <td>需要（SPL ↔ KQL / CIM ↔ ECS）</td>
          <td>不需要（RESP protocol 相容）</td>
      </tr>
      <tr>
          <td>Rule translation</td>
          <td>4-12 週 SOC engineering 工作</td>
          <td>不需要（command 直接相容）</td>
      </tr>
      <tr>
          <td>Parallel run</td>
          <td>4-8 週 dual-SIEM 跑</td>
          <td>1-7 天 dual-write 觀察</td>
      </tr>
      <tr>
          <td>Cutover 邊界</td>
          <td>軟邊界（routing 切換、可逆 30 分鐘）</td>
          <td>硬邊界（client 配置切換、單次完成）</td>
      </tr>
      <tr>
          <td>不可逆 cleanup</td>
          <td>1 年後 archive</td>
          <td>立刻（DragonflyDB 接管後 Redis 可關）</td>
      </tr>
      <tr>
          <td>整體週期</td>
          <td>4-9 個月</td>
          <td>1-4 週</td>
      </tr>
  </tbody>
</table>
<p><strong>判斷依據</strong>：migration 結構由 <em>source 跟 target 的 schema / protocol 差異程度</em> 決定、不是 universal phased playbook。本批第 2 篇驗證 <em>deep article methodology 的 6-section 框架</em> 在 drop-in migration 仍適用（只需前置 <em>相容性驗證</em> 段、其他 6 段對位）。</p>
<h2 id="相容性驗證在-cutover-前要確認的清單">相容性驗證：在 cutover 前要確認的清單</h2>
<p>DragonflyDB 號稱 Redis drop-in、但「drop-in」涵蓋範圍依 Redis feature 使用程度而定。Pre-migration 必跑的相容性 audit：</p>
<table>
  <thead>
      <tr>
          <th>Redis feature</th>
          <th>DragonflyDB 支援程度</th>
          <th>Action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Basic data types (String / Hash / List / Set / ZSet)</td>
          <td>完全相容</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>RESP protocol v2 / v3</td>
          <td>完全相容</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>RDB load</td>
          <td>Redis 6.x RDB 完全相容；7.x 部分 feature 待測</td>
          <td>用 BGSAVE → 切換 → load 驗證</td>
      </tr>
      <tr>
          <td>AOF</td>
          <td>DragonflyDB 不用 AOF、改 <em>snapshotting</em> 模式</td>
          <td>不直接 import AOF、需經 RDB 中介</td>
      </tr>
      <tr>
          <td>Lua scripts</td>
          <td>90% 相容、部分 redis.call API + EVAL 邊界 case 差異</td>
          <td>Lua script audit 必跑、不能假設全相容</td>
      </tr>
      <tr>
          <td>Pub/Sub</td>
          <td>相容、但 message fanout 行為差異（多 thread 處理）</td>
          <td>高 fanout pub/sub 場景需測 latency</td>
      </tr>
      <tr>
          <td>Cluster mode</td>
          <td>DragonflyDB <em>單機</em> 即可達 cluster throughput、不必 cluster；emulated cluster mode 部分相容</td>
          <td>評估是否仍需 cluster</td>
      </tr>
      <tr>
          <td>Sentinel HA</td>
          <td>不直接支援、用 DragonflyDB 自家 replication</td>
          <td>HA 架構重設計</td>
      </tr>
      <tr>
          <td>Redis Modules (RedisJSON / Search / Graph)</td>
          <td><strong>不支援</strong></td>
          <td>必須前置改寫 application</td>
      </tr>
      <tr>
          <td>Streams</td>
          <td>相容、但 consumer group 行為部分差異</td>
          <td>Stream consumer 跑 dual-write 觀察</td>
      </tr>
      <tr>
          <td>Keyspace notifications</td>
          <td>相容</td>
          <td>無需處理</td>
      </tr>
  </tbody>
</table>
<p><strong>Audit 的關鍵 output</strong>：列「不相容功能」清單 + 對應 application code 修改範圍；若 Modules 在 production 使用、migration <em>退役</em>。</p>
<h2 id="step-by-step-cutover">Step-by-step cutover</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 部署 DragonflyDB</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">docker run -d --name dragonfly -p 6380:6379 <span class="se">\
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="se"></span>  -v /data/dragonfly:/data <span class="se">\
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="se"></span>  docker.dragonflydb.io/dragonflydb/dragonfly:latest <span class="se">\
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="se"></span>  --logtostderr --requirepass<span class="o">=</span>&lt;your_password&gt;
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 2. Redis 端 BGSAVE</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">redis-cli -h redis-primary BGSAVE
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 等到 BGSAVE 完成</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">redis-cli -h redis-primary INFO Persistence <span class="p">|</span> grep rdb_last_save_time
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 3. 把 dump.rdb 拷到 DragonflyDB</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">scp redis-primary:/var/lib/redis/dump.rdb dragonfly-host:/data/dragonfly/
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># 4. 重啟 DragonflyDB 載入 RDB</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">docker restart dragonfly
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># 5. 驗證資料一致</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">redis-cli -h dragonfly-host -p <span class="m">6380</span> DBSIZE
</span></span><span class="line"><span class="ln">20</span><span class="cl">redis-cli -h redis-primary DBSIZE
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># 兩端 key 數對齊</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"># 6. Dual-write 1-7 天（application 同時寫兩端）</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"># 7. Read 切換到 DragonflyDB、Redis 端只寫不讀</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"># 8. Write 切換、Redis 端 standby</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"># 9. 觀察 1-2 週、無異常後 Redis decommission</span></span></span></code></pre></div><p>關鍵時間點：</p>
<ul>
<li><strong>BGSAVE → load</strong>：100GB RDB 約 5-15 分鐘、跨網路 SCP 時間另算</li>
<li><strong>Dual-write window</strong>：1-7 天觀察、application 寫兩端、read 仍走 Redis</li>
<li><strong>Cutover</strong>：read switch → write switch、每步間隔 24 小時</li>
<li><strong>Decom</strong>：Redis 保留 standby 1-2 週、無異常後關閉</li>
</ul>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1rdb-版本差dragonflydb-load-失敗">Case 1：RDB 版本差，DragonflyDB load 失敗</h3>
<p><strong>徵兆</strong>：Redis 7.2 端 BGSAVE 出的 <code>dump.rdb</code> 在 DragonflyDB load 時報 <code>Unsupported RDB version</code>、DragonflyDB 啟動失敗。</p>
<p><strong>根因</strong>：Redis 7.2 RDB version 11 含新 feature（function library / sharded pubsub）DragonflyDB 當前 release 沒支援；版本相容性需逐 release 確認。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Pre-migration 版本相容矩陣 audit</strong>：DragonflyDB release note 對照 Redis version、確認 RDB version 支援</li>
<li><strong>降級 BGSAVE</strong>：Redis 端設 <code>rdb-version 9</code>（Redis 6.x 兼容版本）、犧牲 Redis 7.x 新 feature</li>
<li><strong>替代方案</strong>：用 <code>redis-cli --scan</code> + <code>MIGRATE</code> 命令 incremental 搬、不用 RDB；速度慢 100x 但相容性好</li>
</ol>
<h3 id="case-2lua-script-跑進-eval-不一致">Case 2：Lua script 跑進 EVAL 不一致</h3>
<p><strong>徵兆</strong>：dual-write 階段、發現某些 EVAL script 在 Redis 跟 DragonflyDB 結果不同；具體是某個 <code>redis.call(&quot;OBJECT&quot;, &quot;ENCODING&quot;, key)</code> 在 DragonflyDB 回不一樣的 encoding 字串。</p>
<p><strong>根因</strong>：DragonflyDB 內部不用 Redis 的 ziplist / listpack encoding（dashtable 不需要）、<code>OBJECT ENCODING</code> 返回值不對等；script 邏輯依賴 encoding 來決定行為、結果不同。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Audit Lua script</strong>：grep 所有 <code>redis.call(&quot;OBJECT&quot;</code>、列出依賴 encoding 的 script</li>
<li><strong>改寫 application</strong>：不依賴 encoding、改用 <code>MEMORY USAGE</code> 或 high-level check</li>
<li><strong>接受差異</strong>：DragonflyDB 不會回 encoding 但 functional 結果對等、SOC review 確認可接受</li>
</ol>
<h3 id="case-3pubsub-fanout-高負載-latency">Case 3：Pub/Sub fanout 高負載 latency</h3>
<p><strong>徵兆</strong>：production 切到 DragonflyDB 後、Pub/Sub 訂閱端 latency p99 從 5ms 漲到 20-50ms；topic fanout &gt;10K subscriber 場景。</p>
<p><strong>根因</strong>：DragonflyDB 多 thread 設計、Pub/Sub message 在 thread 間 dispatch 需要 routing；Redis single-thread 沒這個 overhead。高 fanout 是 DragonflyDB 設計取捨。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>架構</strong>：高 fanout Pub/Sub 不用 DragonflyDB、改 <a href="/blog/backend/03-message-queue/vendors/nats/" data-link-title="NATS" data-link-desc="Lightweight messaging、JetStream 加持久化與 streams">NATS</a> / Redis Streams + consumer group</li>
<li><strong>DragonflyDB 配置調整</strong>：<code>--proactor_threads</code> 對 Pub/Sub 影響大、調到符合 CPU 核心數</li>
<li><strong>接受 latency</strong>：&lt; 10K subscriber 差異可忽略、不必動</li>
</ol>
<h3 id="case-4cluster-mode-看似相容但-slot-routing-行為差">Case 4：Cluster mode 看似相容但 slot routing 行為差</h3>
<p><strong>徵兆</strong>：application 用 Redis Cluster client（lettuce / Jedis cluster mode）連 DragonflyDB emulated cluster、運行幾天後 <code>MOVED</code> redirect 異常、key 找不到。</p>
<p><strong>根因</strong>：DragonflyDB emulated cluster mode 是 <em>single node 模擬</em>、CLUSTER SLOTS 返回固定 mapping；某些 client 端 cluster topology cache 跟實際 routing 不對齊、發 redirect。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Application 改 standalone client</strong>：DragonflyDB single node 已能達 cluster 級 throughput、不必用 cluster client</li>
<li><strong>Client config</strong>：lettuce 端 <code>clusterTopologyRefreshOptions(...)</code> 設較長 refresh、減少 redirect 機會</li>
<li><strong>長期</strong>：等 DragonflyDB cluster 正式 GA 後再評估</li>
</ol>
<h3 id="case-5modules-用了沒注意migration-卡住">Case 5：Modules 用了沒注意，migration 卡住</h3>
<p><strong>徵兆</strong>：cutover 後幾天、application 某個功能完全壞、log 顯示 <code>ERR unknown command 'JSON.SET'</code>；DragonflyDB 不支援 RedisJSON。</p>
<p><strong>根因</strong>：Pre-migration audit 漏掉 application 用了 RedisJSON（透過某 client library 抽象）；DragonflyDB 不支援該 Module 命令、application 直接壞。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Pre-migration audit 必跑</strong>：<code>MONITOR</code> 抓 1 小時 production traffic、grep 非 standard command（<code>JSON.*</code> / <code>FT.*</code> / <code>GRAPH.*</code>）</li>
<li><strong>應急回退</strong>：Redis standby 還在、application client config 切回</li>
<li><strong>長期</strong>：JSON 改用 standard Hash + serialization、Search 改 Elasticsearch / Meilisearch、Graph 改 Neo4j</li>
</ol>
<h2 id="capacity--cost-對照">Capacity / cost 對照</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Redis（self-managed）</th>
          <th>DragonflyDB</th>
          <th>取捨</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Single-node throughput</td>
          <td>~100K-200K ops/s</td>
          <td>~2-5M ops/s（號稱 25x）</td>
          <td>DragonflyDB 領先、實測依 workload 而定</td>
      </tr>
      <tr>
          <td>Memory efficiency</td>
          <td>baseline</td>
          <td>-30% 平均、依資料分佈</td>
          <td>DragonflyDB 領先</td>
      </tr>
      <tr>
          <td>Persistence</td>
          <td>RDB / AOF 雙模式</td>
          <td>Snapshotting 為主、不用 AOF</td>
          <td>Redis 對 durability 要求高的 workload 仍領先</td>
      </tr>
      <tr>
          <td>HA / Replication</td>
          <td>Sentinel + Cluster 成熟</td>
          <td>自家 replication、HA 文件相對少</td>
          <td>Redis 領先</td>
      </tr>
      <tr>
          <td>Modules ecosystem</td>
          <td>RedisJSON / Search / Graph / TimeSeries</td>
          <td>不支援</td>
          <td>Redis 領先</td>
      </tr>
      <tr>
          <td>Cluster scaling</td>
          <td>Cluster mode 成熟</td>
          <td>單機效能高、cluster 仍 emerging</td>
          <td>Redis 領先、但 DragonflyDB 單機已能 cover 多數 use case</td>
      </tr>
      <tr>
          <td>Total cost (10TB cache)</td>
          <td>$8-15K USD / month</td>
          <td>$2-5K USD / month</td>
          <td>DragonflyDB 顯著便宜</td>
      </tr>
      <tr>
          <td>Operational maturity</td>
          <td>高（10+ 年 production）</td>
          <td>中（2022+、production 案例 1000+）</td>
          <td>Redis 領先</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：cache use case 簡單（pure cache / session store）走 DragonflyDB；複雜 use case（Modules / Pub/Sub fanout / strict durability）保留 Redis。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-client-library-整合">跟 client library 整合</h3>
<p>主流 Redis client（lettuce / Jedis / redis-py / node-redis / go-redis）都直接相容 DragonflyDB；唯一例外是 <em>cluster client</em> 模式行為差（見 Case 4）。</p>
<h3 id="跟-monitoring-整合">跟 monitoring 整合</h3>
<p>DragonflyDB exporter 提供 Prometheus metric、跟 Redis exporter 對應 metric 名稱 80% 相同；grafana dashboard 需小改：</p>
<ul>
<li><code>redis_memory_used_bytes</code> → <code>dragonfly_memory_used_bytes</code></li>
<li><code>redis_commands_processed_total</code> → <code>dragonfly_commands_processed_total</code></li>
</ul>
<h3 id="跟-redis-sentinel-ha-對位">跟 <a href="/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">Redis Sentinel HA</a> 對位</h3>
<p>DragonflyDB 不直接支援 Sentinel、HA 走自家 <em>master-replica</em> + DNS-based failover：</p>
<ol>
<li>DragonflyDB primary + replica</li>
<li>K8s 用 StatefulSet + Service + readiness probe</li>
<li>失敗 failover 比 Sentinel 慢（30s-2min vs 5-15s）</li>
</ol>
<h3 id="下一步議題">下一步議題</h3>
<ul>
<li><strong>DragonflyDB Cluster GA</strong>：正式 cluster mode 出來後重評估</li>
<li><strong>Stream + consumer group 細節</strong>：dual-write 期間驗證每個 consumer pattern</li>
<li><strong>Modules 替代方案</strong>：JSON / Search / Graph 各自的 cloud-native 替代評估</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a></li>
<li>平行 migration playbook：<a href="/blog/backend/07-security-data-protection/vendors/splunk/migrate-to-elastic-security/" data-link-title="Splunk → Elastic Security Detection Rule Migration：6 段 phased playbook 跟 5 大踩雷" data-link-desc="從 Splunk Enterprise Security 遷到 Elastic Security 的 detection rule translation playbook：SPL ↔ KQL/ES|QL schema 對位、AI-assisted translation pipeline、parallel run 比對、cutover routing、5 個 production 踩雷（macro 沒對應 / time zone 差異 / summary index 不對位 / alert dedup key 衝突 / 過早 decommission）、capacity / cost 對照">Splunk → Elastic Security</a></li>
<li>Methodology：<a href="/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">Vendor 深度技術文章的寫作方法論</a></li>
</ul>
]]></content:encoded></item><item><title>Atlassian Statuspage → Instatus：status page 成本下降、但 compatibility audit 不能跳</title><link>https://tarrragon.github.io/blog/backend/08-incident-response/vendors/atlassian-statuspage/migrate-to-instatus/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/08-incident-response/vendors/atlassian-statuspage/migrate-to-instatus/</guid><description>&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>Atlassian Statuspage（Business / Enterprise）&lt;/th>
 &lt;th>Instatus（Pro / Business）&lt;/th>
 &lt;th>差距判讀&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>月費&lt;/td>
 &lt;td>Business 約 $399/mo、Enterprise 約 $1,499/mo 起&lt;/td>
 &lt;td>Pro 約 $20/mo、Business 約 $300/mo&lt;/td>
 &lt;td>savings 取決於 target tier&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Custom domain + SSL&lt;/td>
 &lt;td>內建&lt;/td>
 &lt;td>Free tier 起就含&lt;/td>
 &lt;td>持平&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Subscriber 上限&lt;/td>
 &lt;td>依 tier 提升&lt;/td>
 &lt;td>Pro 約 5,000 subscriber、Business 約 25,000 subscriber&lt;/td>
 &lt;td>需對齊現有 subscriber 數&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Component 上限&lt;/td>
 &lt;td>依 tier 提升&lt;/td>
 &lt;td>Pro 有上限、Business 放寬&lt;/td>
 &lt;td>大型 page 要逐項確認&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Notification channel&lt;/td>
 &lt;td>Email / SMS / Slack / Teams / webhook / RSS / Atom&lt;/td>
 &lt;td>Email / SMS / Slack / Discord / Teams / Telegram / RSS / Webhook&lt;/td>
 &lt;td>Instatus 多 chat channel&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metrics 圖表&lt;/td>
 &lt;td>Datadog / Pingdom / New Relic / Library&lt;/td>
 &lt;td>Datadog / Pingdom / New Relic / StatusCake / API&lt;/td>
 &lt;td>payload / auth 要重接&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SAML SSO&lt;/td>
 &lt;td>Enterprise tier&lt;/td>
 &lt;td>Business tier&lt;/td>
 &lt;td>不是產品缺口、是 tier 差異&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Audit / activity log&lt;/td>
 &lt;td>Enterprise / team governance 能力&lt;/td>
 &lt;td>需依 plan 確認&lt;/td>
 &lt;td>強合規要逐項驗證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SLA / uptime report&lt;/td>
 &lt;td>內建能力較成熟&lt;/td>
 &lt;td>需確認 plan 或外接&lt;/td>
 &lt;td>contract deliverable 要驗證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>API parity&lt;/td>
 &lt;td>完整 REST&lt;/td>
 &lt;td>REST API&lt;/td>
 &lt;td>endpoint / schema 不同&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>成本差距是這條 migration 的 &lt;em>driver&lt;/em>、但表格右側的 tier 差異是 &lt;em>blocker candidate&lt;/em>。對 &lt;em>不需要 Enterprise governance / 強 SLA reporting / 深 Atlassian 整合&lt;/em> 的中小 SaaS、從 Statuspage Business / Enterprise 降到 Instatus Pro / Business 可以有明顯 savings、cutover 工作量通常落在 1-4 週；對 &lt;em>enterprise 強合規&lt;/em> 的場景、SSO、audit、reporting 與可用性承諾任一不能讓步時、migration 要先停在 compatibility audit。&lt;/p></description><content:encoded><![CDATA[<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>Atlassian Statuspage（Business / Enterprise）</th>
          <th>Instatus（Pro / Business）</th>
          <th>差距判讀</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>月費</td>
          <td>Business 約 $399/mo、Enterprise 約 $1,499/mo 起</td>
          <td>Pro 約 $20/mo、Business 約 $300/mo</td>
          <td>savings 取決於 target tier</td>
      </tr>
      <tr>
          <td>Custom domain + SSL</td>
          <td>內建</td>
          <td>Free tier 起就含</td>
          <td>持平</td>
      </tr>
      <tr>
          <td>Subscriber 上限</td>
          <td>依 tier 提升</td>
          <td>Pro 約 5,000 subscriber、Business 約 25,000 subscriber</td>
          <td>需對齊現有 subscriber 數</td>
      </tr>
      <tr>
          <td>Component 上限</td>
          <td>依 tier 提升</td>
          <td>Pro 有上限、Business 放寬</td>
          <td>大型 page 要逐項確認</td>
      </tr>
      <tr>
          <td>Notification channel</td>
          <td>Email / SMS / Slack / Teams / webhook / RSS / Atom</td>
          <td>Email / SMS / Slack / Discord / Teams / Telegram / RSS / Webhook</td>
          <td>Instatus 多 chat channel</td>
      </tr>
      <tr>
          <td>Metrics 圖表</td>
          <td>Datadog / Pingdom / New Relic / Library</td>
          <td>Datadog / Pingdom / New Relic / StatusCake / API</td>
          <td>payload / auth 要重接</td>
      </tr>
      <tr>
          <td>SAML SSO</td>
          <td>Enterprise tier</td>
          <td>Business tier</td>
          <td>不是產品缺口、是 tier 差異</td>
      </tr>
      <tr>
          <td>Audit / activity log</td>
          <td>Enterprise / team governance 能力</td>
          <td>需依 plan 確認</td>
          <td>強合規要逐項驗證</td>
      </tr>
      <tr>
          <td>SLA / uptime report</td>
          <td>內建能力較成熟</td>
          <td>需確認 plan 或外接</td>
          <td>contract deliverable 要驗證</td>
      </tr>
      <tr>
          <td>API parity</td>
          <td>完整 REST</td>
          <td>REST API</td>
          <td>endpoint / schema 不同</td>
      </tr>
  </tbody>
</table>
<p>成本差距是這條 migration 的 <em>driver</em>、但表格右側的 tier 差異是 <em>blocker candidate</em>。對 <em>不需要 Enterprise governance / 強 SLA reporting / 深 Atlassian 整合</em> 的中小 SaaS、從 Statuspage Business / Enterprise 降到 Instatus Pro / Business 可以有明顯 savings、cutover 工作量通常落在 1-4 週；對 <em>enterprise 強合規</em> 的場景、SSO、audit、reporting 與可用性承諾任一不能讓步時、migration 要先停在 compatibility audit。</p>
<p>這篇是 Type B drop-in migration playbook、結構順序是：先跑 <em>compatibility audit</em>（確認 gap 都可接受）→ 再進 cutover。Type B 看起來簡單、但跳過 audit 直接切是這 batch 第三常見的事故來源。</p>
<h2 id="為什麼是-type-b全-low">為什麼是 Type B（全 Low）</h2>
<p>跑 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/#6-%e7%b6%ad-diff-dimension-audit" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">6 維 diff dimension audit</a>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema</td>
          <td>Low</td>
          <td>component / incident / subscriber model 接近一致、欄位名稱 1:1</td>
      </tr>
      <tr>
          <td>Operational</td>
          <td>Low</td>
          <td>都是 public status page + notification、ops 模型相同</td>
      </tr>
      <tr>
          <td>Paradigm</td>
          <td>Low</td>
          <td>同 paradigm（public service status disclosure）</td>
      </tr>
      <tr>
          <td>Components</td>
          <td>Low</td>
          <td>都是單一 SaaS</td>
      </tr>
      <tr>
          <td>App change</td>
          <td>Low</td>
          <td>API 端點換、payload 接近一致</td>
      </tr>
      <tr>
          <td>Topology</td>
          <td>Low</td>
          <td>都是 cloud SaaS</td>
      </tr>
  </tbody>
</table>
<p>全 Low → <strong>Type B drop-in + compatibility audit prefix</strong>。</p>
<h2 id="compatibility-audit-prefix">Compatibility audit prefix</h2>
<p>切換前先跑 audit、確認以下 9 項 <em>對自己的 case 是否可接受</em>。任一項是 <em>no</em>、回頭評估是否真要遷：</p>
<h3 id="1-subscriber-channel-完整度">1. Subscriber channel 完整度</h3>
<p>Statuspage 主要 channel：Email、SMS、Slack、Microsoft Teams、Webhook、RSS、Atom。Instatus 多了 Discord 跟 Telegram、少了 Atom（RSS 仍在）。</p>
<ul>
<li>確認現有 subscriber 用的 channel 都在 Instatus 支援列表</li>
<li>特別注意 <em>legacy RSS Atom feed reader</em> — 有些 monitoring service 用 Atom 訂閱、要改成 RSS 或 webhook</li>
</ul>
<h3 id="2-saml-sso">2. SAML SSO</h3>
<p>SAML SSO 是 <em>tier decision</em>、不是單純產品有無。Statuspage 把 SAML 放在較高 tier；Instatus 也在 Business tier 提供 SAML。真正要判斷的是：成本 savings 是否仍成立、以及 IdP / SCIM / role mapping 是否符合 audit 要求。</p>
<ul>
<li>確認 target Instatus plan 是否包含 SAML</li>
<li>確認 IdP / group / role mapping 是否能對上現有 audit requirement</li>
<li>如果 savings 只在 Pro tier 成立、但 compliance 要 SAML，就不能用 Pro tier 當 ROI 基準</li>
</ul>
<h3 id="3-audit-log">3. Audit log</h3>
<p>Audit log 是 governance surface。誰 publish 哪則 incident、誰改了哪個 component status、誰匯入 subscriber，這些事件在 Statuspage Enterprise / Instatus Business 類 plan 的支援深度與匯出能力要逐項比對。</p>
<ul>
<li>確認 status page 變更是否需要 internal audit trail</li>
<li>確認 target plan 是否能查詢、匯出與保留 admin activity</li>
<li>金融 / 醫療場景要把 audit retention 與 evidence export 放進 go/no-go gate</li>
</ul>
<h3 id="4-sla--uptime-report-自動產出">4. SLA / uptime report 自動產出</h3>
<p>SLA / uptime report 是 customer contract surface。Statuspage 的 enterprise workflow 通常更成熟；Instatus 是否能直接覆蓋，要看 plan、API 與既有客戶報表格式。</p>
<ul>
<li>如果 contract 寫了「每月 SLA report 自動推送客戶」、Instatus 要外接補這條</li>
<li>評估外接成本（一條 cron + 一個 BI dashboard、3-5 天工程）vs Statuspage 內建</li>
</ul>
<h3 id="5-可用性承諾與-provider-outage">5. 可用性承諾與 provider outage</h3>
<p>Status page provider 本身的可用性承諾是 compatibility audit 的一部分。強合規或大型 customer-facing page 要確認 provider SLA、status page provider 自身 outage 時的 fallback、以及是否需要獨立備援頁。</p>
<ul>
<li>多數場景能接受 status page provider 跟自己 service 不同供應商已經足夠</li>
<li>強合規 + 「status page must never be down」場景要設獨立 fallback，而不是只比較 UI 功能</li>
</ul>
<h3 id="6-metrics-integration-來源">6. Metrics integration 來源</h3>
<p>兩家都接 Datadog / Pingdom / New Relic / StatusCake / Library API。Instatus 多了 StatusCake、少了某些 Statuspage 內建 library。</p>
<ul>
<li>確認當前 metrics 顯示圖表的 source 在 Instatus 支援列表</li>
<li>特別注意 <em>custom metrics from API</em>（自家 push 上去的）— 兩家都支援、payload 格式不同、要重寫 push script</li>
</ul>
<h3 id="7-custom-css--branding-完整度">7. Custom CSS / branding 完整度</h3>
<p>Statuspage Enterprise 允許 <em>完整 custom CSS override</em>、Instatus Pro / Team 允許 <em>theme customization</em>（颜色 / logo / font）但 <em>不允許任意 CSS injection</em>。</p>
<ul>
<li>如果有大量 custom CSS 跟既有品牌 site 視覺 1:1 對齊、Instatus 可能達不到、要評估視覺退讓</li>
<li>大多數 status page 視覺 ≠ 主 product site、退讓常見</li>
</ul>
<h3 id="8-api-parity-跟自動化-hook">8. API parity 跟自動化 hook</h3>
<p>兩家都有完整 REST API（create incident、update component status、push subscriber）。但 <em>endpoint URL / auth scheme / payload schema 不同</em>：</p>
<ul>
<li>Statuspage：<code>https://api.statuspage.io/v1/pages/{page_id}/...</code>、OAuth bearer token</li>
<li>Instatus：<code>https://api.instatus.com/v1/{page_id}/...</code>、API key header</li>
</ul>
<p>如果有 <em>從 IR 平台（incident.io / Rootly / FireHydrant / 自製 webhook）push status update</em> 的自動化、要重寫對接、估算 2-5 天工程。</p>
<h3 id="9-atlassian-生態整合opsgenie--jsm--confluence">9. Atlassian 生態整合（Opsgenie / JSM / Confluence）</h3>
<p>Statuspage 跟 Opsgenie / JSM / Confluence 同生態、有原生整合（Opsgenie incident → Statuspage incident draft、Confluence post-mortem auto-link）。Instatus 跟 Atlassian 沒原生整合、要走 webhook。</p>
<ul>
<li>如果 Atlassian 整合是核心 workflow、評估走 webhook 工作量</li>
<li>如果是 incident.io / Rootly / FireHydrant 主用、Instatus 反而有原生整合（這條變優勢）</li>
</ul>
<h2 id="cutover-階段">Cutover 階段</h2>
<p>Audit 全過後、Type B drop-in 不需要 11-phase 結構、4 階段：</p>
<h3 id="stage-1setup--parallel-run1-週">Stage 1：Setup + parallel run（1 週）</h3>
<ul>
<li>在 Instatus 開帳號、設 component（先複製 Statuspage 結構 1:1）</li>
<li>設 custom domain + SSL（Instatus 預設 free tier 已含）</li>
<li>接 subscriber channels（先不切 DNS、純內部測試）</li>
<li>用 Instatus API 從 Statuspage export incident history 灌回 Instatus（保留歷史 uptime 連續性）</li>
<li>Parallel run：當前若有 incident、在 Statuspage 跟 Instatus 兩邊都 push、確認 subscriber 在兩邊都收到、UI 都正常</li>
</ul>
<h3 id="stage-2dns-預備1-天">Stage 2：DNS 預備（1 天）</h3>
<ul>
<li>Statuspage custom domain CNAME / ALIAS 預設 TTL 通常 1 小時、提前 48 小時把 TTL 降到 5 分鐘</li>
<li>這步是 minimize cutover window 的關鍵、不做的話 cutover 期間有 1 小時 DNS cache 兩邊 page 不同步</li>
</ul>
<h3 id="stage-3dns-cutover30-分鐘---1-小時">Stage 3：DNS cutover（30 分鐘 - 1 小時）</h3>
<ul>
<li>把 status page custom domain 從 Statuspage CNAME 改指 Instatus CNAME</li>
<li>5 分鐘 TTL 後新流量都進 Instatus</li>
<li>監控 1 小時、確認 subscriber notification 從 Instatus 發出、metrics 圖表 wire 正確、history uptime continuity 沒斷</li>
<li>既有 IR 平台 webhook 改指 Instatus API endpoint</li>
</ul>
<h3 id="stage-4statuspage-關閉2-4-週後">Stage 4：Statuspage 關閉（2-4 週後）</h3>
<ul>
<li>不要立即取消 Statuspage 帳號 — 留 2-4 週作 rollback 緩衝</li>
<li>Subscriber 通知「status page URL 不變、underlying provider 換了」（多數場景不需要、subscriber 不會察覺）</li>
<li>確認 incident history / uptime data 在 Instatus 完整、Statuspage rollback 場景 &lt; 0.5% 後、取消 Statuspage subscription</li>
</ul>
<p>完成標準：DNS 100% 流量在 Instatus、Statuspage subscription 取消、SRE / SaaS provisioning team 不再 maintain Statuspage account。</p>
<h2 id="5-個-production-踩雷">5 個 production 踩雷</h2>
<h3 id="1-sso-tier-選錯導致-admin-login-退化">1. SSO tier 選錯導致 admin login 退化</h3>
<p>audit 漏掉 <em>當前 admin 用 SAML 登入</em> 這個事實、卻用不含 SAML 的 target tier 計算 savings，cutover 後 admin login 被迫退回 email/password + 2FA。修法是 Stage 1 就用含 SAML 的 target plan 測試 IdP、group mapping 與 break-glass admin。對 SOC 2 audit 期間 <em>admin login method 變更要記錄</em>的 org 來說，這是不可預期的 audit finding、要在 Stage 1 就溝通。</p>
<h3 id="2-metrics-圖表來源整合斷">2. Metrics 圖表來源整合斷</h3>
<p>Statuspage 接 Datadog metrics 的 OAuth integration 在 Instatus 要重接、auth flow 重做、Datadog API key 重 provision。常見漏網之魚：</p>
<ul>
<li>跨 region Datadog account（US / EU）integration 重 provision 時 region 沒選對、圖表全空</li>
<li>Pingdom check ID 在新 integration 重新 register、historic data 斷層</li>
<li>自家 push metrics 的 webhook payload schema 不同（Statuspage 是 <code>{component_id, status, ...}</code>、Instatus 是 <code>{componentId, status, ...}</code> camelCase）</li>
</ul>
<p>修法是 Stage 1 parallel run 期間就把所有 metrics integration 在 Instatus wire 通、對比兩邊圖表一致再進 Stage 2。</p>
<h3 id="3-subscriber-import-format-不一致">3. Subscriber import format 不一致</h3>
<p>Statuspage subscriber export CSV 是 <code>email, phone, slack_webhook_url, ...</code> 一行多 channel；Instatus import CSV 是 <code>email\nemail\n...</code> 純 email list、其他 channel 要分開 import。如果有 5000 subscriber 包含 SMS / Slack mix、import 時要拆開、否則 SMS subscriber 會掉。</p>
<p>修法是寫 import script 把 Statuspage CSV 拆成多個 channel-specific CSV、分批 import Instatus。</p>
<h3 id="4-sla-report-月報突然斷">4. SLA report 月報突然斷</h3>
<p>Statuspage 月報自動 push 給客戶、cutover 後 Instatus 沒原生 SLA report、客戶下個月沒收到報表會問。修法是 <em>cutover 前先建外接 SLA report</em>：</p>
<ul>
<li>寫 cron job（per month）從 Instatus API 拉 component uptime data</li>
<li>用簡單 template（Google Doc / PDF generator）產 report</li>
<li>自動 email 推給原 Statuspage SLA report distribution list</li>
</ul>
<p>如果這條 contract 強制、外接成本約 3-5 天工程、要算進 migration 總成本。</p>
<h3 id="5-custom-css--branding-視覺退讓">5. Custom CSS / branding 視覺退讓</h3>
<p>Statuspage Enterprise 有大量 custom CSS、cutover 後 Instatus 視覺對齊不到 1:1。視覺退讓清單通常是：</p>
<ul>
<li>font weight 跟 line-height 微差</li>
<li>mobile breakpoint 不同</li>
<li>incident timeline 排版 spacing 略不同</li>
</ul>
<p>修法是 cutover 前先在 Instatus theme customization 內把能調的調好、能接受的退讓在 Stage 1 跟設計 / brand team 確認、不能接受的就回去 audit Step 7 重新評估是否要遷。</p>
<h2 id="容量與成本對比">容量與成本對比</h2>
<p>對中小 SaaS（3000 subscriber、10 component、月均 2 incident）：</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>Statuspage Business</th>
          <th>Instatus Pro</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>月費</td>
          <td>約 $399</td>
          <td>約 $20</td>
      </tr>
      <tr>
          <td>Subscriber 上限</td>
          <td>依 plan</td>
          <td>約 5,000</td>
      </tr>
      <tr>
          <td>Component</td>
          <td>依 plan</td>
          <td>有上限</td>
      </tr>
      <tr>
          <td>工程成本（cutover）</td>
          <td>-</td>
          <td>1-4 週</td>
      </tr>
      <tr>
          <td>外接 SLA report</td>
          <td>不需要或較成熟</td>
          <td>0-5 天 / 持續維運</td>
      </tr>
      <tr>
          <td>年化 saving</td>
          <td>-</td>
          <td>約數千美元等級</td>
      </tr>
  </tbody>
</table>
<p>對 enterprise（30000 subscriber、50+ component、強合規）：</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>Statuspage Enterprise</th>
          <th>Instatus Business / Enterprise</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>月費</td>
          <td>約 $1,499 起或 custom</td>
          <td>低於典型 Enterprise quote</td>
      </tr>
      <tr>
          <td>SAML / Audit log</td>
          <td>必要</td>
          <td>需逐項驗證</td>
      </tr>
      <tr>
          <td>SLA / uptime report</td>
          <td>必要</td>
          <td>需逐項驗證或外接</td>
      </tr>
      <tr>
          <td>結論</td>
          <td>未必適合遷</td>
          <td>先跑 audit、不要只看月費</td>
      </tr>
  </tbody>
</table>
<h2 id="何時不要切">何時不要切</h2>
<ul>
<li><strong>SAML SSO + audit log 是 compliance requirement</strong>：金融 / 醫療 / 政府場景、Statuspage Enterprise 留</li>
<li><strong>SLA report 是 customer contract 強制</strong>：如果 contract 寫明 SLA report deliverable、外接成本 + 風險高、Statuspage 留</li>
<li><strong>Provider availability / fallback 必要</strong>：status page provider 自身 outage 時仍要可訪、先設獨立 fallback 或保留 Enterprise 級 provider</li>
<li><strong>Atlassian 整合（Opsgenie / JSM / Confluence）是核心 workflow</strong>：原生整合斷會多很多 webhook 維護、Statuspage 留</li>
<li><strong>subscriber &gt; 10K + 強客戶 SLA</strong>：規模本身讓 Instatus 風險增大、Statuspage Enterprise 比較穩</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>平行 batch：<a href="/blog/backend/08-incident-response/vendors/pagerduty/migrate-to-incident-io/" data-link-title="PagerDuty → incident.io：「On-call」是個 retconned word、同名不同 contract" data-link-desc="PagerDuty → incident.io 不是 schema translation — 兩家的「on-call」字面相同、contract 不同（alert routing vs IR coordination &#43; Slack-native &#43; retrospective）。本文走 Type E paradigm shift、6 維 audit 顯示 paradigm / schema / operational 三軸 High、用 4-phase partial migration（不收斂、Phase 1-2 多數 org 停留）、5 個 production 踩雷（雙系統 state drift / severity 翻譯失真 / schedule layer 漏 / Slack channel 過載 / retrospective 斷層）、跟 PagerDuty Process Automation / AIOps 沒對應的 capability gap">PagerDuty → incident.io</a>（Type E paradigm shift）/ <a href="/blog/backend/08-incident-response/vendors/opsgenie/migrate-from-pagerduty/" data-link-title="PagerDuty → Opsgenie：Atlassian 全家桶整合 vs Opsgenie 2027 EOL 的 vendor consolidation 取捨" data-link-desc="PagerDuty → Opsgenie 是 Type A phased schema translation、但 Atlassian 已宣布 Opsgenie 2027-04 EOL — 這條 migration 只在 Atlassian-heavy org &#43; 明確 JSM unification roadmap 下成立、本質是 PD → Opsgenie → JSM Cloud 的雙 hop migration。本文走 6 維 audit（Schema Medium-High 其他 Low）、PagerDuty ↔ Opsgenie ↔ JSM field mapping 對照、5 production 踩雷（escalation step / Heartbeat 缺對應 / integration key dedup 重設 / schedule 時區 / Atlassian Identity SSO 整合）、何時直接走 PD → JSM 跳過 Opsgenie">PagerDuty → Opsgenie</a>（Type A schema translation）</li>
<li>同 batch Type B：（待補、本篇是 batch 唯一 Type B）</li>
<li>vendor 對照：<a href="/blog/backend/08-incident-response/vendors/atlassian-statuspage/" data-link-title="Atlassian Statuspage" data-link-desc="公開狀態頁 SaaS、Atlassian 出品、enterprise polish &#43; Atlassian 生態整合、subscriber notification &#43; component dependency 是核心責任">Atlassian Statuspage</a> / <a href="/blog/backend/08-incident-response/vendors/instatus/" data-link-title="Instatus" data-link-desc="輕量 status page SaaS、現代 UI、價格敏感替代">Instatus</a></li>
<li>方法論：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration Playbook Methodology</a>（Type B drop-in + compatibility audit prefix 結構說明）</li>
</ul>
]]></content:encoded></item></channel></rss>