<?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>Session-Token on Tarragon</title><link>https://tarrragon.github.io/blog/tags/session-token/</link><description>Recent content in Session-Token on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 27 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/session-token/index.xml" rel="self" type="application/rss+xml"/><item><title>Cosmos DB 5 Consistency Levels：Session 預設、Bounded staleness、Strong 邊界跟跨 collection 分流策略</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/cosmosdb/consistency-levels-engineering/</link><pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/cosmosdb/consistency-levels-engineering/</guid><description>&lt;p>Cosmos DB 文件列 &lt;em>5 個 consistency level&lt;/em>（Strong / Bounded staleness / Session / Consistent prefix / Eventual）、用 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/pacelc/" data-link-title="PACELC" data-link-desc="在 CAP 之外補上正常時段的延遲與一致性取捨框架">PACELC&lt;/a> 講概念、但沒給具體工程判準。team 啟動 Cosmos DB 第一個要決定的就是 account 預設 level、再決定哪些 query 要 per-request override。本文先講 5 個 level 的精確語義、再進 Session 為什麼是 production 預設、再進「同一 application 內不同操作選不同 level」的進階策略；&lt;em>Strong + multi-region write 互斥&lt;/em>議題 cross-link 到 &lt;a href="../multi-region-write-conflict/">multi-region-write-conflict&lt;/a>、本篇不展開。&lt;/p>
&lt;p>本文不是 Cosmos DB overview（請看 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/cosmosdb/" data-link-title="Azure Cosmos DB" data-link-desc="全球分散式 multi-model DB、5 個 consistency levels、Microsoft 自家 dogfood 證據">Cosmos DB vendor 頁&lt;/a>）— 而是 &lt;em>consistency level 工程選擇邏輯&lt;/em> 的深度展開。Case anchor 是 &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/minecraft-earth-cosmos-db-global/" data-link-title="9.C11 Minecraft Earth：Azure Cosmos DB 上的全球分散式 AR 遊戲" data-link-desc="Minecraft Earth 用 Cosmos DB 跨地區分散、測試到 100 萬 RU/s 仍維持承諾延遲">9.C11 Minecraft Earth&lt;/a>（用 session consistency 撐 AR 全球同步、5 level 跨 collection 分流）+ &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/asos-cosmos-db-black-friday/" data-link-title="9.C21 ASOS：Cosmos DB 在 Black Friday 撐 1.67 億請求" data-link-desc="ASOS 在 2016 Black Friday 用 Azure Cosmos DB 撐 24 小時 1.67 億請求、3500 req/sec、48ms 平均延遲">9.C21 ASOS&lt;/a>（Black Friday 用較弱 consistency 換 throughput）。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Cosmos DB workload 適配判讀（四層 framing）&lt;/strong>：API model 三型遷移路徑 / RU 思維轉換成本 / multi-model 差異化是否真用上 / 跨雲 hedging vs 單雲 lock-in — 判讀軸詳見 &lt;a href="../mongodb-api-vs-sql-api/#%e5%9b%9b%e5%b1%a4-framingvendor-selection-%e7%9a%84%e7%9c%9f%e5%af%a6%e6%b1%ba%e7%ad%96%e8%bb%b8">mongodb-api-vs-sql-api 開頭四層 framing&lt;/a>。本文聚焦 consistency level 選擇操作層、是 &lt;em>已選 Cosmos DB 後&lt;/em> 的 read / write 語義決策；若 workload 不適用 Cosmos DB、level 選擇無法救回 vendor 選錯的取捨。&lt;/p>&lt;/blockquote>
&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>典型觸發場景：team 啟動 Cosmos DB account、setup wizard 問「預設 consistency level」 — 5 個選項、文件講概念、不知道實際業務該選哪個。production 上線後使用者反映「加入購物車後立刻看『我的購物車』讀到舊狀態」、「跨 region 看到玩家瞬移回舊位置」 — debug 發現是 consistency level 沒選對。&lt;/p></description><content:encoded><![CDATA[<p>Cosmos DB 文件列 <em>5 個 consistency level</em>（Strong / Bounded staleness / Session / Consistent prefix / Eventual）、用 <a href="/blog/backend/knowledge-cards/pacelc/" data-link-title="PACELC" data-link-desc="在 CAP 之外補上正常時段的延遲與一致性取捨框架">PACELC</a> 講概念、但沒給具體工程判準。team 啟動 Cosmos DB 第一個要決定的就是 account 預設 level、再決定哪些 query 要 per-request override。本文先講 5 個 level 的精確語義、再進 Session 為什麼是 production 預設、再進「同一 application 內不同操作選不同 level」的進階策略；<em>Strong + multi-region write 互斥</em>議題 cross-link 到 <a href="../multi-region-write-conflict/">multi-region-write-conflict</a>、本篇不展開。</p>
<p>本文不是 Cosmos DB overview（請看 <a href="/blog/backend/01-database/vendors/cosmosdb/" data-link-title="Azure Cosmos DB" data-link-desc="全球分散式 multi-model DB、5 個 consistency levels、Microsoft 自家 dogfood 證據">Cosmos DB vendor 頁</a>）— 而是 <em>consistency level 工程選擇邏輯</em> 的深度展開。Case anchor 是 <a href="/blog/backend/09-performance-capacity/cases/minecraft-earth-cosmos-db-global/" data-link-title="9.C11 Minecraft Earth：Azure Cosmos DB 上的全球分散式 AR 遊戲" data-link-desc="Minecraft Earth 用 Cosmos DB 跨地區分散、測試到 100 萬 RU/s 仍維持承諾延遲">9.C11 Minecraft Earth</a>（用 session consistency 撐 AR 全球同步、5 level 跨 collection 分流）+ <a href="/blog/backend/09-performance-capacity/cases/asos-cosmos-db-black-friday/" data-link-title="9.C21 ASOS：Cosmos DB 在 Black Friday 撐 1.67 億請求" data-link-desc="ASOS 在 2016 Black Friday 用 Azure Cosmos DB 撐 24 小時 1.67 億請求、3500 req/sec、48ms 平均延遲">9.C21 ASOS</a>（Black Friday 用較弱 consistency 換 throughput）。</p>
<blockquote>
<p><strong>Cosmos DB workload 適配判讀（四層 framing）</strong>：API model 三型遷移路徑 / RU 思維轉換成本 / multi-model 差異化是否真用上 / 跨雲 hedging vs 單雲 lock-in — 判讀軸詳見 <a href="../mongodb-api-vs-sql-api/#%e5%9b%9b%e5%b1%a4-framingvendor-selection-%e7%9a%84%e7%9c%9f%e5%af%a6%e6%b1%ba%e7%ad%96%e8%bb%b8">mongodb-api-vs-sql-api 開頭四層 framing</a>。本文聚焦 consistency level 選擇操作層、是 <em>已選 Cosmos DB 後</em> 的 read / write 語義決策；若 workload 不適用 Cosmos DB、level 選擇無法救回 vendor 選錯的取捨。</p></blockquote>
<h2 id="問題情境">問題情境</h2>
<p>典型觸發場景：team 啟動 Cosmos DB account、setup wizard 問「預設 consistency level」 — 5 個選項、文件講概念、不知道實際業務該選哪個。production 上線後使用者反映「加入購物車後立刻看『我的購物車』讀到舊狀態」、「跨 region 看到玩家瞬移回舊位置」 — debug 發現是 consistency level 沒選對。</p>
<p>讀者徵兆：</p>
<ul>
<li>「Session 跟 Eventual 看起來差不多、為什麼 Session 是預設」</li>
<li>「Bounded staleness 的 K 跟 T 該設多少」</li>
<li>「Strong 在 multi-region account 為什麼有額外限制」</li>
<li>「跨 region read 拿到舊版本、是 consistency 設錯還是 partition key 問題」</li>
</ul>
<p>真實壓力：</p>
<ul>
<li>購物車場景：加入購物車後立刻看「我的購物車」、結果讀到舊狀態（user 體驗破洞）</li>
<li>遊戲場景：玩家位置同步、跨 region 看到「玩家瞬移」回舊位置（遊戲體驗 bug）</li>
<li>金融場景：跨服務寫入後立即 read confirm、看不到剛寫的 — 業務邏輯誤判「沒寫進去」、重試 / rollback</li>
</ul>
<p>consistency level 選錯不是 config 問題、是 <em>影響 user-facing 行為</em> 的 selection 決策、必須在 selection 階段釐清。</p>
<h2 id="核心機制5-個-level-的精確語義">核心機制：5 個 level 的精確語義</h2>
<h3 id="strong">Strong</h3>
<ul>
<li>機制：read 拿到最新 commit、提供 linearizable read</li>
<li>限制：<em>single-write region 限制</em>；multi-region write 不可同時用 Strong（時間敏感 claim、查 <a href="https://learn.microsoft.com/azure/cosmos-db/consistency-levels">最新文件</a>）；跨 region 配 Strong 還要付 <a href="/blog/backend/knowledge-cards/cross-region-quorum/" data-link-title="Cross-Region Quorum" data-link-desc="multi-region distributed SQL 強制 voting replica 跨 region、commit 等多 region quorum ack、跨洲 RTT 物理硬限">Cross-Region Quorum</a> 的物理 latency tax（跨洲 100-200ms）</li>
<li>適合：金融交易、庫存扣減、status 機器寫後 read confirm</li>
<li>為什麼互斥：詳見 <a href="../multi-region-write-conflict/">multi-region-write-conflict</a> 的 AP 取捨段、本篇不展開</li>
</ul>
<h3 id="bounded-staleness">Bounded staleness</h3>
<ul>
<li>機制：read 落後 <em>不超過 K 個 version 或 T 秒</em>（取較嚴格者）；單 region 內 linearizable、跨 region 有 bounded lag、跟 <a href="/blog/backend/knowledge-cards/freshness-token/" data-link-title="Freshness Token" data-link-desc="DB write 後返回的版本 token、後續 read 帶 token、保證 read 看到的資料 ≥ token 版本、解 DB &#43; cache 跨層 read-after-write">Freshness Token</a> 是兩種「跨層 read-after-write」協議的選擇（前者 vendor 內建、後者 application-level）</li>
<li>設定：K（version 上限）+ T（時間上限）兩個參數</li>
<li>適合：multi-region 但需要「有 bound 的 staleness 保證」、如 trading system 跨 region read with SLA</li>
</ul>
<h3 id="session預設最常用">Session（預設、最常用）</h3>
<ul>
<li>機制：同一 session token 內讀寫一致；session 之外 eventual</li>
<li>適合：<em>多數互動式產品的甜蜜點</em> — 使用者寫入後自己立刻讀得到、其他 session 可接受 eventual</li>
<li>為什麼是預設：cost 接近 eventual（不像 Strong 多 2x RU）、體驗接近 Strong（自己讀寫一致）— 是 trade-off 的甜蜜點</li>
</ul>
<h3 id="consistent-prefix">Consistent prefix</h3>
<ul>
<li>機制：read 不會看到亂序的寫入（看到 A→B→C、不會看到 A→C→B）、但可能落後</li>
<li>適合：時序敏感但可 stale 的場景（如新聞 feed 不能跳序、但可以晚幾秒）</li>
<li>風險：常被誤用為 Session 替代、跨 session 一樣 stale、但比 Eventual 多保證 <em>順序</em></li>
</ul>
<h3 id="eventual">Eventual</h3>
<ul>
<li>機制：最便宜、無順序保證</li>
<li>適合：完全可 stale + 不需順序的場景（分析、log 聚合、推薦系統）</li>
</ul>
<h3 id="跟-cosmos-db-account--container-的關係">跟 Cosmos DB account / container 的關係</h3>
<ul>
<li>account 預設一個 level</li>
<li>單一 request 可以 <em>降級</em>（讀更弱 level）、<em>不可升級</em>（讀更強）</li>
<li>container 層 <em>無法獨立設定 consistency level</em>（時間敏感、查最新文件）— 分流靠 <em>collection 切分</em> + <em>per-request override</em></li>
</ul>
<h3 id="ru-成本差異">RU 成本差異</h3>
<ul>
<li>Strong / Bounded read ≈ 2x Session / Eventual 的 <a href="/blog/backend/knowledge-cards/request-unit/" data-link-title="Request Unit" data-link-desc="Cosmos DB 的容量抽象單位、1 RU = 1KB document strong-consistent read 的 CPU &#43; memory &#43; IOPS 綜合 cost、寫 ~5 RU、複雜 query 數百 RU">Request Unit</a></li>
<li>write 成本不直接受 read level 影響、但 multi-region replication 開銷會（每多一個 region、寫成本 ×N）</li>
<li>selection 階段要把 consistency level 當「RU 倍數」進入容量公式、見 <a href="../ru-cost-model-sizing/">ru-cost-model-sizing</a></li>
</ul>
<h3 id="跟通用-consistency-卡片的對應">跟通用 consistency 卡片的對應</h3>
<p>Cosmos DB 是 <em>少數把 5 level 都商品化</em> 的服務、其他系統通常只給 2-3 級（MongoDB read concern majority / local / linearizable、DynamoDB strong / eventual）。對應 <a href="/blog/backend/knowledge-cards/consistency-level/" data-link-title="Consistency Level" data-link-desc="資料系統對讀寫一致性語意的可選擇層級">consistency-level</a> 卡片的概念分層。</p>
<p>跟 <a href="/blog/backend/knowledge-cards/linearizability/" data-link-title="Linearizability" data-link-desc="每次操作看起來都在單一全域順序中即時生效的一致性語意">linearizability</a> 的關係：Cosmos DB Strong = single-region linearizable、<em>不是</em> 跨 region external consistency（跟 Spanner 的 TrueTime + Paxos 不同）。這個區別是 selection 階段的常見誤判 — 別把 Cosmos DB Strong 當成 Spanner 替代品。</p>
<p>對應 knowledge cards：<a href="/blog/backend/knowledge-cards/consistency-level/" data-link-title="Consistency Level" data-link-desc="資料系統對讀寫一致性語意的可選擇層級">consistency-level</a> / <a href="/blog/backend/knowledge-cards/linearizability/" data-link-title="Linearizability" data-link-desc="每次操作看起來都在單一全域順序中即時生效的一致性語意">linearizability</a> / <a href="/blog/backend/knowledge-cards/stale-read/" data-link-title="Stale Read" data-link-desc="讀取到落後於最新寫入版本的舊資料">stale-read</a>。</p>
<h2 id="進階設計策略同一-application-內不同操作選不同-level">進階設計策略：同一 application 內不同操作選不同 level</h2>
<p>9.C11 Minecraft Earth 案例的平台特性段揭露「一致性是 spectrum、不是 binary」 — AR 遊戲玩家位置稍 stale OK（用 session / eventual）、庫存交易需要 strong；<em>同一 application 內不同 collection / container 配不同 consistency 是進階策略</em>、不一定是 account 一刀切。</p>
<p>container 層無法獨立設定 consistency level（時間敏感、查最新文件）、所以分流靠：</p>
<ul>
<li><strong>Collection / container 切分</strong>：高一致需求的資料放獨立 account、預設 Strong；低一致需求放另一 account、預設 Session</li>
<li><strong>Per-request override</strong>：account 預設 Session、特定「寫入後立即讀」場景升 Bounded、批次分析降 Eventual；用 SDK 的 <code>RequestOptions.ConsistencyLevel</code></li>
</ul>
<h3 id="per-request-override-範例c-sdk">Per-request override 範例（C# SDK）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// account 預設 Session</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// 但這個 read 需要 Bounded staleness</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">container</span><span class="p">.</span><span class="n">ReadItemAsync</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">id</span><span class="p">:</span> <span class="s">&#34;item-123&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">partitionKey</span><span class="p">:</span> <span class="k">new</span> <span class="n">PartitionKey</span><span class="p">(</span><span class="s">&#34;user-456&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">requestOptions</span><span class="p">:</span> <span class="k">new</span> <span class="n">ItemRequestOptions</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="n">ConsistencyLevel</span> <span class="p">=</span> <span class="n">ConsistencyLevel</span><span class="p">.</span><span class="n">BoundedStaleness</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">});</span>
</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">// 批次分析、降到 Eventual 換成本</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="kt">var</span> <span class="n">queryOptions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">QueryRequestOptions</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">ConsistencyLevel</span> <span class="p">=</span> <span class="n">ConsistencyLevel</span><span class="p">.</span><span class="n">Eventual</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="kt">var</span> <span class="n">iterator</span> <span class="p">=</span> <span class="n">container</span><span class="p">.</span><span class="n">GetItemQueryIterator</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;(</span><span class="n">query</span><span class="p">,</span> <span class="n">requestOptions</span><span class="p">:</span> <span class="n">queryOptions</span><span class="p">);</span></span></span></code></pre></div><p>注意 <em>不可升級</em> 的限制：account 預設 Eventual、per-request 不能升 Strong（會 error）。要保留升級彈性、account 預設應該是 <em>最強需要的 level</em>、再 per-request 降級。</p>
<h3 id="跟-partition-key-design-的關係">跟 partition-key-design 的關係</h3>
<p>partition 失衡時即使設 Strong consistency 也看到 throttle、application 看到的是 <em>429 retry 後的高 latency</em>、不是 stale data — consistency level 跟 partition key 共同決定 <em>真實一致性體驗</em>。partition skew 把 Strong 的 SLA 拉到比 Session 還差、見 <a href="../partition-key-design/">partition-key-design</a> 的 latency budget 拆解段。</p>
<h2 id="操作流程">操作流程</h2>
<h3 id="account-層設定">account 層設定</h3>





<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"># Portal / ARM template / CLI</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">az cosmosdb update --name mycosmos --resource-group myrg <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --default-consistency-level Session</span></span></code></pre></div><p>切換 level 是即時生效、但 production 切換需要 audit 所有 client 的 session 邏輯（特別是 Strong → Session 的降級會讓「跨 session read 變 stale」）。</p>
<h3 id="request-層-override">Request 層 override</h3>
<p>SDK 傳 <code>RequestOptions.ConsistencyLevel</code>（C# / Java / Node SDK 行為一致）。注意 <em>只能降級</em>、升級會 reject。</p>
<h3 id="session-token-管理">Session token 管理</h3>
<p>每個 read response 帶 session token、client 下次 read 帶回去；跨 service 共享 token 需要顯式傳遞（不然每個 service 自己一個 session）。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 拿到 session token</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">container</span><span class="p">.</span><span class="n">ReadItemAsync</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;(</span><span class="n">id</span><span class="p">,</span> <span class="n">pk</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kt">var</span> <span class="n">sessionToken</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">Headers</span><span class="p">[</span><span class="s">&#34;x-ms-session-token&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// 跨 service 傳遞（如 HTTP header）</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">httpClient</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s">&#34;X-Cosmos-Session-Token&#34;</span><span class="p">,</span> <span class="n">sessionToken</span><span class="p">);</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 class="c1">// 下游 service 取得 token、用在 SDK request</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="kt">var</span> <span class="n">requestOptions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ItemRequestOptions</span> <span class="p">{</span> <span class="n">SessionToken</span> <span class="p">=</span> <span class="n">sessionToken</span> <span class="p">};</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="kt">var</span> <span class="n">downstreamResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="n">container</span><span class="p">.</span><span class="n">ReadItemAsync</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;(</span><span class="n">id</span><span class="p">,</span> <span class="n">pk</span><span class="p">,</span> <span class="n">requestOptions</span><span class="p">);</span></span></span></code></pre></div><h3 id="驗證-level-行為">驗證 level 行為</h3>
<p>寫入後立即 read 同 partition key、量 staleness window。用 Cosmos DB Diagnostic Log 看 request 的實際 consistency level；對照 SDK 設定確認沒被預設 override。</p>
<h3 id="rollback-boundary">Rollback boundary</h3>
<p>account 預設可改、但 production 切換 level 需要 audit 所有 client 的 session 邏輯；container 層無法獨立設定（時間敏感、查最新文件）。</p>
<h2 id="失敗模式">失敗模式</h2>
<h3 id="failure-1全用-strong-consistency">Failure 1：全用 Strong consistency</h3>
<p>互動式產品 Session 即足夠、用 Strong 浪費 2x RU + 限制 multi-region write、cost 暴漲且 multi-region 配置受限。徵兆是「RU consumption 明顯偏高、且 multi-region write 開不起來」 — 才發現預設選 Strong。</p>
<p>修：</p>
<ul>
<li>盤點業務需求、絕大多數讀寫場景 Session 就夠</li>
<li>把需要 Strong 的少數 collection 拆獨立 account、其他 default Session</li>
<li>計算 cost：Session vs Strong 在多數 workload 差距 1.5-2x、長期成本顯著</li>
</ul>
<h3 id="failure-2session-token-沒回傳">Failure 2：Session token 沒回傳</h3>
<p>read 後拿 token、下次 read 沒帶、實際變 Eventual；徵兆是「自己的寫立刻 read 看不到」、debug 才發現 SDK 設定漏。SDK 預設會自動管理 session token、但跨 service 傳遞時容易漏。</p>
<p>修：</p>
<ul>
<li>同一 service 內用 SDK 預設行為、不要關 session token cache</li>
<li>跨 service 通信時把 session token 隨 HTTP header 傳遞</li>
<li>或改 account 層 Bounded staleness（提供跨 session 的 K/T bound、不依賴 token）</li>
</ul>
<h3 id="failure-3跨-service-共享-session-假設">Failure 3：跨 service 共享 session 假設</h3>
<p>service A 寫、service B 讀、B 沒拿到 A 的 session token → 看不到 A 的寫。常見場景：order service 寫訂單、notification service 立刻 read 訂單寄通知 — notification 沒拿到 order 的 token、讀到舊狀態（或讀不到）。</p>
<p>修：</p>
<ul>
<li>service A 寫完、把 session token 進 message（Kafka event / HTTP response）傳給 B</li>
<li>B 用 token 做 read、保證讀到 A 的寫</li>
<li>或業務上接受 eventual、design notification 有 retry / reconcile 機制</li>
</ul>
<h3 id="failure-4bounded-staleness-設太鬆">Failure 4：Bounded staleness 設太鬆</h3>
<p>K = 100,000、T = 1 hour、實際等於 Eventual、team 以為自己有保護。bounded staleness 的 K/T 要對應業務 SLA、不是 vendor 預設值。</p>
<p>修：</p>
<ul>
<li>根據業務 read-after-write SLA 設 T（如「5 秒內必須讀到」設 T=5）</li>
<li>K 通常設成「peak QPS × T」的合理倍數</li>
<li>量測：production 觀察實際 staleness 分布、調整 K/T</li>
</ul>
<h3 id="failure-5multi-region-write-配-strong">Failure 5：multi-region write 配 Strong</h3>
<p>文件不允許 / 行為退化（時間敏感、查最新）— 必須改 Bounded / Session。這是 <em>AP 取捨的硬約束</em>、不是 config 問題；詳見 <a href="../multi-region-write-conflict/">multi-region-write-conflict</a> 的 AP 取捨段。</p>
<p>修：在 selection 階段就決定「要 active-active write 還是要 Strong」、不能事後補；要全球 linearizable 轉 Spanner / Aurora DSQL、要 active-active 接受 eventual / session / bounded。</p>
<h3 id="failure-6consistent-prefix-誤用">Failure 6：Consistent prefix 誤用</h3>
<p>把它當 Session 用、跨 session read 還是 stale、但比 Eventual 多一個順序保證；用錯地方等於浪費。常見誤判：「我要『順序對』、所以選 Consistent prefix」 — 但實際業務需求是「自己讀到自己寫的」、應該是 Session 而非 Consistent prefix。</p>
<p>修：</p>
<ul>
<li>Consistent prefix 適合 <em>時序敏感但可跨 session stale</em> 場景（新聞 feed、event log）</li>
<li>「自己讀到自己寫的」場景用 Session</li>
<li>跨 session 也要強一致用 Bounded / Strong</li>
</ul>
<h2 id="容量與觀測">容量與觀測</h2>
<ul>
<li>必看 metric：<code>NormalizedRUConsumption</code>、<code>TotalRequestUnits</code>、<code>ReplicationLatency</code>（跨 region lag）</li>
<li>Diagnostic Log：每個 request 的實際 consistency level、確認沒被預設 override</li>
<li>成本計算：Strong / Bounded read 算 2x RU；multi-region 開後寫入成本 × region 數；level 跟 region 數的 cost matrix 是規劃必算</li>
<li>回 <a href="/blog/backend/09-performance-capacity/capacity-planning/" data-link-title="9.6 容量規劃模型" data-link-desc="peak forecast、headroom budget、growth curve、autoscaling sizing">9.6 容量規劃模型</a>：consistency level 當「RU 倍數」進入容量公式</li>
<li>Alert：
<ul>
<li><code>ReplicationLatency</code> 突增（跨 region 同步異常）</li>
<li>Diagnostic log 偵測 Strong read 突增（成本失控）</li>
<li>跨 service session token 缺失導致 stale read 比例上升</li>
</ul>
</li>
</ul>
<h2 id="邊界與整合">邊界與整合</h2>
<ul>
<li>Sibling deep articles：<a href="../partition-key-design/">partition-key-design</a>（partition key 跟 consistency 共同決定真實一致性體驗）、<a href="../ru-cost-model-sizing/">ru-cost-model-sizing</a>（RU 倍數量化）、<a href="../multi-region-write-conflict/">multi-region-write-conflict</a>（multi-region 下 consistency 的特殊行為、Strong + multi-region 互斥的 SSoT 主寫位置）、<a href="../mongodb-api-vs-sql-api/">mongodb-api-vs-sql-api</a>（MongoDB read concern → Cosmos DB consistency level 對應）</li>
<li>跟 <a href="/blog/backend/01-database/vendors/spanner/" data-link-title="Google Cloud Spanner" data-link-desc="全球分散式 strong-consistency OLTP、TrueTime API、線性擴展到 10 億 req/sec">Spanner vendor</a> 對比：external consistency vs Cosmos DB Strong 不是同一個 thing</li>
<li>跟 <a href="/blog/backend/01-database/vendors/dynamodb/" data-link-title="DynamoDB" data-link-desc="AWS managed key-value、partition-based scaling、9000 萬 RPS sustained 實戰證據">DynamoDB vendor</a> 對比：DynamoDB 只 strong / eventual 兩級、Cosmos DB 5 級提供細粒度</li>
<li>跟 1.x 章節：<a href="/blog/backend/01-database/global-distributed-oltp/" data-link-title="1.11 全球分散式 OLTP" data-link-desc="Spanner / Aurora DSQL / Cosmos DB multi-region write / CockroachDB / TiDB 的全球一致性取捨">1.11 全球分散式 OLTP</a>（Cosmos DB 5 level 跟 Spanner external consistency 並陳）</li>
<li>Knowledge cards：<a href="/blog/backend/knowledge-cards/consistency-level/" data-link-title="Consistency Level" data-link-desc="資料系統對讀寫一致性語意的可選擇層級">consistency-level</a> / <a href="/blog/backend/knowledge-cards/linearizability/" data-link-title="Linearizability" data-link-desc="每次操作看起來都在單一全域順序中即時生效的一致性語意">linearizability</a> / <a href="/blog/backend/knowledge-cards/stale-read/" data-link-title="Stale Read" data-link-desc="讀取到落後於最新寫入版本的舊資料">stale-read</a></li>
<li>Anti-recommendation：別把 Cosmos DB Strong 跟 Spanner external consistency 等同視之；產品需要真正全球 linearizable transaction 時、Cosmos DB 不是替代品 — 轉 Spanner / Aurora DSQL</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li><a href="/blog/backend/01-database/vendors/cosmosdb/" data-link-title="Azure Cosmos DB" data-link-desc="全球分散式 multi-model DB、5 個 consistency levels、Microsoft 自家 dogfood 證據">Cosmos DB vendor overview</a> — 本文是該頁尾 5 consistency levels backlog 的深度展開</li>
<li><a href="/blog/backend/09-performance-capacity/cases/minecraft-earth-cosmos-db-global/" data-link-title="9.C11 Minecraft Earth：Azure Cosmos DB 上的全球分散式 AR 遊戲" data-link-desc="Minecraft Earth 用 Cosmos DB 跨地區分散、測試到 100 萬 RU/s 仍維持承諾延遲">9.C11 Minecraft Earth case</a> — session consistency + 跨 collection 分流主案例</li>
<li><a href="/blog/backend/09-performance-capacity/cases/asos-cosmos-db-black-friday/" data-link-title="9.C21 ASOS：Cosmos DB 在 Black Friday 撐 1.67 億請求" data-link-desc="ASOS 在 2016 Black Friday 用 Azure Cosmos DB 撐 24 小時 1.67 億請求、3500 req/sec、48ms 平均延遲">9.C21 ASOS case</a> — 高 throughput + 較弱 level 補充</li>
<li><a href="../multi-region-write-conflict/">multi-region-write-conflict</a> — Strong + multi-region 互斥的 SSoT 主寫位置</li>
<li><a href="/blog/backend/knowledge-cards/consistency-level/" data-link-title="Consistency Level" data-link-desc="資料系統對讀寫一致性語意的可選擇層級">Consistency Level 卡片</a> / <a href="/blog/backend/knowledge-cards/linearizability/" data-link-title="Linearizability" data-link-desc="每次操作看起來都在單一全域順序中即時生效的一致性語意">Linearizability 卡片</a> / <a href="/blog/backend/knowledge-cards/stale-read/" data-link-title="Stale Read" data-link-desc="讀取到落後於最新寫入版本的舊資料">Stale Read 卡片</a> — 概念基底</li>
<li>官方：<a href="https://learn.microsoft.com/azure/cosmos-db/consistency-levels">Cosmos DB consistency levels</a> / <a href="https://learn.microsoft.com/azure/cosmos-db/how-to-manage-consistency">Consistency level overrides</a></li>
</ul>
]]></content:encoded></item></channel></rss>