<?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>Slo on Tarragon</title><link>https://tarrragon.github.io/blog/tags/slo/</link><description>Recent content in Slo on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 23 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/slo/index.xml" rel="self" type="application/rss+xml"/><item><title>Sloth：SLO YAML 與 Multi-burn-rate Alert 生成</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/slo-yaml-and-multi-burn-rate-alert-generation/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/slo-yaml-and-multi-burn-rate-alert-generation/</guid><description>&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>SLO 從定義到 Prometheus 落地需要多層 rule。一個 SLO 對應 4 組 time window 的 recording rule（計算各窗口的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a>），再對應 fast burn 和 slow burn 兩組 alerting rule。手動維護這些 rule 容易出錯：window 參數不一致、新增 SLO 忘記補 alert、修改 SLI expression 只改了部分 rule。&lt;/p>
&lt;p>Sloth 的責任是把這個過程自動化。輸入一份 SLO YAML，產出一組完整的 Prometheus recording + alerting rules，讓 SLO 維護回到宣告式：改 YAML、重新生成、載入 Prometheus。&lt;/p>
&lt;h2 id="slo-yaml-設計">SLO YAML 設計&lt;/h2>
&lt;p>Sloth YAML 的核心結構是 &lt;code>version&lt;/code> → &lt;code>service&lt;/code> → &lt;code>slos[]&lt;/code>。每個 SLO 定義三件事：目標數字（objective）、量測方式（SLI）、告警等級（alerting）。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">prometheus/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">service&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">checkout-api&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">slos&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">availability&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">objective&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">99.9&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;checkout API 的請求成功率&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sli&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">events&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">error_query&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sum(rate(http_requests_total{service=&amp;#34;checkout&amp;#34;,code=~&amp;#34;5..&amp;#34;}[{{.window}}]))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">total_query&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sum(rate(http_requests_total{service=&amp;#34;checkout&amp;#34;}[{{.window}}]))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">alerting&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">CheckoutAvailability&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">page_alert&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">severity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">critical&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ticket_alert&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">severity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">warning&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>SLI 有兩種類型。events-based SLI 用 error/total ratio 定義，Sloth 自動把 &lt;code>{{.window}}&lt;/code> 參數代入各 recording rule 的 range vector。raw SLI 直接寫 PromQL expression 算 error ratio，適合非 request-based 的 SLO（如 data freshness、replication lag）。&lt;/p>
&lt;p>raw SLI 範例 — data freshness：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">data-freshness&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">objective&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">99.5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sli&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">raw&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">error_ratio_query&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="sd"> 1 - clamp_max(
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="sd"> replication_lag_seconds{service=&amp;#34;checkout-db&amp;#34;} / 60,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="sd"> 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="sd"> )&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>objective 數字的來源是 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 政策&lt;/a> — 先從使用者旅程定義服務承諾，再把承諾轉成 objective。Sloth 不負責決定 objective 該是多少，只負責把 objective 轉成可執行的 Prometheus rule。&lt;/p>
&lt;p>alerting 分 page（嚴重，觸發即時通知）和 ticket（一般，產生工單）。兩者的 burn rate 門檻不同：page 用 fast burn window，ticket 用 slow burn window。label 設計跟 Alertmanager routing 對齊 — &lt;code>severity: critical&lt;/code> 走 PagerDuty / Slack alert channel，&lt;code>severity: warning&lt;/code> 走 ticket system（Jira / Linear）。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題情境">問題情境</h2>
<p>SLO 從定義到 Prometheus 落地需要多層 rule。一個 SLO 對應 4 組 time window 的 recording rule（計算各窗口的 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a>），再對應 fast burn 和 slow burn 兩組 alerting rule。手動維護這些 rule 容易出錯：window 參數不一致、新增 SLO 忘記補 alert、修改 SLI expression 只改了部分 rule。</p>
<p>Sloth 的責任是把這個過程自動化。輸入一份 SLO YAML，產出一組完整的 Prometheus recording + alerting rules，讓 SLO 維護回到宣告式：改 YAML、重新生成、載入 Prometheus。</p>
<h2 id="slo-yaml-設計">SLO YAML 設計</h2>
<p>Sloth YAML 的核心結構是 <code>version</code> → <code>service</code> → <code>slos[]</code>。每個 SLO 定義三件事：目標數字（objective）、量測方式（SLI）、告警等級（alerting）。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus/v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">service</span><span class="p">:</span><span class="w"> </span><span class="l">checkout-api</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">slos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">availability</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">objective</span><span class="p">:</span><span class="w"> </span><span class="m">99.9</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;checkout API 的請求成功率&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">sli</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span><span class="nt">events</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="nt">error_query</span><span class="p">:</span><span class="w"> </span><span class="l">sum(rate(http_requests_total{service=&#34;checkout&#34;,code=~&#34;5..&#34;}[{{.window}}]))</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="nt">total_query</span><span class="p">:</span><span class="w"> </span><span class="l">sum(rate(http_requests_total{service=&#34;checkout&#34;}[{{.window}}]))</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">alerting</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">CheckoutAvailability</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="nt">page_alert</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">          </span><span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="l">critical</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span><span class="nt">ticket_alert</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">          </span><span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="l">warning</span></span></span></code></pre></div><p>SLI 有兩種類型。events-based SLI 用 error/total ratio 定義，Sloth 自動把 <code>{{.window}}</code> 參數代入各 recording rule 的 range vector。raw SLI 直接寫 PromQL expression 算 error ratio，適合非 request-based 的 SLO（如 data freshness、replication lag）。</p>
<p>raw SLI 範例 — data freshness：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data-freshness</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nt">objective</span><span class="p">:</span><span class="w"> </span><span class="m">99.5</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">sli</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">      </span><span class="nt">raw</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">        </span><span class="nt">error_ratio_query</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="sd">          1 - clamp_max(
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="sd">            replication_lag_seconds{service=&#34;checkout-db&#34;} / 60,
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="sd">            1
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="sd">          )</span></span></span></code></pre></div><p>objective 數字的來源是 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 政策</a> — 先從使用者旅程定義服務承諾，再把承諾轉成 objective。Sloth 不負責決定 objective 該是多少，只負責把 objective 轉成可執行的 Prometheus rule。</p>
<p>alerting 分 page（嚴重，觸發即時通知）和 ticket（一般，產生工單）。兩者的 burn rate 門檻不同：page 用 fast burn window，ticket 用 slow burn window。label 設計跟 Alertmanager routing 對齊 — <code>severity: critical</code> 走 PagerDuty / Slack alert channel，<code>severity: warning</code> 走 ticket system（Jira / Linear）。</p>
<h2 id="multi-window-multi-burn-rate-alert">Multi-window Multi-burn-rate Alert</h2>
<p>Sloth 預設產生 Google SRE 推薦的 4-window alert 結構。每個 SLO 生成以下 recording rules 和 alerting rules。</p>
<table>
  <thead>
      <tr>
          <th>Window 組合</th>
          <th>責任</th>
          <th>觸發行動</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>5m / 1h</td>
          <td>Fast burn 偵測</td>
          <td>短時間大量消耗 → page 通知</td>
      </tr>
      <tr>
          <td>30m / 6h</td>
          <td>Moderate burn 偵測</td>
          <td>中速消耗 → page 或 ticket</td>
      </tr>
      <tr>
          <td>2h / 1d</td>
          <td>Slow burn 偵測</td>
          <td>緩慢消耗 → ticket</td>
      </tr>
      <tr>
          <td>6h / 3d</td>
          <td>Very slow 偵測</td>
          <td>長期趨勢退化 → ticket 或 review</td>
      </tr>
  </tbody>
</table>
<p>fast burn alert 回答「error budget 是否正在被快速吃掉」。當 5 分鐘窗口的 burn rate 超過 14.4 倍（代表如果持續下去，1 小時會消耗完整個月的 budget），觸發 page。這個門檻的設計邏輯是：越短的窗口允許越高的 burn rate 容忍，因為短窗口的 false positive 率較高，需要搭配較長窗口的確認。</p>
<p>slow burn alert 回答「error budget 是否在不被注意的情況下被緩慢消耗」。6 小時窗口的 burn rate 超過 1 倍（代表月底會剛好用完 budget），觸發 ticket。slow burn 常被忽略，但它是高變更頻率服務最常見的可靠性退化模式 — 每次小回歸都不夠大到觸發 fast burn，累積到月底才發現 budget 已透支。</p>
<p>burn rate alert 跟 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO error budget 政策</a> 直接對應：fast burn → 凍結變更；slow burn → 提高驗證門檻；budget 健康 → 正常發版。</p>
<p>Sloth 產出的 recording rule 範例（5m window）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl">- <span class="nt">record</span><span class="p">:</span><span class="w"> </span><span class="l">slo:sli_error:ratio_rate5m</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">expr</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="sd">    sum(rate(http_requests_total{service=&#34;checkout&#34;,code=~&#34;5..&#34;}[5m]))
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="sd">    /
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="sd">    sum(rate(http_requests_total{service=&#34;checkout&#34;}[5m]))</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">sloth_service</span><span class="p">:</span><span class="w"> </span><span class="l">checkout-api</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w">    </span><span class="nt">sloth_slo</span><span class="p">:</span><span class="w"> </span><span class="l">availability</span></span></span></code></pre></div><p>對應的 alerting rule（fast burn）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl">- <span class="nt">alert</span><span class="p">:</span><span class="w"> </span><span class="l">CheckoutAvailabilityFastBurn</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">expr</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="sd">    slo:sli_error:ratio_rate5m{sloth_slo=&#34;availability&#34;} &gt; (14.4 * 0.001)
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="sd">    and
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="sd">    slo:sli_error:ratio_rate1h{sloth_slo=&#34;availability&#34;} &gt; (14.4 * 0.001)</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="l">critical</span></span></span></code></pre></div><p>fast burn alert 要求 5m 和 1h 兩個窗口同時超過門檻，短窗口防止 spike false positive、長窗口確認趨勢持續。</p>
<h2 id="實作流程">實作流程</h2>
<h3 id="cli-生成">CLI 生成</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">sloth generate -i slo.yaml -o rules.yaml
</span></span><span class="line"><span class="ln">2</span><span class="cl">sloth validate -i slo.yaml</span></span></code></pre></div><p><code>generate</code> 產出的 <code>rules.yaml</code> 包含所有 recording rules 和 alerting rules，直接放入 Prometheus 的 <code>rule_files</code> 載入。<code>validate</code> 在 CI 中先行檢查 YAML 格式與 SLI expression 語法。</p>
<h3 id="k8s-operator-mode">K8s Operator mode</h3>
<p>Sloth 提供 K8s Operator，用 <code>PrometheusServiceLevel</code> CRD 定義 SLO。Operator 自動 reconcile，把 CRD 轉成 Prometheus rules 並同步到 Prometheus Operator 的 <code>PrometheusRule</code> 資源。</p>
<p>Operator mode 適合 K8s-native 環境：SLO 定義跟 service deployment 放在同一個 GitOps repo，變更 SLO 跟變更服務走同一套 PR review + CI 流程。</p>
<h3 id="ci--gitops-整合">CI / GitOps 整合</h3>
<p>在 CI pipeline 中跑 <code>sloth validate</code> 驗證 YAML，再跑 <code>sloth generate</code> 產出 rules，commit 進 GitOps repo。Prometheus 透過 config reload 或 Operator reconcile 載入新 rules。這條流程讓 SLO 變更有版本歷史、有 review、有 rollback 能力。</p>
<h2 id="邊界與陷阱">邊界與陷阱</h2>
<p>Sloth 只支援 Prometheus 作為後端。若觀測平台是 Datadog、New Relic、Honeycomb 或 Grafana Cloud，需要各平台自己的 SLO 功能或 <a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a> 的 multi-source 整合。</p>
<p>SLI expression 錯誤是最常見的問題。分母為零（service 沒有流量）會產生 NaN，cascading 到所有 recording rule。label 不匹配（<code>service</code> label 拼錯）會產生空 series，alert 永遠不觸發。<code>sloth validate</code> 檢查語法但不檢查 Prometheus 中是否真的有對應 series — 上線後需要用 Prometheus query 確認 recording rule 產出非空結果。</p>
<p>SLO 數量增長會累積 recording rule 成本。每個 SLO 產生約 30 條 recording rule（4 windows × 多組 aggregation）。100 個 SLO 產生 3000 條 rule，Prometheus 的 rule evaluation 會消耗明顯的 CPU 和記憶體。定期監控 <code>prometheus_rule_evaluation_duration_seconds</code> 和 <code>prometheus_rule_group_rules</code>，在 rule 數量影響 evaluation latency 前調整。</p>
<p>升級路徑：Sloth YAML 跟 OpenSLO spec 部分相容。從 Sloth 移到 Nobl9 時，SLO 定義的語意可以保留，SLI expression 需要改寫成 Nobl9 的 data source query。這條路徑適合從 Prometheus-only 環境逐步擴展到 multi-source SLO governance。</p>
<h2 id="整合路由">整合路由</h2>
<ul>
<li>上游：<a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 與 Error Budget 政策</a> — SLO 定義與 objective 來源</li>
<li>下游：<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a> — burn rate alert 觸發凍結</li>
<li>平行：<a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a>（SaaS multi-source）、Pyrra（K8s-native + UI）</li>
<li>案例回寫：<a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google G1</a>（error budget policy 原典）、<a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">Honeycomb HC1</a>（burn rate 驅動可靠性操作）</li>
</ul>
]]></content:encoded></item><item><title>9.12 SLO 與 Performance Budget</title><link>https://tarrragon.github.io/blog/backend/09-performance-capacity/slo-performance-budget/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/09-performance-capacity/slo-performance-budget/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>SLO 與 performance budget 的責任是讓容量決策有「可衡量的目標 + 可審查的代價」。沒有 SLO 時、容量規劃容易變「越大越好」、沒邊界；有 SLO + budget 之後、所有決策都能回答「是否在 budget 內」、「超出 budget 該怎麼辦」。&lt;/p>
&lt;p>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">06.6 SLO 與 Error Budget&lt;/a> 的關係：06.6 處理「可靠性 SLO」（用 error budget 凍結 release）、9.12 處理「效能 SLO」（用 performance budget 約束容量）。兩者用同一套方法論、目標不同。讀者可以把本章當作 06.6 的 &lt;em>效能對應&lt;/em> 章節。&lt;/p>
&lt;p>本章覆蓋 SLI/SLO/SLA 分層、latency budget 分解、performance budget vs error budget、SLO 等級的成本含義、多 SLO 對齊、SLO drift 維護。讀完後讀者能設計一套完整的 SLO + budget 系統、把容量決策跟 SLO 對接。&lt;/p>
&lt;h2 id="sli--slo--sla-三層分清">SLI / SLO / SLA 三層分清&lt;/h2>
&lt;p>三個名詞常被混用、實際是三個不同層的概念。&lt;/p>
&lt;p>&lt;strong>SLI（Service Level Indicator）&lt;/strong>：客觀量測值。p99 latency、availability、throughput、error rate 都是 SLI。
&lt;strong>SLO（Service Level Objective）&lt;/strong>：團隊內部目標。「99.95% 用戶請求 &amp;lt; 500ms」這類具體承諾。
&lt;strong>SLA（Service Level Agreement）&lt;/strong>：對外合約承諾。達不到要退款、違約金、信用補償。&lt;/p>
&lt;p>&lt;strong>SLO 比 SLA 嚴 — 給內部 buffer&lt;/strong>。SLA 訂 99.9%、SLO 訂 99.95% — 萬一 SLO 沒達到、SLA 還沒違約、有反應時間。&lt;/p>
&lt;p>&lt;strong>容量規劃針對 SLO、不是 SLA&lt;/strong>：SLA 是「最低不能跌破」、SLO 才是「日常目標」。用 SLA 做容量規劃會經常 violate SLA、給用戶 / 客戶不好體驗。&lt;/p>
&lt;p>詳見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/sli-slo/" data-link-title="SLI / SLO" data-link-desc="說明服務品質指標與服務品質目標如何連接產品承諾">SLI / SLO 卡片&lt;/a>。&lt;/p>
&lt;h2 id="latency-budget-分解">Latency budget 分解&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/latency-budget/" data-link-title="Latency Budget" data-link-desc="把 user-perceived latency 拆到每個 stage 的配額、反推架構選擇">Latency budget&lt;/a> 是把 SLO 翻成可分解工程目標的關鍵工具。&lt;/p>
&lt;p>&lt;strong>從 end-to-end latency 開始&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>用戶感受到的 latency：DNS resolution + TLS handshake + CDN + load balancer + application + cache + DB + serialization + network back&lt;/li>
&lt;li>SLO 訂在 user-perceived：例如「p99 end-to-end &amp;lt; 500ms」&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>拆到每個 stage 的 budget&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>DNS：5ms（assume cached）&lt;/li>
&lt;li>TLS handshake：50ms（first request）&lt;/li>
&lt;li>CDN：20ms&lt;/li>
&lt;li>Load balancer：5ms&lt;/li>
&lt;li>Application：100ms&lt;/li>
&lt;li>Cache lookup：5ms（hit）/ 100ms（miss）&lt;/li>
&lt;li>DB query：30ms&lt;/li>
&lt;li>Serialization：10ms&lt;/li>
&lt;li>Network return：15ms&lt;/li>
&lt;li>&lt;strong>總和&lt;/strong>：240ms（cache hit）/ 335ms（miss）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>每個 stage 的 budget 必須 &lt;em>跟 SLO 對齊&lt;/em>&lt;/strong>：&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>SLO 與 performance budget 的責任是讓容量決策有「可衡量的目標 + 可審查的代價」。沒有 SLO 時、容量規劃容易變「越大越好」、沒邊界；有 SLO + budget 之後、所有決策都能回答「是否在 budget 內」、「超出 budget 該怎麼辦」。</p>
<p>跟 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">06.6 SLO 與 Error Budget</a> 的關係：06.6 處理「可靠性 SLO」（用 error budget 凍結 release）、9.12 處理「效能 SLO」（用 performance budget 約束容量）。兩者用同一套方法論、目標不同。讀者可以把本章當作 06.6 的 <em>效能對應</em> 章節。</p>
<p>本章覆蓋 SLI/SLO/SLA 分層、latency budget 分解、performance budget vs error budget、SLO 等級的成本含義、多 SLO 對齊、SLO drift 維護。讀完後讀者能設計一套完整的 SLO + budget 系統、把容量決策跟 SLO 對接。</p>
<h2 id="sli--slo--sla-三層分清">SLI / SLO / SLA 三層分清</h2>
<p>三個名詞常被混用、實際是三個不同層的概念。</p>
<p><strong>SLI（Service Level Indicator）</strong>：客觀量測值。p99 latency、availability、throughput、error rate 都是 SLI。
<strong>SLO（Service Level Objective）</strong>：團隊內部目標。「99.95% 用戶請求 &lt; 500ms」這類具體承諾。
<strong>SLA（Service Level Agreement）</strong>：對外合約承諾。達不到要退款、違約金、信用補償。</p>
<p><strong>SLO 比 SLA 嚴 — 給內部 buffer</strong>。SLA 訂 99.9%、SLO 訂 99.95% — 萬一 SLO 沒達到、SLA 還沒違約、有反應時間。</p>
<p><strong>容量規劃針對 SLO、不是 SLA</strong>：SLA 是「最低不能跌破」、SLO 才是「日常目標」。用 SLA 做容量規劃會經常 violate SLA、給用戶 / 客戶不好體驗。</p>
<p>詳見 <a href="/blog/backend/knowledge-cards/sli-slo/" data-link-title="SLI / SLO" data-link-desc="說明服務品質指標與服務品質目標如何連接產品承諾">SLI / SLO 卡片</a>。</p>
<h2 id="latency-budget-分解">Latency budget 分解</h2>
<p><a href="/blog/backend/knowledge-cards/latency-budget/" data-link-title="Latency Budget" data-link-desc="把 user-perceived latency 拆到每個 stage 的配額、反推架構選擇">Latency budget</a> 是把 SLO 翻成可分解工程目標的關鍵工具。</p>
<p><strong>從 end-to-end latency 開始</strong>：</p>
<ul>
<li>用戶感受到的 latency：DNS resolution + TLS handshake + CDN + load balancer + application + cache + DB + serialization + network back</li>
<li>SLO 訂在 user-perceived：例如「p99 end-to-end &lt; 500ms」</li>
</ul>
<p><strong>拆到每個 stage 的 budget</strong>：</p>
<ul>
<li>DNS：5ms（assume cached）</li>
<li>TLS handshake：50ms（first request）</li>
<li>CDN：20ms</li>
<li>Load balancer：5ms</li>
<li>Application：100ms</li>
<li>Cache lookup：5ms（hit）/ 100ms（miss）</li>
<li>DB query：30ms</li>
<li>Serialization：10ms</li>
<li>Network return：15ms</li>
<li><strong>總和</strong>：240ms（cache hit）/ 335ms（miss）</li>
</ul>
<p><strong>每個 stage 的 budget 必須 <em>跟 SLO 對齊</em></strong>：</p>
<ul>
<li>每個 stage 加總 = SLO 上限</li>
<li>任何 stage 超 budget → 該 stage 必須改善（不是其他 stage 來補）</li>
<li>每個 stage 必須有 <em>current measurement</em> — 不能訂了沒量</li>
</ul>
<p><strong>Cross-region call 自帶不可壓縮 latency</strong>：</p>
<ul>
<li>同 AZ：&lt; 1ms</li>
<li>跨 AZ：1-2ms</li>
<li>跨 region 同 continent：20-30ms</li>
<li>跨 continent：100-200ms</li>
<li>SLO 訂 50ms 但服務要跨 region 設計 → 不可能達成</li>
</ul>
<p><strong>任何新增 stage 都會吃 budget</strong>：middleware、sidecar、interceptor、API gateway 都會增加 latency。設計時要明確認知這層代價。</p>
<p>對應案例：<a href="/blog/backend/09-performance-capacity/cases/coinbase-ultra-low-latency-exchange-2023/" data-link-title="9.C3 Coinbase International Exchange：超低延遲交易的逆向容量設計" data-link-desc="為什麼 Coinbase 國際交易所選 Cluster Placement Group &#43; z1d 而不是自動擴容 — 延遲敏感型負載的容量取捨">Coinbase sub-ms</a> — sub-millisecond 反推所有架構選擇（Cluster Placement Group 壓網路、z1d 壓 CPU、RAFT 壓共識）；<a href="/blog/backend/09-performance-capacity/cases/tubi-elasticache-ml-feature-store/" data-link-title="9.C25 Tubi：從 ScyllaDB 遷到 ElastiCache、ML feature store 達 sub-10ms p99" data-link-desc="Tubi 把 ML 推薦的 feature store 從 ScyllaDB 遷到 ElastiCache for Redis、99 百分位延遲降到 10ms 以下">Tubi p99 &lt; 10ms</a> — ML inference 多 stage 各自分配 budget。</p>
<h2 id="performance-budget">Performance budget</h2>
<p><a href="/blog/backend/knowledge-cards/performance-budget/" data-link-title="Performance Budget" data-link-desc="跟 error budget 同類概念、但用於 latency / throughput 退化的可控額度">Performance budget</a> 跟 error budget 是 <em>姊妹概念</em> — 用同一套方法論處理可靠性 vs 效能。</p>
<p><strong>Error budget</strong>（<a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">06.6</a>）：</p>
<ul>
<li>每月有允許的 unavailability 額度</li>
<li>例如 SLO 99.95% → error budget = 0.05% × 30 days = 21.6 分鐘 / 月</li>
<li>額度用完 → freeze new release、focus on reliability</li>
</ul>
<p><strong>Performance budget</strong>（本章）：</p>
<ul>
<li>每月有允許的 latency 退化額度</li>
<li>例如「p99 允許比 baseline 高 10ms 連續 X 分鐘」、用 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a> alert</li>
<li>額度用完 → freeze new feature release、focus on perf</li>
</ul>
<p><strong>兩個 budget 並列、不衝突</strong>：</p>
<ul>
<li>一個燒一個健康 → 部分 freeze（freeze 對應的那條）</li>
<li>兩個都健康 → 全速 release</li>
<li>兩個都燒 → 全面 freeze、deep review</li>
</ul>
<p><strong>Burn rate alert 比 threshold alert 好</strong>：</p>
<ul>
<li>threshold：p99 &gt; 500ms 就 alert → false positive 多</li>
<li>burn rate：過去 1 小時 budget burn rate &gt; 14.4x 就 alert（Google SRE 推薦）→ 對應「再這樣下去 budget 5 分鐘內燒光」</li>
</ul>
<p>對應案例：<a href="/blog/backend/09-performance-capacity/cases/coinbase-ultra-low-latency-exchange-2023/" data-link-title="9.C3 Coinbase International Exchange：超低延遲交易的逆向容量設計" data-link-desc="為什麼 Coinbase 國際交易所選 Cluster Placement Group &#43; z1d 而不是自動擴容 — 延遲敏感型負載的容量取捨">Coinbase 延遲就是收入</a> — 沒 performance budget 等於沒 release control；<a href="/blog/backend/09-performance-capacity/cases/fanduel-dual-peak-betting-streaming/" data-link-title="9.C28 FanDuel：體育直播 &#43; 投注的雙重峰值" data-link-desc="FanDuel 3.5M MAU、Super Bowl 期間擴容 5-10 倍、用 AWS Local Zones &#43; Wavelength &#43; Outposts 處理 20&#43; 州的雙重峰值">FanDuel 多 SLO</a> — 直播 vs 投注不同 budget。</p>
<h2 id="slo-等級的成本含義">SLO 等級的成本含義</h2>
<p>不同 SLO 等級對應不同容量成本、選 SLO 就是選成本。</p>
<table>
  <thead>
      <tr>
          <th>SLO</th>
          <th>年 downtime 上限</th>
          <th>工程含義</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>99%</td>
          <td>年 87.6 小時</td>
          <td>單 AZ 部署可接受</td>
          <td>B2C 內部工具、非 critical SaaS</td>
      </tr>
      <tr>
          <td>99.9%</td>
          <td>年 8.76 小時</td>
          <td>多 AZ、reactive failover</td>
          <td>B2C consumer-facing</td>
      </tr>
      <tr>
          <td>99.95%</td>
          <td>年 4.38 小時</td>
          <td>多 AZ active-active、autoscale 必要</td>
          <td>B2B SaaS minimum</td>
      </tr>
      <tr>
          <td>99.99%</td>
          <td>年 52.6 分鐘</td>
          <td>多 region active-active、無人工介入</td>
          <td>mission-critical SaaS</td>
      </tr>
      <tr>
          <td>99.999%</td>
          <td>年 5.26 分鐘</td>
          <td>全球多 region、即時 failover、人工極少</td>
          <td>金融 / 醫療 / 電信</td>
      </tr>
  </tbody>
</table>
<p><strong>每多一個 9、容量成本指數成長</strong>：</p>
<ul>
<li>99 → 99.9：成本 +30-50%</li>
<li>99.9 → 99.99：成本 +50-100%</li>
<li>99.99 → 99.999：成本 +200-500%</li>
</ul>
<p><strong>選 SLO 不是 marketing 決策、是工程經濟決策</strong>：選太高、燒錢；選太低、用戶不滿。要算 <em>每個 9 對應的業務價值</em>、是否值得對應的容量投資。</p>
<p>對應案例：<a href="/blog/backend/09-performance-capacity/cases/amazon-ads-dynamodb-extreme-kv/" data-link-title="9.C5 Amazon Ads：DynamoDB 9000 萬 reads/sec 的廣告事件量測" data-link-desc="Amazon Ads 在 DynamoDB 上跑 9000 萬 reads/sec &#43; 500 萬 writes/sec、99.999% 可用性的廣告事件量測">Amazon Ads 99.999%</a> — 廣告計費 1 分鐘斷線損失幾百萬美金、5 個 9 是真實營收邊界；<a href="/blog/backend/09-performance-capacity/cases/genesys-dynamodb-99999-availability/" data-link-title="9.C24 Genesys：用 DynamoDB 在 15 region 跑出 99.999% 可用性" data-link-desc="Genesys 客服平台用 DynamoDB 為預設資料層、跨 15 主 region &#43; 5 衛星 region、達成 12 個月 99.999% 可用性">Genesys 99.999%</a> — B2B 客服 SaaS、客戶停線 = 客戶失去用戶信任、5 個 9 是合約義務。</p>
<h2 id="多-slo-對齊">多 SLO 對齊</h2>
<p>同一系統不同工作負載可以有不同 SLO、按業務重要性分級。</p>
<p><strong>設計原則</strong>：</p>
<ul>
<li>按「業務重要性 × 用戶感知」分級</li>
<li>同一個 endpoint 不同情境可能有不同 SLO（例如登入 vs 結帳）</li>
<li>多 SLO 必須有 <em>優先順序</em>、衝突時知道犧牲哪個</li>
</ul>
<p><strong>範例</strong>：</p>
<table>
  <thead>
      <tr>
          <th>Endpoint</th>
          <th>SLO</th>
          <th>業務影響</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>登入</td>
          <td>p99 200ms</td>
          <td>用戶 onboarding</td>
      </tr>
      <tr>
          <td>瀏覽商品</td>
          <td>p99 500ms</td>
          <td>用戶 retention</td>
      </tr>
      <tr>
          <td>結帳</td>
          <td>p99 300ms</td>
          <td>直接影響收入</td>
      </tr>
      <tr>
          <td>推薦</td>
          <td>p99 1000ms</td>
          <td>影響 conversion 但非阻斷</td>
      </tr>
  </tbody>
</table>
<p><strong>衝突處理</strong>：當 capacity 不夠時、優先保 <em>結帳</em> 而非 <em>推薦</em>、即使技術上推薦比較好擴容。</p>
<p>對應案例：<a href="/blog/backend/09-performance-capacity/cases/fanduel-dual-peak-betting-streaming/" data-link-title="9.C28 FanDuel：體育直播 &#43; 投注的雙重峰值" data-link-desc="FanDuel 3.5M MAU、Super Bowl 期間擴容 5-10 倍、用 AWS Local Zones &#43; Wavelength &#43; Outposts 處理 20&#43; 州的雙重峰值">FanDuel</a> 直播秒級 SLO vs 投注毫秒級 SLO、同一個 user 同一場 NFL Super Bowl、兩個服務必須分開部署、各自 SLO。</p>
<h2 id="slo-演進baseline-drift">SLO 演進：baseline drift</h2>
<p><a href="/blog/backend/knowledge-cards/slo-baseline-drift/" data-link-title="SLO Baseline Drift" data-link-desc="SLO baseline 因業務變化 / surge / 架構改動而需要重新校準的現象">SLO 不是訂了就不動</a> — 業務變化要重新校準。</p>
<p><strong>SLO drift 來源</strong>：</p>
<ul>
<li>Structural surge：COVID 類外部衝擊讓 baseline 永久上移</li>
<li>Product change：新 feature 改變用戶 journey</li>
<li>Architectural improvement：DB 換型、cache 加強、CDN 擴點</li>
<li>User behavior：mobile share 上升、跨 region 比例變化</li>
</ul>
<p><strong>Drift 不是 anomaly、是 <em>新常態</em></strong>。</p>
<p><strong>Review 節奏</strong>：</p>
<ul>
<li>每季 review SLO：拉過去 90 天 SLI 分布、看是否需要調整</li>
<li>重大產品改動立即 review</li>
<li>Drift 確認後要更新：alert threshold、autoscaler trigger、performance budget 額度、容量規劃 baseline</li>
</ul>
<p>對應案例：<a href="/blog/backend/09-performance-capacity/cases/zoom-covid-surge-dynamodb/" data-link-title="9.C18 Zoom：COVID 期間從 1000 萬到 3 億 DAU 的 30 倍突發" data-link-desc="Zoom 在 2020 年 COVID 爆發時、日活從 1000 萬衝到 3 億、用 DynamoDB 撐住會議後端">Zoom 30x COVID</a> — 30 倍成長後 baseline 永久上移、SLO threshold 跟著重新校準、不能套用 COVID 前的標準。</p>
<h2 id="slo-跟容量規劃對接">SLO 跟容量規劃對接</h2>
<p>回到本章開頭的論點 — SLO 是容量決策的目標。</p>
<p><strong>容量公式</strong>：能撐多少 RPS @ SLO 條件。
<strong>規劃時用「SLO-constrained capacity」、不是「max capacity」</strong>：</p>
<ul>
<li>max capacity：絕對極限、進 cliff</li>
<li>SLO-constrained capacity：知道在 SLO 條件下能撐多少</li>
<li>兩者差 30-50%（headroom）</li>
</ul>
<p><strong>9.4 saturation 找 knee 是技術指標、9.6 容量規劃用 SLO-constrained knee</strong>：</p>
<ul>
<li>saturation 在 utilization 80% 時開始</li>
<li>但 SLO 可能要求 utilization 60% 以下</li>
<li>容量規劃用 60% 而非 80%</li>
</ul>
<p><strong>跟 <a href="/blog/backend/09-performance-capacity/cost-engineering/" data-link-title="9.7 成本邊界與 efficiency" data-link-desc="cost per request、cost curve、降級成本、over-provisioning trade-off">9.7 成本工程</a> 對接</strong>：</p>
<ul>
<li>每多一個 9 多花多少錢</li>
<li>業務需要這個 9 嗎</li>
<li>不需要的話降 SLO 省成本</li>
</ul>
<h2 id="slo-跟-performance-budget-一起用">SLO 跟 performance budget 一起用</h2>
<p>最後的整合 — error budget + performance budget 一起治理 release 節奏。</p>
<p><strong>Error budget 控制 <em>變更節奏</em></strong>：</p>
<ul>
<li>error budget 健康 → release 可以快</li>
<li>error budget 燒光 → freeze release</li>
</ul>
<p><strong>Performance budget 控制 <em>容量決策</em></strong>：</p>
<ul>
<li>performance budget 健康 → 新 feature 可以引入 perf cost</li>
<li>performance budget 燒光 → freeze new feature</li>
</ul>
<p><strong>兩個 budget 並列</strong>：</p>
<ul>
<li>都健康 → 全速 release + 新 feature</li>
<li>error 健康 + perf 燒 → release 但只接 perf-neutral 變更</li>
<li>error 燒 + perf 健康 → 暫停 release、修可靠性</li>
<li>都燒 → 全面 freeze、deep review</li>
</ul>
<p>對應 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">06.6 SLO</a> 跟 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">06.8 release gate</a>。</p>
<h2 id="案例對照">案例對照</h2>
<table>
  <thead>
      <tr>
          <th>案例</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/coinbase-ultra-low-latency-exchange-2023/" data-link-title="9.C3 Coinbase International Exchange：超低延遲交易的逆向容量設計" data-link-desc="為什麼 Coinbase 國際交易所選 Cluster Placement Group &#43; z1d 而不是自動擴容 — 延遲敏感型負載的容量取捨">9.C3 Coinbase</a></td>
          <td>latency budget 反推架構</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/amazon-ads-dynamodb-extreme-kv/" data-link-title="9.C5 Amazon Ads：DynamoDB 9000 萬 reads/sec 的廣告事件量測" data-link-desc="Amazon Ads 在 DynamoDB 上跑 9000 萬 reads/sec &#43; 500 萬 writes/sec、99.999% 可用性的廣告事件量測">9.C5 / C24 99.999%</a></td>
          <td>5 個 9 的容量代價</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/tubi-elasticache-ml-feature-store/" data-link-title="9.C25 Tubi：從 ScyllaDB 遷到 ElastiCache、ML feature store 達 sub-10ms p99" data-link-desc="Tubi 把 ML 推薦的 feature store 從 ScyllaDB 遷到 ElastiCache for Redis、99 百分位延遲降到 10ms 以下">9.C25 Tubi ML stage budget</a></td>
          <td>p99 多 stage 分配</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/fanduel-dual-peak-betting-streaming/" data-link-title="9.C28 FanDuel：體育直播 &#43; 投注的雙重峰值" data-link-desc="FanDuel 3.5M MAU、Super Bowl 期間擴容 5-10 倍、用 AWS Local Zones &#43; Wavelength &#43; Outposts 處理 20&#43; 州的雙重峰值">9.C28 FanDuel 多 SLO</a></td>
          <td>直播 vs 投注不同 SLO 並存</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/zoom-covid-surge-dynamodb/" data-link-title="9.C18 Zoom：COVID 期間從 1000 萬到 3 億 DAU 的 30 倍突發" data-link-desc="Zoom 在 2020 年 COVID 爆發時、日活從 1000 萬衝到 3 億、用 DynamoDB 撐住會議後端">9.C18 Zoom</a></td>
          <td>SLO baseline 重新校準</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游：<a href="/blog/backend/09-performance-capacity/performance-theory/" data-link-title="9.1 壓測理論與系統行為" data-link-desc="Little&#39;s Law、queueing theory、USL、saturation curve 在容量規劃中的角色">9.1 壓測理論</a>（latency budget 反推）</li>
<li>上游：<a href="/blog/backend/09-performance-capacity/saturation-discovery/" data-link-title="9.4 Saturation Discovery" data-link-desc="找出 throughput plateau 與 latency knee 的方法">9.4 Saturation Discovery</a>（SLO-constrained capacity）</li>
<li>跨模組：<a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">06.6 SLO 與 Error Budget 政策</a>（可靠性 SLO）</li>
<li>跨模組：<a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">04.16 SLI / SLO 訊號</a>（量測層）</li>
</ul>
<h2 id="既建知識卡片">既建知識卡片</h2>
<ul>
<li><a href="/blog/backend/knowledge-cards/sli-slo/" data-link-title="SLI / SLO" data-link-desc="說明服務品質指標與服務品質目標如何連接產品承諾">SLI / SLO</a></li>
<li><a href="/blog/backend/knowledge-cards/latency-budget/" data-link-title="Latency Budget" data-link-desc="把 user-perceived latency 拆到每個 stage 的配額、反推架構選擇">Latency Budget</a></li>
<li><a href="/blog/backend/knowledge-cards/performance-budget/" data-link-title="Performance Budget" data-link-desc="跟 error budget 同類概念、但用於 latency / throughput 退化的可控額度">Performance Budget</a></li>
<li><a href="/blog/backend/knowledge-cards/slo-baseline-drift/" data-link-title="SLO Baseline Drift" data-link-desc="SLO baseline 因業務變化 / surge / 架構改動而需要重新校準的現象">SLO Baseline Drift</a></li>
<li><a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">Error Budget</a></li>
</ul>
]]></content:encoded></item></channel></rss>