System Design × 分散式系統完整單檔 Roadmap
單檔整合版:本文件已內嵌 Roadmap、Learning Objective 理解確認、診斷題與答案、最小實驗、Hexagonal Architecture 共用規範,以及全部 11 個 Lab 的完整規格。
文件內不再依賴其他web-sandbox、Markdown、HTML 或 ZIP 附件;只有官方參考來源會開啟外部網站。
如何使用這份單檔
- 依 Roadmap 階段閱讀教材。
- 在同一階段完成 Learning Objective 的診斷題與最小實驗。
- 點擊文件內的 Lab 連結,直接跳到後半的完整實作規格。
- 同時通過「理解題目」與「工程測試」後,才勾選該目標。
- HTML 版適合互動閱讀;Markdown 版適合 GitHub、Obsidian、VS Code。
Python 後端工程師的 System Design × 分散式系統整合學習 Roadmap
這是整合版主文件:Roadmap、Learning Objectives、診斷題、標準答案、最小實驗、Lab 與通過條件全部放在同一份文件。
核心路線:約 40 週;建議每週投入 6–8 小時。理論教材不限語言;主要實作使用 Python,FastAPI 系統採 Hexagonal Architecture。
整合版使用方式
- 依階段閱讀資源,不要跳過理論主線。
- 完成該階段 Lab 的 Critical Tests、concurrency tests 與 fault tests。
- 在同一階段展開「學習目標與理解驗收」,先不看答案作答。
- 每題自評 0–3 分:0 不會;1 只會名詞;2 結論正確但理由/邊界不足;3 能解釋原因、反例、前提與設計影響。
- 完成每個目標的最小確認實驗與 required evidence。
- 題目分數與實驗證據都通過後,才勾選該目標。
- 答錯題必須轉成 learning note、ADR 或新的 automated test。
總覽與使用方法
40 週核心路線,可依每週時間拉長 6–8 h建議每週投入時間 50%+時間用於實作、測試與設計文件 5 層Unit → Integration → Concurrency → Fault → Checker
這不是課程收藏清單。每個階段都必須產生可檢查的成果:程式、測試、故障矩陣、Design Doc 或 Maelstrom checker 結果。
每週時間分配
| 工作 | 時間 |
|---|---|
| 理論閱讀/影片 | 2 小時 |
| Python 實作 | 3 小時 |
| 測試與故障注入 | 1.5 小時 |
| Design Doc/筆記 | 1 小時 |
| 回顧與重構 | 0.5 小時 |
每個 Repository 固定保留
SPEC.md
DESIGN.md
INVARIANTS.md
FAILURE_MATRIX.md
TEST_REPORT.md
所有 Lab 的統一規格(Hexagonal Edition)
重要修正:所有 Lab 都不能只有題目名稱。
每個 Lab 都必須包含 observable contract、ports、adapters、invariants、failure model、automated tests、evidence 與 pass criteria。
- 跳到本檔後半的完整 Lab Handbook
- 互動式 Lab Handbook 已合併進本檔,不再需要另外開啟附件。
- 所有獨立 Lab 規格已完整內嵌於本檔後半,不再依賴 ZIP。
- 跳到 FastAPI A → B Hexagonal 完整規格
FastAPI 系統統一依賴方向
FastAPI Router / Message Consumer(Inbound Adapter)
↓
Inbound Port / Use Case
↓
Application + Domain
↓
Outbound Port
↓
PostgreSQL / HTTPX / Kafka / Redis / Temporal(Outbound Adapter)
domain不得 import FastAPI、Pydantic、SQLAlchemy、HTTPX、Kafka SDK、Temporal SDK。application只依賴 domain 與抽象 ports。adapters實作 ports。bootstrap.py/composition_root.py是唯一組裝具體依賴的位置。- 每個 fake adapter 與 real adapter 必須接受相同 contract tests。
- 真實 database、broker 與 network behavior 仍須 integration / fault tests,不能只靠 fake。
階段 0:必要基礎:Python Concurrency、HTTP 與 DB Transaction
建議時間:2 週|先能精確理解 timeout、deadline、cancellation、connection pool 與 transaction anomaly。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 0|Coroutine、Task、Thread、Process;Concurrency 與 Parallelism
**Roadmap 原始目標:** 能區分 coroutine、task、thread、process,以及 concurrency 與 parallelism。
必須真正理解的觀念
- Coroutine 是可暫停與恢復的計算單位;呼叫 async function 只建立 coroutine object,不代表已排程執行。
- Task 是 event loop 對 coroutine 的排程與生命週期管理單位;它可能與其他 task 交錯執行。
- Thread 是作業系統排程的執行緒,共享同一 process 記憶體;同步與資料競爭是主要風險。
- Process 有獨立位址空間,隔離較強,跨 process 通訊與序列化成本較高。
- Concurrency 是多項工作在重疊期間推進;Parallelism 是同一瞬間真的在多個運算資源上執行。
- asyncio 適合大量等待型 I/O;CPU-bound 工作不能因為加上 async 就變快。
- CPython GIL 影響一般 Python bytecode 的多執行緒 CPU parallelism,但不等於 thread 沒用,也不等於所有 Python implementation 都完全相同。
一對一診斷題
Q1. 呼叫 coro = fetch() 後,fetch() 是否已開始執行?
標準答案、解析與錯題診斷
標準答案
通常沒有。async def 被呼叫時會建立 coroutine object;要 await coro、用 asyncio.create_task(coro) 排程,或由其他框架排程後才會推進。
為什麼
這題區分 coroutine object 與正在執行的 Task。
答錯通常代表
若回答『已執行』,表示把建立 coroutine 與排程執行混在一起。
Q2. await 是否代表作業系統一定切換到另一個 thread?
標準答案、解析與錯題診斷
標準答案
不是。await 表示目前 coroutine 在等待可 await 物件時把控制權交回 event loop;其他 task 可在同一 thread 上執行。是否涉及 thread 取決於底層 operation,例如 asyncio.to_thread()。
為什麼
Async concurrency 可以完全在單一 OS thread 中發生。
答錯通常代表
若回答一定切 thread,表示把 cooperative scheduling 與 preemptive threads 混淆。
Q3. 一個 event loop 同時管理 10,000 個 socket,這是 concurrency 還是 parallelism?
標準答案、解析與錯題診斷
標準答案
主要是 concurrency。多個連線的等待時間互相重疊,但如果只有一個 event-loop thread,Python callbacks 通常不是同一瞬間平行執行。
為什麼
大量 I/O concurrency 不需要每個連線一個 thread。
答錯通常代表
若回答 parallelism,表示把『很多工作同時存在』誤認為『同時運算』。
Q4. 兩個 Python threads 在 CPython 中是否完全沒有用途?
標準答案、解析與錯題診斷
標準答案
不是。Threads 對 blocking I/O、會釋放 GIL 的 C extension,以及整合同步 library 很有用;只是純 Python CPU-bound bytecode 通常不能藉多 thread 線性擴展。
為什麼
GIL 限制的是特定類型的 CPU parallelism,不是否定 thread 的所有用途。
答錯通常代表
若回答完全沒用,表示把 GIL 結論過度泛化。
Q5. CPU-bound 影像轉碼放進 FastAPI async def,是否會避免阻塞 event loop?
標準答案、解析與錯題診斷
標準答案
不會。只要函式在 event-loop thread 上執行長時間同步 CPU 工作,就會阻塞其他 tasks。應移到 process pool、外部 worker 或專用服務;to_thread 只適合某些 blocking I/O 或會釋放 GIL 的工作。
為什麼
async def 本身不會自動把運算搬離 event loop。
答錯通常代表
若回答會,表示把 async syntax 當成自動平行化。
Q6. Thread 與 process 的最重要隔離差異是什麼?
標準答案、解析與錯題診斷
標準答案
同一 process 內 threads 共享位址空間與多數資源;不同 processes 有獨立位址空間,必須用 IPC、socket、queue 或 shared memory 溝通。
為什麼
隔離影響故障傳播、資料共享、同步與部署方式。
答錯通常代表
若只回答『process 比較慢』,表示尚未掌握語意差異。
Q7. 以下哪個情境較適合 asyncio:大量呼叫外部 HTTP API,還是純 Python 計算一億次雜湊?
標準答案、解析與錯題診斷
標準答案
大量外部 HTTP API 較適合 asyncio,因為大部分時間在等待 I/O。純 Python CPU 計算較適合多 process、native extension 或專用計算 worker。
為什麼
選擇 concurrency model 要看工作時間花在 waiting 還是 computing。
答錯通常代表
若只依函式數量選 async,表示缺少 workload 分析。
Q8. Task 被取消時,遠端 HTTP request 是否一定未執行?
標準答案、解析與錯題診斷
標準答案
不一定。取消只會停止或中斷本地等待與後續程式;遠端 server 可能已收到甚至完成請求。仍要處理 RPC ambiguity 與 idempotency。
為什麼
Local cancellation 無法回滾遠端 side effect。
答錯通常代表
若回答一定取消遠端,表示混淆本地 task lifecycle 與分散式操作。
Q9. asyncio.gather() 是否必然讓工作平行執行?
標準答案、解析與錯題診斷
標準答案
不必然。它讓 awaitables 並發推進;若內容是非阻塞 I/O,等待可重疊。若每個 coroutine 都做同步 CPU 工作且不 yield,仍會序列阻塞 event loop。
為什麼
API 名稱或同時排程不等於硬體 parallel execution。
答錯通常代表
若回答必然平行,表示 concurrency/parallelism 尚未真正分清。
Q10. 資料結構只在單一 event-loop thread 中被多個 tasks 使用,就完全沒有 race condition 嗎?
標準答案、解析與錯題診斷
標準答案
不一定。Coroutine 可在 await 處交出控制權,讀取—等待—寫入的複合操作可能被其他 task 插入,形成 logical race。需要 lock、immutable design 或重整臨界區。
為什麼
Cooperative concurrency 仍然會有 interleaving。
答錯通常代表
若認為單 thread 就沒有 race,表示把資料競爭只理解成 CPU 同時寫入。
Q11. 什麼情況下應該選 process pool 而不是 thread pool?
標準答案、解析與錯題診斷
標準答案
主要是純 Python CPU-bound 工作,希望使用多核心;也可能需要更強故障隔離。代價是序列化、啟動與 IPC 成本較高。
為什麼
選擇基於 CPU 特性與隔離需求,不是固定偏好。
答錯通常代表
若回答所有 background job 都用 process,表示缺少成本判斷。
Q12. Concurrency 和 parallelism 可以同時存在嗎?
標準答案、解析與錯題診斷
標準答案
可以。例如四個 process 各自有 asyncio event loop:process 間可能在多核心 parallel 執行,每個 process 內又以 tasks 處理 I/O concurrency。
為什麼
兩者是不同維度,不是互斥選項。
答錯通常代表
若認為只能二選一,表示概念仍過度簡化。
最小確認實驗
最小確認實驗:四種執行模型比較
- 建立 50 個
asyncio.sleep(0.1)工作,量測 sequential await 與 create_task/gather 的總時間。 - 建立 20 個
time.sleep(0.1),分別直接放在 event loop、asyncio.to_thread()執行,觀察 heartbeat task 是否被阻塞。 - 建立一個純 Python CPU-heavy 函式,分別以 sequential、ThreadPoolExecutor、ProcessPoolExecutor 執行,記錄 wall time 與 CPU 使用。
- 製造 logical race:兩個 tasks 讀 counter、
await asyncio.sleep(0)、再寫 counter;重現 lost update,再以 Lock 修正。
必須提交的證據
- 一張表:模型、工作類型、總時間、是否阻塞 event loop、是否使用多核心。
- 能解釋為什麼 I/O gather 約接近單次等待,而 CPU task 不會因 async 自動加速。
- pytest 驗證 heartbeat 在 blocking call 直接執行時延遲、移到適當 executor 後恢復。
勾選這條學習目標前的通過條件
- 12 題中至少 10 題達 3 分,其餘不得低於 2 分。
- 不看答案畫出 coroutine → task → event loop 的關係圖。
- 能針對 I/O-bound、blocking library、CPU-bound 三個案例選擇模型並說明代價。
- 完成最小實驗,不能只用口頭回答。
錯題回補路徑
- 若 coroutine/task 題錯:重讀 Python asyncio 的 Coroutines and Tasks、Task cancellation。
- 若 thread/process 題錯:做 executor 對照實驗,觀察 PID、thread ID 與 CPU 使用。
- 若 concurrency/parallelism 題錯:每個案例強制回答『工作是否重疊推進?同一瞬間是否在多核心執行?』。
- 若 race 題錯:在每個
await前後標記可能 interleave 的位置。
參考來源
階段 0|HTTP Timeout、Pool 與 Overall Deadline
**Roadmap 原始目標:** 能區分 connect、read、write、pool timeout 與 overall deadline。
必須真正理解的觀念
- Connect timeout:建立 TCP/TLS connection 的等待上限。
- Read timeout:已送出後等待 response data 的讀取上限,不代表遠端沒執行。
- Write timeout:傳送 request body 時無法在限制內寫入。
- Pool timeout:等待從本地 connection pool 取得可用 connection。
- Overall deadline:整個業務操作的總時間預算,應涵蓋 pool、connect、write、read、backoff 與 retry。
一對一診斷題
Q1. PoolTimeout 發生時,request 是否一定已抵達 server?
標準答案、解析與錯題診斷
標準答案
通常尚未。Client 還在等待本地 pool connection;下游慢可能間接造成 pool 被占滿。
為什麼
區分本地資源等待與遠端 response 等待。
答錯通常代表
若當成 ReadTimeout,錯誤分類尚未掌握。
Q2. ReadTimeout 能否安全地自動重試 POST?
標準答案、解析與錯題診斷
標準答案
不能只靠 ReadTimeout 判斷。遠端可能已 commit;需 operation idempotency 或 idempotency key。
為什麼
Transport phase 不等於 business execution state。
答錯通常代表
若回答可以,RPC ambiguity 尚未理解。
Q3. 為何每次 attempt 都設 500ms,重試 3 次,不等於 overall deadline 500ms?
標準答案、解析與錯題診斷
標準答案
因為還包含三次 attempt、backoff、pool/connect 等,總時間可能遠大於 500ms。
為什麼
單次 timeout 與端到端 budget 不同。
答錯通常代表
若只把 timeout 數字複製,deadline 設計不足。
Q4. 剩餘 deadline 只有 20ms,預估下一次 call p50 100ms,應否重試?
標準答案、解析與錯題診斷
標準答案
通常不應,應快速回覆 deadline exceeded/unknown。
為什麼
不開始注定無法在 budget 內完成的工作。
答錯通常代表
若仍重試,缺少 deadline-aware policy。
Q5. ConnectTimeout 是否百分之百代表 server 未收到請求?
標準答案、解析與錯題診斷
標準答案
不能宣稱百分之百,實際路徑可能有 proxy、重用連線或 failure boundary 差異;它通常風險較低,但 retry 仍需依業務語意。
為什麼
避免把經驗機率當 protocol guarantee。
答錯通常代表
若使用『一定』,保證邊界不清。
Q6. 如何驗證 overall deadline?
標準答案、解析與錯題診斷
標準答案
用 fake clock 測 retry/backoff unit tests,再用延遲 server/Toxiproxy integration test,assert wall time 不超過 deadline + tolerance。
為什麼
時間政策需 deterministic 與真實整合雙層驗證。
答錯通常代表
若只 sleep 後目測,測試不可靠。
最小確認實驗
最小確認實驗:四種 timeout 分離
- HTTPX max_connections=1 製造 PoolTimeout。
- 對不可路由/未監聽位址製造 ConnectTimeout。
- Server 接受後延遲 body 製造 ReadTimeout。
- 建立 overall deadline,加入兩次 retry 與 backoff。
必須提交的證據
- 每種錯誤映射到不同 domain/application error。
- 第二個 PoolTimeout request 未抵達 server 的 assertion。
- 總時間符合 deadline。
勾選這條學習目標前的通過條件
- 6 題至少 5 題達 3 分。
- 能畫出一次 HTTP call 的 pool→connect→write→read timeline。
- 完成四種 timeout integration tests。
錯題回補路徑
- 錯在 timeout phase:重畫 HTTP lifecycle。
- 錯在 retry safety:回看 RPC ambiguity/idempotency。
- 錯在 deadline:用剩餘時間逐步計算一次 scenario。
參考來源
階段 0|MVCC、Lost Update 與 Transaction Retry
**Roadmap 原始目標:** 能重現 lost update,並以 lock、version 或 Serializable retry 解決。
必須真正理解的觀念
- MVCC 讓不同 transaction 看到各自 snapshot,但不自動保護所有 read-modify-write invariant。
- Lost update 可能在最終欄位看似合理時仍重複接受業務操作。
- Pessimistic lock、optimistic version、Serializable 各有不同 contention 與 retry 成本。
- Serialization failure 必須重跑整個 transaction。
- 外部 side effect 不能安全放進可重試 transaction。
一對一診斷題
Q1. 兩個訂單都成功,但庫存最終從 1 變 0,是否一定正確?
標準答案、解析與錯題診斷
標準答案
不一定,可能兩個 transaction 都讀 1 並覆寫成 0,產生 lost update。應檢查成功 reservation 總和。
為什麼
Final state 不足以描述 history correctness。
答錯通常代表
若只看 stock 欄位,並發 anomaly 未掌握。
Q2. SELECT FOR UPDATE 的主要代價?
標準答案、解析與錯題診斷
標準答案
Lock wait、deadlock、長 transaction contention 與 throughput 降低。
為什麼
悲觀鎖用等待換互斥。
答錯通常代表
若說沒有代價,trade-off 不足。
Q3. Optimistic version conflict 應如何處理?
標準答案、解析與錯題診斷
標準答案
Adapter 辨認 CAS/update rowcount conflict,轉成語意錯誤;application 有限 retry 或回 409。
為什麼
SQL 細節不應洩漏到 router/domain。
答錯通常代表
若 silent overwrite,correctness 失敗。
Q4. Serializable 是否保證不會 abort?
標準答案、解析與錯題診斷
標準答案
不保證;DB 可中止無法序列化的 transaction,application 要重跑。
為什麼
Isolation guarantee 伴隨 retry obligation。
答錯通常代表
若認為最嚴格就一定成功,概念錯誤。
Q5. 為什麼不能只 retry 最後一條 UPDATE?
標準答案、解析與錯題診斷
標準答案
所有讀取/決策建立在失效 snapshot,需重跑完整 transaction。
為什麼
Transaction 是決策一致性邊界。
答錯通常代表
若只重送 SQL,snapshot reasoning 不足。
Q6. 為何可重試 transaction 內寄 email 危險?
標準答案、解析與錯題診斷
標準答案
Transaction retry 可能重寄,DB rollback 也不能撤回 email。應用 outbox或 commit 後處理。
為什麼
外部 side effect 不在 DB atomic boundary。
答錯通常代表
若只 catch exception,未處理 crash window。
最小確認實驗
最小確認實驗:三種策略同一驗收測試
- 先故意寫 naive read-modify-write 重現 lost update。
- 以 SELECT FOR UPDATE 修正。
- 以 version column/CAS 修正。
- 以 Serializable + whole transaction retry 修正。
必須提交的證據
- 初始庫存 1、20 concurrent requests 恰好一個成功。
- 三個實作通過同一 acceptance contract。
- 記錄 conflict/retry/latency,說明沒有普遍最好。
勾選這條學習目標前的通過條件
- 6 題至少 5 題達 3 分。
- 能說明三種策略適用 workload。
- Critical concurrency tests 全通過。
錯題回補路徑
- 錯在 anomaly:畫兩個 transaction timeline。
- 錯在 Serializable:重讀 PostgreSQL isolation 與 retry 文件。
- 錯在 side effect:提前學 Outbox 的 dual-write 問題。
參考來源
學習目標
- 能區分 coroutine、task、thread、process,以及 concurrency 與 parallelism。
- 能區分 connect、read、write、pool timeout 與 overall deadline。
- 能重現 lost update,並以 lock、version 或 Serializable retry 解決。
學習資源
必修 階段 0 深入教材 Python asyncio 分層教學
從 Runtime、Event Loop、Coroutine 與 Task,一路連接到 Thread、Process、FastAPI worker 與 uvloop;完成程式範例及自我檢查後,再回到本階段的最小實驗。
必修 原始依據 Python asyncio 官方文件
閱讀 Coroutines and Tasks、TaskGroup、Timeout、Cancellation。
必修 階段 0 深入教材 HTTPX:Timeouts、Deadlines 與 Resource Limits
從 request lifecycle、四種 timeout 與 overall deadline,一路連接到 connection pool、FastAPI client lifecycle、cancellation、retry、idempotency 與 fault injection。
必修 原始依據 HTTPX:Timeouts 與 Resource Limits
理解四種 timeout 以及 client connection pool。
開啟 Timeout 文件 ↗ 開啟 Resource Limits ↗
必修 階段 0 深入教材 PostgreSQL MVCC、Isolation 與 Transaction Retry
從 tuple versions、snapshot visibility 與 Read Committed,一路連接到 lost update、write skew、locks、optimistic version、Serializable SSI、retry 與 vacuum。
必修 原始依據 PostgreSQL MVCC 與 Isolation
閱讀 MVCC、Transaction Isolation、Explicit Locking。
MVCC Introduction ↗ Transaction Isolation ↗
Python Lab
Lab A:FastAPI A → B
- 建立 normal、delay-before-commit、delay-after-commit、hold-connection 等測試情境。
- 實作共用 AsyncClient、四種 timeout、pool limit 與 overall deadline。
- 實作 database-backed idempotency key 與 request hash 驗證。
- 通過 commit 後 response timeout、20 個 concurrent duplicate、pool exhaustion 等核心測試。
Lab B:庫存併發
- 重現兩個 transaction 同時扣最後一件庫存。
- 比較 SELECT FOR UPDATE、optimistic lock、Serializable retry。
驗收標準
- 能說明 read timeout 時,server 為什麼可能已完成操作。
- 能解釋為什麼不能對所有 POST 無條件 retry。
- 測試使用真實 PostgreSQL,不以 SQLite 或 mock 代替。
階段 1:建立 System Design 固定分析框架
建議時間:2 週|學會由需求、NFR、容量、資料模型一路推進至失敗情境與取捨。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 1|System Design 問題拆解與 NFR
**Roadmap 原始目標:** 能由需求、NFR、容量、API、資料模型推進到 failure handling 與 trade-off。
必須真正理解的觀念
- 先定義 goals/non-goals。
- NFR 必須可量測。
- Capacity estimate 用來找壓力點。
- 元件選擇由需求推導。
- Failure matrix 必須包含偵測、使用者行為與恢復。
一對一診斷題
Q1. 為何不能一開始就選 Kafka?
標準答案、解析與錯題診斷
標準答案
因為是否需要 durable log、async decoupling、ordering/scale 要由需求與 failure model 推導。
為什麼
工具是設計結果。
答錯通常代表
若以熟悉度選元件,問題拆解不足。
Q2. 『高可用』算合格 NFR 嗎?
標準答案、解析與錯題診斷
標準答案
不夠;應定義 SLI、目標值、窗口與排除條件。
為什麼
不可量測就不可驗收。
答錯通常代表
若沒有數字/window,NFR 模糊。
Q3. Capacity estimate 不是精準預測,為何仍重要?
標準答案、解析與錯題診斷
標準答案
可發現量級、hot partition、storage growth、bandwidth與讀寫比例。
為什麼
估算是風險探索工具。
答錯通常代表
若只算機器數,目的過窄。
Q4. 回傳 202 代表什麼 durability guarantee?
標準答案、解析與錯題診斷
標準答案
必須由 API contract 明確定義,例如 durable queue/DB 已接收,而非只是 process memory。
為什麼
Status code 必須連到持久狀態。
答錯通常代表
若沒有 durable boundary,202 可能丟資料。
Q5. Failure matrix 除了『DB 掛掉』還要寫什麼?
標準答案、解析與錯題診斷
標準答案
Detection、user-visible behavior、data state、auto/manual recovery、metrics/alert、RPO/RTO。
為什麼
列故障名稱不等於設計行為。
答錯通常代表
若無恢復與觀測,設計不可營運。
Q6. Alternative considered 如何避免成為形式文件?
標準答案、解析與錯題診斷
標準答案
列真實可行方案、條件式優缺點與未來改選條件。
為什麼
保留決策前提才能重評。
答錯通常代表
若替代方案明顯是稻草人,trade-off 不真實。
最小確認實驗
最小確認實驗:B2B 表單 Design Doc
- 寫 Goals/Non-goals/NFR。
- 估算流量與資料。
- 畫 C4 Context/Container。
- 列 8 個 failure cases。
- 做一個 critical slice 驗證 202 durability。
必須提交的證據
- Design Doc rubric ≥75。
- 能指出至少一個實驗後修正的假設。
勾選這條學習目標前的通過條件
- 6 題至少 5 題達 3 分。
- Design Doc 包含具體 SLO 與 failure matrix。
- 能在 15 分鐘內口頭由需求推導架構。
錯題回補路徑
- NFR 錯:把形容詞改成數字+窗口。
- Capacity 錯:列出 QPS、payload、retention、fanout。
- 元件先行:強制寫『不用此元件會先在哪裡失效』。
參考來源
資源
必修 免費 System Design Primer(繁中)
建立可重複使用的設計題分析流程。
查閱 免費 ByteByteGo System Design 101
作為 load balancer、cache、queue、sharding 等圖解辭典。
在 Obsidian 依分類閱讀 查看 GitHub 上游 ↗
必修 免費 C4 Model
先學 System Context 與 Container Diagram。
必修 免費 Software Engineering at Google:Documentation → Design Docs 小節
短篇原則概覽:Google 說明 Design Doc 應涵蓋 goals、implementation strategy、key decisions、trade-offs 與 alternatives;這不是完整模板。
第一份 Design Doc:可靠 B2B 詢價表單
- 寫出 Goals、Non-goals、功能需求與 NFR。
- 完成容量估算、API 與 data model。
- 完成 Context 與 Container diagram。
- 定義「回傳 202」時已經獲得的 durability guarantee。
- 列出重複提交、寄信失敗、API 不可用等 failure matrix。
固定設計順序
需求與範圍
→ Non-functional requirements
→ 容量與流量估算
→ API 與資料模型
→ 高階架構
→ 核心資料流
→ Consistency / transaction boundary
→ Failure handling
→ Observability / security
→ Alternatives / trade-offs
階段 2:分散式系統基本直覺
建議時間:5 週|理解 partial failure、RPC ambiguity、clock、ordering、replication 與 consensus。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 2|Partial Failure、RPC Ambiguity 與 Delivery Semantics
**Roadmap 原始目標:** 能區分 node/network failure、at-most-once、at-least-once、exactly-once effect。
必須真正理解的觀念
- 分散式失敗常是局部且不可立即判斷。
- Timeout 是觀察,不是遠端執行結果。
- Delivery 次數與 business effect 次數不同。
- Idempotency 是重複執行安全的核心。
一對一診斷題
Q1. Timeout 是否等於 operation failure?
標準答案、解析與錯題診斷
標準答案
不等於;可能未執行、執行中或已完成。
為什麼
RPC ambiguity。
答錯通常代表
若等同,partial failure 未掌握。
Q2. At-most-once 是否代表不會遺失?
標準答案、解析與錯題診斷
標準答案
不是;避免重複通常可能犧牲 retry,訊息可遺失。
為什麼
Delivery semantics 有 trade-off。
答錯通常代表
若以為最安全,概念錯。
Q3. At-least-once 的 application obligation?
標準答案、解析與錯題診斷
標準答案
必須容忍 duplicate,使用 idempotency/dedupe。
為什麼
可靠傳遞通常以重複換不遺失。
答錯通常代表
若 consumer 非 idempotent,風險未掌握。
Q4. Exactly-once execution 和 effect 差別?
標準答案、解析與錯題診斷
標準答案
執行可多次,但 observable business effect 可藉去重只出現一次。
為什麼
物理執行與業務結果不同。
答錯通常代表
若混同,後續 messaging 設計會錯。
Q5. Node 沒回 heartbeat 就一定 crash?
標準答案、解析與錯題診斷
標準答案
不一定,也可能 network delay/partition/overload。
為什麼
Failure detector 基於假設。
答錯通常代表
若確定宣告,asynchrony 未理解。
Q6. Client cancellation 能撤銷已 commit 遠端操作嗎?
標準答案、解析與錯題診斷
標準答案
不能。
為什麼
本地 lifecycle 不等於遠端 rollback。
答錯通常代表
若認為能,分散式邊界不清。
最小確認實驗
最小確認實驗:Commit 後斷 response
- B commit order。
- 切斷 response。
- A 收 timeout/unknown。
- 同 key retry,assert 同 order。
必須提交的證據
- DB row、API response、retry result 三方 assertions。
勾選這條學習目標前的通過條件
- 6 題全部至少 2 分,RPC ambiguity 題必須 3 分。
- 完成 commit-after-response-loss test。
錯題回補路徑
- 畫 request 的五種可能 timeline。
- 把 delivery 與 business effect 分成兩欄重答。
參考來源
階段 2|Clock、Causality 與 Ordering
**Roadmap 原始目標:** 能區分 physical clock、Lamport clock、vector clock、causal order 與 total order。
必須真正理解的觀念
- Wall clock 不可靠地表示跨節點先後。
- Lamport 保證 a→b 則 L(a)<L(b),反向不成立。
- Vector clock 可判斷 concurrency。
- Total order 可人工打破 concurrent ties,但不等於 causality。
一對一診斷題
Q1. Wall timestamp 小一定先發生?
標準答案、解析與錯題診斷
標準答案
不一定,clock skew/adjustment。
為什麼
Physical time 有 uncertainty。
答錯通常代表
若依 timestamp 判因果,錯。
Q2. Lamport 的反向命題成立嗎?
標準答案、解析與錯題診斷
標準答案
不成立。
為什麼
Concurrent events 也可有大小。
答錯通常代表
necessary/sufficient 混淆。
Q3. Vector clocks 如何判 concurrent?
標準答案、解析與錯題診斷
標準答案
向量互不逐維小於等於。
為什麼
偏序比較。
答錯通常代表
若比較總和,錯。
Q4. (Lamport,node_id) 可建 total order嗎?
標準答案、解析與錯題診斷
標準答案
可以,但 concurrent ties 是人工順序。
為什麼
Total 不等於 causal。
答錯通常代表
若視為真實先後,錯。
Q5. Duplicate message 由 clock 自動去重嗎?
標準答案、解析與錯題診斷
標準答案
不會,需要 message identity。
為什麼
Ordering 與 dedupe 不同。
答錯通常代表
若混在一起,語意不清。
Q6. Logical clock 能直接解 consistency conflict?
標準答案、解析與錯題診斷
標準答案
不能,只提供資訊,仍需 merge/reject/serialize policy。
為什麼
檢測不等於解決。
答錯通常代表
若認為加 clock 即一致,錯。
最小確認實驗
最小確認實驗:Deterministic simulator
- 實作 Lamport。
- 實作 vector。
- 生成 send/reorder/duplicate scenario。
- property test happened-before。
必須提交的證據
- 事件表列兩種 timestamp。
- 能指出 concurrent pair。
勾選這條學習目標前的通過條件
- 6 題至少 5 題 3 分。
- Property tests 通過。
錯題回補路徑
- 錯在反向命題:寫出反例。
- 錯在 vector:逐維手算 5 組。
參考來源
階段 2|Replication、Quorum 與 Consistency
**Roadmap 原始目標:** 能說明 replication lag、quorum、linearizability、eventual consistency 的保證與代價。
必須真正理解的觀念
- Replication 提升容錯/讀取能力但引入同步與一致性問題。
- R+W>N 是特定假設下的 quorum intersection,不自動保證最新讀。
- Linearizability 提供像單一即時副本的操作語意。
- Eventual consistency 必須搭配收斂與 conflict policy。
一對一診斷題
Q1. 有三 replicas、W=2、R=2 就一定 linearizable 嗎?
標準答案、解析與錯題診斷
標準答案
不一定,還取決於版本、讀修復、並發寫、leader/protocol等。
為什麼
Quorum intersection 只是必要拼圖。
答錯通常代表
若套公式即保證,過度簡化。
Q2. Replication lag 會造成什麼?
標準答案、解析與錯題診斷
標準答案
Stale read、read-your-write 違反、monotonic read 違反。
為什麼
副本進度不同。
答錯通常代表
若只說延遲,使用者 anomaly 未掌握。
Q3. Linearizability 和 serializability 差別?
標準答案、解析與錯題診斷
標準答案
前者是單物件/操作與 real-time order;後者是 transaction 等價 serial order,未必尊重 real-time。
為什麼
不同一致性維度。
答錯通常代表
若混同,consistency model 不清。
Q4. Eventual consistency 是否表示任何結果都可以?
標準答案、解析與錯題診斷
標準答案
不是;在更新停止且網路恢復等假設下副本最終收斂,還需明確 safety。
為什麼
模型仍有保證與前提。
答錯通常代表
若當無一致性,錯。
Q5. Read-your-writes 是全域 linearizability 嗎?
標準答案、解析與錯題診斷
標準答案
不是,是 session/client-centric guarantee。
為什麼
局部 guarantee 較弱。
答錯通常代表
若等同,模型層級不清。
Q6. Leader failure 後讀哪個 replica 安全?
標準答案、解析與錯題診斷
標準答案
取決於 protocol/commit index/lease/quorum;不能只選『最新 timestamp』。
為什麼
安全性需由 replication protocol證明。
答錯通常代表
若任意 failover,stale leader 風險未掌握。
最小確認實驗
最小確認實驗:Primary/Replica simulation
- 非同步複寫。
- 製造 lag。
- 讀不同副本。
- 模擬 failover 與 stale read。
必須提交的證據
- 列出每種 read policy 提供的 guarantee。
勾選這條學習目標前的通過條件
- 6 題至少 5 題 3 分。
- 能針對三個 API path選一致性模型。
錯題回補路徑
- 錯在 quorum:補寫 protocol assumptions。
- 錯在模型:用 execution history 畫例子。
參考來源
階段 2|Distributed Transaction、2PC 與 Consensus
**Roadmap 原始目標:** 能區分 distributed transaction、2PC、consensus 各自解決的問題。
必須真正理解的觀念
- 2PC 協調多參與者原子 commit,但 coordinator/participants 可能 blocking。
- Consensus 讓節點就值/日誌達成一致,處理 leader/failure safety。
- 兩者可組合,但不是同一問題。
- Saga 是多個 local transactions + compensation,不是 ACID rollback。
一對一診斷題
Q1. 2PC 是否等於 consensus?
標準答案、解析與錯題診斷
標準答案
不等於;2PC 是 atomic commit protocol,通常假設 coordinator,故障可能 blocking。
為什麼
問題與 failure property 不同。
答錯通常代表
若等同,核心分類錯。
Q2. Consensus 是否能直接讓跨兩 DB transaction 原子?
標準答案、解析與錯題診斷
標準答案
不能直接;還需 transaction/commit protocol。
為什麼
一致同意 log 不等於跨資源業務 atomicity。
答錯通常代表
若認為 Raft 解所有 transaction,錯。
Q3. 2PC coordinator crash 可能如何?
標準答案、解析與錯題診斷
標準答案
Participants prepared 後可能等待決策而 blocking。
為什麼
Prepared state 不能自行任意 commit/abort。
答錯通常代表
若認為自動 failover即可,需 protocol細節。
Q4. Saga compensation 是 rollback 嗎?
標準答案、解析與錯題診斷
標準答案
不是,是新的業務反向操作,可失敗且不完全恢復。
為什麼
跨服務無共同 rollback。
答錯通常代表
若視為 undo,過度簡化。
Q5. Outbox 能取代所有 distributed transaction 嗎?
標準答案、解析與錯題診斷
標準答案
不能;適合異步事件與最終一致,不適合所有需同步原子可見的業務。
為什麼
Pattern 有適用範圍。
答錯通常代表
若萬用化,trade-off 不足。
Q6. 何時應避免跨服務 transaction?
標準答案、解析與錯題診斷
標準答案
能重劃 boundary/aggregate、接受 async consistency 或集中 ownership 時。
為什麼
先減少協調需求。
答錯通常代表
若直接加2PC,設計成本未評估。
最小確認實驗
最小確認實驗:Atomicity failure table
- 列 DB+broker、兩 DB、payment provider 三種場景。
- 分別比較 2PC、outbox、Saga、reconciliation。
必須提交的證據
- 一張保證/阻塞/可用性/複雜度比較表。
勾選這條學習目標前的通過條件
- 6 題至少 5 題 3 分。
- 能對三個場景選方案並說前提。
錯題回補路徑
- 錯在分類:逐一寫『要一致同意什麼?要原子 commit 什麼?』。
參考來源
核心教材
主教材 付費 Think Distributed Systems
以 mental models 建立 correctness、scalability、reliability 的推理能力,適合作為第一本正式教材。
主課程 免費 Martin Kleppmann Distributed Systems
約 7 小時影片與 87 頁講義,涵蓋 RPC、clock、broadcast、replication、transactions、consensus。
查閱 免費 Distributed Systems 第四版
針對 communication、coordination、replication、fault tolerance 查閱。
建議週次
第 1 週:Network、RPC、Partial FailureTimeout 不代表未執行;失敗偵測永遠建立在假設之上。 第 2 週:Physical / Logical Clock、CausalityLamport clock、vector clock、happened-before。 第 3 週:Broadcast、Ordering、Delivery SemanticsAt-most-once、at-least-once、duplicate 與 reorder。 第 4 週:Replication、Quorum、ConsistencyReplication lag、read/write quorum、stale read。 第 5 週:Transactions、2PC、Consensus釐清 distributed transaction 與 consensus 解決的問題不同。
Python Lab
- 實作 Lamport clock simulator。
- 擴充為 vector clock,判斷 concurrent events。
- 建立 create_order RPC ambiguity 實驗,加入 idempotency key。
- 完成 Maelstrom Echo(Python)。
- 完成 Maelstrom Unique ID(Python)。
Lab 免費 Gossip Glomers
官方教學以 Go 展示,但 Maelstrom 是 language-agnostic,可使用 Python。
Protocol 免費 Maelstrom Protocol
Python node 透過 stdin/stdout 交換逐行 JSON,debug 輸出到 stderr。
階段 3:Replication、Consistency 與故障驗證
建議時間:5 週|不只讓服務能跑,而是宣告 guarantee,並由 checker 與故障測試驗證。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 3|Broadcast、Anti-entropy、CRDT 與 Checker
**Roadmap 原始目標:** 能用 Maelstrom 驗證 broadcast convergence、G-Counter merge 與 failure behavior。
必須真正理解的觀念
- 一次轉發不足以承受 loss。
- Anti-entropy 透過重複狀態同步收斂。
- CRDT merge 要滿足 idempotent/commutative/associative。
- Checker valid 只在 workload/model 範圍成立。
一對一診斷題
Q1. valid=true 是否 production-ready?
標準答案、解析與錯題診斷
標準答案
否,只證明該測試模型/history。
為什麼
證據有邊界。
答錯通常代表
若過度宣稱,驗證觀念不足。
Q2. 為何 set broadcast 可容忍 duplicate?
標準答案、解析與錯題診斷
標準答案
set add idempotent。
為什麼
重複 effect 不變。
答錯通常代表
若認為 network 不重複,錯。
Q3. Anti-entropy 解決什麼?
標準答案、解析與錯題診斷
標準答案
補回單次遺失/partition 後缺少狀態。
為什麼
依狀態收斂。
答錯通常代表
若只是定時發送但無比較,理解不足。
Q4. G-Counter merge 為何是逐維 max?
標準答案、解析與錯題診斷
標準答案
避免 duplicate 重複計數。
為什麼
state-based CRDT merge。
答錯通常代表
若 sum,錯。
Q5. G-Counter 能直接 decrement?
標準答案、解析與錯題診斷
標準答案
不能;需 PN-Counter等。
為什麼
operation set與單調性。
答錯通常代表
若直接減,破壞 merge。
Q6. Safety/liveness 如何寫?
標準答案、解析與錯題診斷
標準答案
Safety 不產生未 broadcast 值;liveness 最終網路可通時已 ack 值收斂。
為什麼
兩類 property。
答錯通常代表
若只有最終一致,描述不足。
最小確認實驗
最小確認實驗:四版 Broadcast
- fanout。
- topology gossip。
- batch anti-entropy。
- delta/ack optimization。
必須提交的證據
- valid、message count、latency、convergence比較。
勾選這條學習目標前的通過條件
- 6 題至少 5 題 3 分。
- Maelstrom fault workloads valid。
- 能解釋 checker property。
錯題回補路徑
- 錯在 CRDT:手算 duplicate/reorder merge。
- 錯在 checker:寫出未測範圍。
參考來源
理論資源
必修 付費 DDIA 第二版:Replication / Trouble / Consistency
先集中閱讀 replication、distributed failure 與 consistency / consensus 章節。
必修 免費 Jepsen Consistency Models
查閱 linearizability、serializability、sequential consistency 等模型與 anomaly。
核心 Lab 免費 Maelstrom
提供模擬 network、fault injection、history、timeline、Lamport diagram 與 consistency checker。
Maelstrom Lab 順序
- Single-node Broadcast。
- Multi-node / Fault-tolerant Broadcast。
- Efficient Broadcast:topology、batch、anti-entropy。
- Grow-Only Counter:monotonic state 與 merge properties。
- 保存並分析 history、messages.svg、timeline.html。
每個 Lab 的 Learning Notes
1. 宣告的 consistency guarantee
2. 允許的 failure model
3. Retry 策略
4. Duplicate / reorder 處理
5. Convergence 或 safety 的理由
6. 仍然無法處理的情況
7. 效能與正確性的取捨
階段 4:DDIA 與 Python Application Architecture
建議時間:7 週|把分散式概念接回 FastAPI、PostgreSQL、domain model 與 transaction boundary。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 4|Hexagonal Architecture、Aggregate 與 UoW
**Roadmap 原始目標:** 能以 FastAPI 建立 Ports & Adapters,正確劃分 aggregate、repository、UoW 與 events。
必須真正理解的觀念
- 核心依賴抽象 ports。
- Aggregate boundary 由 invariant 推導。
- Repository 以 aggregate 語意隔離 persistence。
- UoW 定義 transaction boundary。
- Domain event 不等於 integration event。
一對一診斷題
Q1. Domain 不 import FastAPI 就一定 hexagonal?
標準答案、解析與錯題診斷
標準答案
不一定,application 仍可能直接依賴 SQLAlchemy/HTTPX或 router承載規則。
為什麼
邊界是依賴與責任。
答錯通常代表
若只檢查 import,過窄。
Q2. Repository 只是為了換 DB?
標準答案、解析與錯題診斷
標準答案
不是,主要隔離 persistence並以 aggregate語意存取。
為什麼
換 DB 只是次要收益。
答錯通常代表
若 CRUD wrapper,設計薄弱。
Q3. Aggregate boundary 怎麼決定?
標準答案、解析與錯題診斷
標準答案
圍住需原子維護的 invariant。
為什麼
不是照 table。
答錯通常代表
若一 table一 aggregate,可能錯。
Q4. UoW 何時 commit?
標準答案、解析與錯題診斷
標準答案
Use case完成所有核心變更後明確 commit;例外 rollback。
為什麼
transaction boundary由 application掌控。
答錯通常代表
若 repository自動commit,協調困難。
Q5. Domain event 可直接序列化到 Kafka嗎?
標準答案、解析與錯題診斷
標準答案
通常不應;映射成 versioned integration event。
為什麼
內外契約演進不同。
答錯通常代表
若直接外洩 domain model,耦合。
Q6. FastAPI Depends 可進 domain嗎?
標準答案、解析與錯題診斷
標準答案
不可,僅 inbound/composition root。
為什麼
framework DI不是核心抽象。
答錯通常代表
若進 core,依賴方向錯。
最小確認實驗
最小確認實驗:Order/Inventory use case
- 純 domain allocation。
- application handler + fake UoW。
- SQLAlchemy adapter contract。
- FastAPI inbound mapping。
- architecture import test。
必須提交的證據
- Domain tests不啟動 framework/DB。
- Fake與real adapter同 contract。
勾選這條學習目標前的通過條件
- 6 題全部至少2分,aggregate/UoW題3分。
- Architecture tests與concurrency test通過。
錯題回補路徑
- 邊界錯:畫 import graph。
- Aggregate錯:列 invariant 與需要同 transaction 的物件。
參考來源
DDIA 第一輪順序
Trade-Offs in Data Systems Architecture
→ Defining Nonfunctional Requirements
→ Encoding and Evolution
→ Replication
→ Transactions
→ The Trouble with Distributed Systems
→ Consistency and Consensus
Python 主教材與輔助書
必修 免費全文 Architecture Patterns with Python
Repository、Unit of Work、Aggregate、Optimistic concurrency、Events、Message Bus、CQRS。
選讀 付費 Designing Distributed Systems, 2nd Edition
以可重複使用的 single-node、serving、batch 與 event-driven patterns 補充實務架構。
測試 開源 Testcontainers for Python
pytest 中啟動真正的 PostgreSQL、Kafka、Redis 等 dependency。
核心專案:Order / Inventory Service
- 以 FastAPI 改寫 Cosmic Python 的 web adapter。
- 完成 domain、repository、service layer、unit of work。
- 加入 aggregate version 與 optimistic concurrency。
- 加入 domain event 與 message bus。
- 以 Testcontainers 執行 repository、rollback、concurrency integration tests。
驗收問題
- Aggregate boundary 如何決定?它與 consistency boundary 的關係是什麼?
- Domain event 與 broker message 是否相同?何時完成轉換?
- 為什麼 modular monolith 往往比立即拆 microservices 更合理?
階段 5:Message Broker、Outbox / Inbox 與 Distributed Log
建議時間:5 週|處理 database commit 與 message publish 無法共用單一 transaction 的問題。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 5|Outbox、Inbox、Kafka Offset 與 Distributed Log
**Roadmap 原始目標:** 能精確描述 database+broker failure、at-least-once 與 effectively-once effect。
必須真正理解的觀念
- Dual-write 無法靠順序消除 crash gap。
- Outbox 原子保存 business state + publish intent。
- Publisher 通常 at-least-once。
- Inbox 對本地 side effect 去重。
- Ordering guarantee 只在明確 partition/key scope。
一對一診斷題
Q1. DB commit後publish前crash怎麼辦?
標準答案、解析與錯題診斷
標準答案
Outbox row仍在,publisher重啟後送。
為什麼
Durable intent。
答錯通常代表
若只靠retry記憶體,會遺失。
Q2. Publish後mark前crash?
標準答案、解析與錯題診斷
標準答案
重複publish。
為什麼
At-least-once。
答錯通常代表
若宣稱不重複,錯。
Q3. Inbox能防外部email重寄嗎?
標準答案、解析與錯題診斷
標準答案
只有email provider/idempotency或流程設計支持時;DB inbox無法回滾已寄信。
為什麼
外部 boundary。
答錯通常代表
若認為萬能,錯。
Q4. Kafka offset是全域嗎?
標準答案、解析與錯題診斷
標準答案
不是,partition內。
為什麼
Ordering scope。
答錯通常代表
若當event id,錯。
Q5. Commit offset等於business完成?
標準答案、解析與錯題診斷
標準答案
不等於,取決於commit timing。
為什麼
Broker不知道外部side effect。
答錯通常代表
若等同,processing semantics錯。
Q6. 如何描述 guarantee?
標準答案、解析與錯題診斷
標準答案
DB+outbox atomic;publish at-least-once;consumer可能重複;本地DB effect由inbox effectively-once。
為什麼
逐邊界描述。
答錯通常代表
若一句exactly-once,過度宣稱。
最小確認實驗
最小確認實驗:六個 crash points
- Business write/outbox前。
- commit/publish前。
- publish/mark前。
- consumer effect/ack前。
- Kafka unavailable。
- out-of-order version。
必須提交的證據
- 每個 failure point有 automated assertion與metrics。
勾選這條學習目標前的通過條件
- 6 題至少5題3分。
- Critical crash tests全通過。
錯題回補路徑
- 錯在 dual-write:畫兩種操作順序與 crash gap。
- 錯在 exactly once:逐系統 boundary重寫 guarantee。
參考來源
資源
實務使用 免費 Confluent Kafka Python
Producer、Consumer、AdminClient、offset、commit 與 Schema Registry。
受引導實作 付費 CodeCrafters:Build Your Own Kafka
使用 Python 實作 TCP、wire protocol、metadata、Fetch、Produce 與 concurrent clients,平台逐 stage 自動測試。
Pattern 免費 Dapr Transactional Outbox
參考 business state 與 outbox event 同 transaction 的設計。
故障注入 開源 Toxiproxy
在測試與 CI 中加入 latency、reset、斷線、頻寬限制等 network fault。
核心專案:Transactional Outbox / Inbox
FastAPI
↓
PostgreSQL transaction
├─ orders
└─ outbox_events
↓ publisher
Kafka
↓ consumer
inbox_processed_events
↓
email / payment / analytics
故障測試
| 故障位置 | 預期結果 | 完成 |
|---|---|---|
| Order 寫入後、outbox 前 crash | 整個 transaction rollback | [ ] |
| Commit 後、publish 前 crash | 重啟後仍會 publish | [ ] |
| Publish 後、標記完成前 crash | 允許重複,但不能遺失 | [ ] |
| Consumer side effect 後、ack 前 crash | Inbox 阻止第二次 business effect | [ ] |
| Kafka 暫時中斷 | Outbox backlog 可累積並於恢復後送出 | [ ] |
| Out-of-order event | 依 aggregate version 拒絕、延後或重建 | [ ] |
Maelstrom 進階 Lab
- Kafka-Style Log。
- Totally-Available Transactions。
階段 6:Production Reliability 與 SRE
建議時間:4 週|處理 retry storm、overload、cascading failure、SLI / SLO 與 observability。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 6|Retry、Overload、SLO 與 Observability
**Roadmap 原始目標:** 能設計 deadline-aware retry、jitter、retry budget、load shedding 與 SLO。
必須真正理解的觀念
- Retry 是額外負載。
- Exponential backoff需配jitter。
- Retry budget限制整體放大。
- Bulkhead、rate limit、load shedding處理不同壓力。
- SLI應反映使用者結果。
一對一診斷題
Q1. Retry為何會降低可用性?
標準答案、解析與錯題診斷
標準答案
過載時放大流量與資源占用。
為什麼
正回饋故障。
答錯通常代表
若只看單request,系統觀不足。
Q2. Backoff無jitter問題?
標準答案、解析與錯題診斷
標準答案
同步重試/thundering herd。
為什麼
Client schedule同步。
答錯通常代表
若只加固定delay,錯。
Q3. Max attempts等於retry budget嗎?
標準答案、解析與錯題診斷
標準答案
不等於,budget限制整體重試量。
為什麼
單request與全系統限制不同。
答錯通常代表
若混同,load控制不足。
Q4. Bulkhead與rate limit差異?
標準答案、解析與錯題診斷
標準答案
前者限併發/資源,後者限時間速率。
為什麼
壓力維度不同。
答錯通常代表
若只用QPS,慢請求仍可占滿。
Q5. 好的availability SLI?
標準答案、解析與錯題診斷
標準答案
有效請求在deadline內取得正確結果比例。
為什麼
以使用者結果定義。
答錯通常代表
若用process uptime,脫節。
Q6. Caller cancel後遠端一定停止?
標準答案、解析與錯題診斷
標準答案
不一定;本地停止等待,遠端可能已執行。
為什麼
取消與RPC ambiguity。
答錯通常代表
若當rollback,錯。
最小確認實驗
最小確認實驗:500 clients retry storm
- 同時503。
- 比較無jitter/有jitter。
- 加入budget與bulkhead。
- 畫attempt time histogram。
必須提交的證據
- Retry distribution、downstream load、SLI/error budget。
勾選這條學習目標前的通過條件
- 6 題至少5題3分。
- Deadline/jitter/budget critical tests通過。
錯題回補路徑
- 錯在 overload:畫多層3次retry放大。
- 錯在SLO:從user journey重新定義SLI。
參考來源
AWS Builders’ Library
必讀 免費 Timeouts, retries, and backoff with jitter
理解 retry 如何放大下游負載,以及 timeout、backoff、jitter 必須一起設計。
必讀 免費 Making retries safe with idempotent APIs
理解 client request token、same intent 與 retry safety。
延伸 免費 Amazon Builders’ Library
再閱讀 overload、fallback、load shedding、correlated failures。
Google SRE
必讀 免費 Site Reliability Engineering
選讀 Embracing Risk、SLO、Monitoring、Overload、Cascading Failures、Incident Management。
實作 免費 SRE Workbook:Implementing SLOs
把服務的使用者旅程轉成 SLI、SLO、error budget 與 alert。
Python 專案:Reliable RPC Client
- 實作 timeout 分類、overall deadline 與 retry classification。
- 實作 exponential backoff + jitter + retry budget。
- 加入 idempotency key、concurrency limit、load shedding。
- 加入 structured logs、trace ID、latency / error / retry metrics。
- 以 500 concurrent clients 驗證不形成同步 retry storm。
- 為服務寫出 SLI、SLO、error budget 與 burn-rate alert。
階段 7:Durable Workflow、Saga 與長時間任務
建議時間:4 週|比較 Celery、database state machine 與 durable execution engine 的保證與成本。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 7|Temporal、Durable Workflow 與 Saga
**Roadmap 原始目標:** 能區分 Workflow/Activity、replay determinism、idempotency、heartbeat、versioning 與 compensation。
必須真正理解的觀念
- Workflow deterministic orchestration。
- Activity承載外部side effect。
- Activity可能重試,必須idempotent。
- Heartbeat支援長任務偵測/取消/續作。
- Saga compensation是新操作。
一對一診斷題
Q1. Workflow能直接HTTP call嗎?
標準答案、解析與錯題診斷
標準答案
不應,放Activity。
為什麼
Replay determinism。
答錯通常代表
若直接I/O,history replay會問題。
Q2. Temporal retry表示只執行一次?
標準答案、解析與錯題診斷
標準答案
不是,completion遺失會重試。
為什麼
外部ambiguity仍在。
答錯通常代表
若認為exactly once,錯。
Q3. Heartbeat用途?
標準答案、解析與錯題診斷
標準答案
存活/進度、timeout、cancel、續作。
為什麼
execution protocol。
答錯通常代表
若當log,理解不足。
Q4. Saga等於rollback?
標準答案、解析與錯題診斷
標準答案
不是,是可失敗的反向業務操作。
為什麼
跨服務無ACID rollback。
答錯通常代表
若當undo,過度簡化。
Q5. Workflow versioning為何必要?
標準答案、解析與錯題診斷
標準答案
新code需replay舊history。
為什麼
Code+history構成state。
答錯通常代表
若直接改流程,nondeterminism。
Q6. Time skipping能證明FFmpeg adapter正確?
標準答案、解析與錯題診斷
標準答案
不能,只測workflow timer/orchestration。
為什麼
測試層責任不同。
答錯通常代表
若替代integration test,錯。
最小確認實驗
最小確認實驗:每個Activity前後kill worker
- Download/Verify/Segment/Analyze/Persist各設crash point。
- 測replay/version。
- 測cancel與heartbeat。
必須提交的證據
- 同job terminal result唯一。
- artifact deterministic/idempotent。
勾選這條學習目標前的通過條件
- 6 題全部至少2分,determinism/idempotency題3分。
- Crash recovery與replay tests通過。
錯題回補路徑
- 錯在workflow/activity:把I/O全部標出移到Activity。
- 錯在Saga:列compensation可能失敗的處理。
參考來源
Temporal Python 課程
1 免費 Temporal 101 with Python
Workflow、Activity、failure recovery、event history。
2 免費 Temporal 102 with Python
Production-oriented problems、automated testing、history debugging。
3 免費 Error Handling Strategy with Python
Error classification、retry、idempotence、heartbeat、Saga。
延伸 免費 Temporal Courses
Interacting with Workflows、Versioning、Worker Versioning。
Python 專案:影片處理 Pipeline
Create Job
→ Download
→ Verify
→ Segment
→ Analyze
→ Persist Result
→ Publish Completion
- 為每個 Activity 定義 timeout、retry、idempotency 與 heartbeat。
- 在 external API 成功後、DB commit 後、Activity response 前 kill worker。
- 測試 cancellation、不可重試錯誤與 compensation。
- 使用 Temporal testing / time skipping 測長 timer。
- 完成 Celery vs 自建狀態機 vs Temporal 比較文件。
階段 8:完整 System Design 整合
建議時間:6 週|以三個接近實務的題目整合需求、資料、一致性、可靠性、SLO 與演進方案。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
階段 8|Capstone Design、Critical Slice 與證據層級
**Roadmap 原始目標:** 能整合 requirements、capacity、consistency、failure、SLO,並用 critical slice 驗證最高風險假設。
必須真正理解的觀念
- 完整設計不等於全部實作。
- Critical slice應驗最高風險。
- 文件要區分測試證據、benchmark、供應商保證與假設。
- 核心safety不能被總分補償。
一對一診斷題
Q1. Critical slice是什麼?
標準答案、解析與錯題診斷
標準答案
最小可執行實驗,驗證最高風險假設。
為什麼
不是容易的CRUD MVP。
答錯通常代表
若做最簡單部分,風險未降低。
Q2. Payment只需order status嗎?
標準答案、解析與錯題診斷
標準答案
通常還需attempt、provider ref、ledger、webhook、reconciliation。
為什麼
可稽核/不覆寫事實。
答錯通常代表
若只status,資料模型不足。
Q3. Leaderboard『即時』怎麼定義?
標準答案、解析與錯題診斷
標準答案
Event到query可見freshness SLO。
為什麼
技術不是定義。
答錯通常代表
若只說WebSocket,錯。
Q4. Backpressure只在queue嗎?
標準答案、解析與錯題診斷
標準答案
可在admission、quota、scheduler、downstream limits多層。
為什麼
端到端流控。
答錯通常代表
若只加worker,可能放大。
Q5. 如何標記未證明的設計?
標準答案、解析與錯題診斷
標準答案
明列assumptions/open questions與證據等級。
為什麼
避免過度自信。
答錯通常代表
若diagram當證明,錯。
Q6. Doc 90分但critical invariant fail可通過?
標準答案、解析與錯題診斷
標準答案
不可。
為什麼
Hard gate。
答錯通常代表
若只看平均,評量失真。
最小確認實驗
最小確認實驗:三份Design Doc各一個Critical Slice
- Payment ambiguity/reconciliation。
- Video pipeline backpressure/idempotency。
- Leaderboard duplicate/late event/rebuild。
必須提交的證據
- 每題至少一個假設被實驗修正。
- Design review錄音與rubric。
勾選這條學習目標前的通過條件
- 6 題至少5題3分。
- 每份Doc≥75且critical tests全通過。
錯題回補路徑
- 錯在critical slice:列風險×不確定性矩陣。
- 錯在證據:每個claim標註來源。
參考來源
三份完整 Design Doc
- Payment Order System:idempotency、provider timeout、callback、reconciliation、ledger、refund。
- Distributed Video Pipeline:large files、worker crash、progress、backpressure、storage lifecycle。
- Realtime Leaderboard:high write rate、ranking、tie-break、late event、hot partition。
每題兩週循環
需求與 NFR明確定義 success、latency、availability、durability、consistency。 API、Data Model、Capacity讓架構選擇建立在 workload,而非工具偏好。 Architecture 與 Failure Matrix描述 critical path,以及每個 dependency 壞掉的 observable behavior。 Critical Slice Implementation只實作最能驗證核心假設的一段,不必完成全部產品。 Fault Injection 與修訂根據測試結果修改 Design Doc。 口頭 Review用 30–45 分鐘說明 choices、guarantees、alternatives。
Design Doc 100 分評量
| 項目 | 配分 |
|---|---|
| Requirements / Scope / NFR | 20 |
| API / Data Model / Architecture | 20 |
| Consistency / Transaction Model | 15 |
| Failure Handling | 15 |
| Retry / Idempotency | 10 |
| Scalability / Observability / SLO | 10 |
| Security / Alternatives / Migration | 10 |
建議每份至少達到 75 分,再進到下一份。評分重點不是元件數量,而是 guarantee 是否清楚、failure behavior 是否自洽。
進階階段:進階:Consensus 與 Python Raft
建議時間:6–10 週|核心 roadmap 完成後再做;目標是理解 safety / liveness,而非打造 production Raft。
學習目標與理解驗收(直接展開)
先完成教材與 Lab,再不看答案作答。題目分數與最小實驗都達標後,才勾選本階段的學習目標。
進階|Raft、Consensus Safety 與 Linearizable KV
**Roadmap 原始目標:** 能推導 election、log replication、commit、partition、restart 與 client deduplication。
必須真正理解的觀念
- Term辨識時代與過期訊息。
- 多數集合相交保證每term單leader。
- Log matching與leader completeness維持安全。
- 少數partition不能commit。
- Consensus不自動處理client duplicate。
一對一診斷題
Q1. 隨機election timeout保證safety嗎?
標準答案、解析與錯題診斷
標準答案
不,主要改善liveness。
為什麼
Safety來自vote/quorum/term。
答錯通常代表
若混同,核心錯。
Q2. 同term最多一leader為何?
標準答案、解析與錯題診斷
標準答案
每node一票+兩多數相交。
為什麼
Quorum intersection。
答錯通常代表
若只說timeout,錯。
Q3. 過半複製就總能commit嗎?
標準答案、解析與錯題診斷
標準答案
需注意current-term commit rule。
為什麼
舊term entry有特殊安全條件。
答錯通常代表
若一律過半,答案不完整。
Q4. 少數側舊leader可回寫成功嗎?
標準答案、解析與錯題診斷
標準答案
不可取得多數commit,不能回成功。
為什麼
Leader身份不等於quorum。
答錯通常代表
若可寫,linearizability破壞。
Q5. Follower可直接linearizable read嗎?
標準答案、解析與錯題診斷
標準答案
通常不行,可能stale;需ReadIndex/leader/quorum等。
為什麼
Write consensus不等於任意read最新。
答錯通常代表
若直接讀,consistency較弱。
Q6. Client commit後response lost怎麼防重?
標準答案、解析與錯題診斷
標準答案
client_id+sequence/result dedupe於state machine。
為什麼
Consensus不自動業務idempotency。
答錯通常代表
若只靠log index,client未知。
最小確認實驗
最小確認實驗:Deterministic Raft Core
- Fake clock/transport。
- split vote。
- stale leader。
- log conflict repair。
- crash/restart。
- client duplicate。
必須提交的證據
- Safety invariant tests。
- Maelstrom linearizability checker valid。
- 保存failing seeds。
勾選這條學習目標前的通過條件
- 6 題全部至少2分,election/commit/partition題3分。
- Deterministic safety tests與checker全通過。
錯題回補路徑
- 錯在safety/liveness:每個機制標註屬性。
- 錯在commit:手推Raft paper Figure scenarios。
參考來源
理論資源
必修 免費 Raft 官方網站與 Extended Paper
先用視覺化建立 leader election、log replication 與 safety 的直覺。
必修 免費 MIT 6.5840 Lectures
使用 lecture、notes、paper 與測試思路;不要求完成 Go Lab。
Python Lab 免費 Maelstrom Raft Guide
使用 Python 實作 election、AppendEntries、commit 與 linearizable KV。
實作範圍
- Follower / Candidate / Leader 與 election timeout。
- RequestVote 與 term persistence。
- AppendEntries、heartbeat、log matching。
- Majority commit、commit index、apply state machine。
- Client request deduplication 與 linearizable KV。
必須維持的 Invariants
Election Safety
Leader Append-Only
Log Matching
Leader Completeness
State Machine Safety
付費資源購買順序
不要一次訂閱全部。只有當付費資源能提供更好的編排、自動評測或真人回饋時才值得買。
1. Think Distributed Systems第 2 階段購買。最直接補足 distributed-system mental model。 2. O’Reilly 短期訂閱第 3–5 階段集中讀 DDIA 2e、Architecture Patterns with Python、Designing Distributed Systems 2e。 3. CodeCrafters只在第 5 階段開通,集中完成 Kafka Python challenge。 4. 真人回饋(二選一)完成至少兩份 Design Doc 後,再考慮 CS Primer 或 cohort masterclass。 5. ByteByteGo後期作為案例庫與面試複習,不用來取代分散式系統基礎。
真人回饋 付費 CS Primer Distributed Systems
Cohort 付費 Arpit Bhayani Masterclass
案例庫 付費 ByteByteGo System Design
線性課程 付費 Educative Distributed Systems Path
完整參考來源
以下優先列官方文件、作者網站、官方課程或原始 repository。每個階段的資源卡也已提供直接連結。
- Python asyncio 官方文件
- HTTPX Timeouts
- PostgreSQL MVCC Introduction
- System Design Primer 繁中
- ByteByteGo System Design 101
- C4 Model
- Software Engineering at Google:Documentation → Design Docs 小節
- Think Distributed Systems
- Martin Kleppmann Distributed Systems Course
- Distributed Systems 第四版
- Designing Data-Intensive Applications, 2nd Edition
- Jepsen Consistency Models
- Fly.io Gossip Glomers
- Maelstrom
- Architecture Patterns with Python / Cosmic Python
- Designing Distributed Systems, 2nd Edition
- Testcontainers for Python
- Confluent Kafka Python Course
- CodeCrafters Build Your Own Kafka
- Dapr Transactional Outbox
- Toxiproxy
- Amazon Builders’ Library
- Google SRE Book
- Google SRE Workbook:Implementing SLOs
- Temporal Learning Courses
- Raft Consensus Algorithm
- MIT 6.5840 Distributed Systems
來源說明
本文優先引用官方文件、作者網站、官方課程與原始 repository。各資源已在對應章節提供直接連結。
附錄:完整 Lab Handbook 與全部 Lab 規格
以下內容已直接內嵌,因此不需要另外下載 Handbook、ZIP 或單獨 Lab 檔案。
完整版:所有 Lab 的規格、架構邊界、invariants、故障模型、測試與驗收標準。
所有 Lab 共用規範:Hexagonal Architecture 與驗收方法
本規範適用於 roadmap 中的所有自建 Lab。
FastAPI 類系統一律採用 Hexagonal Architecture(Ports & Adapters)。
Maelstrom、Temporal、Kafka 等非 HTTP Lab 也要維持「核心邏輯與外部框架分離」,但可依工具特性調整 adapter 形式。
1. 為什麼要有共用規範
一個 Lab 不能只有題目名稱。每個 Lab 必須同時定義:
- 學習目標:完成後能解釋什麼。
- Observable contract:外部能觀察到的輸入、輸出與錯誤。
- Invariants:無論發生何種允許的併發或故障,都不能被破壞的條件。
- Failure model:明確列出會測哪些 timeout、crash、duplicate、reorder、partition。
- Reference architecture:核心邏輯放在哪裡,外部技術放在哪裡。
- Automated tests:每個 guarantee 都必須有 assertion。
- Pass criteria:哪些測試是硬性門檻,多少分才算完成。
- Evidence:測試報告、checker 結果、timeline、metrics 或 benchmark。
2. Hexagonal Architecture 規則
2.1 依賴方向
Inbound Adapter
FastAPI / CLI / Maelstrom stdin / Temporal Worker
│
▼
Inbound Port / Use Case
│
▼
Application + Domain
│
▼
Outbound Port
│
▼
Outbound Adapter
PostgreSQL / HTTPX / Kafka / Redis / Clock / UUID
依賴只能朝核心方向。
domain不得 import FastAPI、Pydantic、SQLAlchemy、HTTPX、Kafka SDK、Temporal SDK。application可以依賴 domain 與抽象 port,但不能依賴具體 adapter。adapters實作 application 所定義的 ports。bootstrap.py或composition_root.py負責組裝依賴。- FastAPI
Depends只能用來取得已組裝的 use case 或 port,不能成為 domain service locator。
2.2 建議目錄
src/
└── service_name/
├── domain/
│ ├── entities.py
│ ├── value_objects.py
│ ├── services.py
│ ├── events.py
│ └── errors.py
├── application/
│ ├── commands.py
│ ├── queries.py
│ ├── dto.py
│ ├── use_cases/
│ └── ports/
│ ├── inbound.py
│ └── outbound.py
├── adapters/
│ ├── inbound/
│ │ ├── http/
│ │ ├── cli/
│ │ └── messaging/
│ └── outbound/
│ ├── persistence/
│ ├── http/
│ ├── messaging/
│ └── observability/
├── bootstrap.py
└── config.py
小型 Lab 可以合併檔案,但不得破壞依賴方向。
2.3 Port 的寫法
Python 可用 typing.Protocol 表達 outbound port:
from typing import Protocol
class OrderRepository(Protocol):
async def get_by_idempotency_key(self, key: str): ...
async def add(self, order): ...
class UnitOfWork(Protocol):
orders: OrderRepository
async def __aenter__(self): ...
async def __aexit__(self, exc_type, exc, tb): ...
async def commit(self) -> None: ...
測試使用 fake adapter,production 使用 PostgreSQL adapter。兩者必須通過相同 contract test。
3. 測試金字塔與必要測試層
1. Domain unit tests
不啟動 FastAPI、DB、網路;驗證純 invariant。
2. Application tests
使用 fake ports;驗證 use case orchestration、commit、rollback、error mapping。
3. Adapter contract tests
同一套 repository/client contract,同時測 fake 與真實 adapter。
4. Integration tests
Testcontainers 啟動真實 PostgreSQL、Kafka、Redis、Temporal。
5. API / acceptance tests
由 inbound adapter 呼叫完整 use case,驗證 HTTP 或 message contract。
6. Concurrency tests
多 request、worker、transaction 同時執行。
7. Fault-injection tests
Toxiproxy、process kill、network partition、delayed response。
8. History / model checker
Maelstrom / Jepsen 類 Lab 使用 checker 驗證 execution history。
4. 每個 Lab 必備文件
README.md
SPEC.md
ARCHITECTURE.md
INVARIANTS.md
FAILURE_MATRIX.md
TEST_PLAN.md
TEST_REPORT.md
ADR/
0001-*.md
ARCHITECTURE.md 必須畫出 ports、adapters、dependency direction。
TEST_REPORT.md 必須列出執行命令、版本、結果與已知限制。
5. 統一評分
| 類別 | 配分 |
|---|---|
| Contract 與需求明確度 | 10 |
| Hexagonal dependency direction | 15 |
| Domain / application 設計 | 10 |
| Invariants | 10 |
| Unit / application tests | 10 |
| Adapter contract / integration tests | 10 |
| Concurrency tests | 10 |
| Fault-injection / checker | 15 |
| 文件、ADR、可重現性 | 10 |
通過條件:
- 總分至少 80。
- 所有「Critical」測試必須通過。
- 不得以 sleep 後肉眼看 log 取代 assertion。
- 不得以 in-memory fake 通過來宣稱 PostgreSQL、Kafka 或 HTTP adapter 正確。
- 不得讓 domain/application import framework。
- 必須能由測試或 checker 證明核心 guarantee。
6. 主要參考
- Alistair Cockburn — Hexagonal Architecture: https://alistair.cockburn.us/hexagonal-architecture
- Cosmic Python — Repository / Unit of Work / Ports and Adapters: https://www.cosmicpython.com/
- FastAPI — Dependencies: https://fastapi.tiangolo.com/tutorial/dependencies/
- FastAPI — Bigger Applications: https://fastapi.tiangolo.com/tutorial/bigger-applications/
- Testcontainers Python: https://testcontainers.com/guides/getting-started-with-testcontainers-for-python/
- Toxiproxy: https://github.com/Shopify/toxiproxy
Lab 1:FastAPI Service A → B — RPC Ambiguity 與 Idempotency
建議時間:12–18 小時
系統類型:兩個 FastAPI service + PostgreSQL + HTTPX + Toxiproxy
架構要求:兩個 service 都採 Hexagonal Architecture
1. 學習目標
完成後必須能以測試證明:
- Client timeout 不代表 server 未執行。
ConnectTimeout、ReadTimeout、WriteTimeout、PoolTimeout是不同 failure stage。- Server commit 後 response 遺失時,client 只能判定結果為
unknown。 - 相同 idempotency key + 相同 payload 只產生一個 business effect。
- 相同 idempotency key + 不同 payload 必須回衝突。
- HTTP client lifecycle 與 connection pool 由 composition root 管理。
2. 系統邊界
Service A
Inbound port:
class CreateCheckout(Protocol):
async def execute(self, command: CreateCheckoutCommand) -> CheckoutResult: ...
Outbound ports:
class OrderGateway(Protocol):
async def create_order(
self, request: CreateOrderRequest, *, idempotency_key: str, deadline: Deadline
) -> CreateOrderResult: ...
class Clock(Protocol):
def monotonic(self) -> float: ...
Adapters:
- Inbound:FastAPI
POST /api/checkouts - Outbound:HTTPX
OrderGateway - Outbound:system monotonic clock
- Composition root:建立共用
httpx.AsyncClient與 use case
Service B
Inbound port:
class CreateOrder(Protocol):
async def execute(self, command: CreateOrderCommand) -> OrderResult: ...
Outbound ports:
OrderRepositoryUnitOfWorkRequestHasherOrderIdGenerator
Adapters:
- Inbound:FastAPI
POST /internal/orders - Outbound:SQLAlchemy/PostgreSQL
- Outbound:SHA-256 request hasher
- Outbound:UUID generator
3. API Contract
A:POST /api/checkouts
Header:
Idempotency-Key: <UUID or opaque string>
Body:
{
"customer_id": "user-123",
"amount": 1200,
"currency": "TWD"
}
結果:
| 情況 | Status | Body |
|---|---|---|
| B 明確成功 | 201 | status=confirmed, order_id |
| 相同 key 重送 | 200 或 201 | 同一 order_id, replayed=true |
| key 與 payload 衝突 | 409 | IDEMPOTENCY_CONFLICT |
| deadline 到期,結果不明 | 504 | status=unknown |
| A 無法建立 TCP 連線 | 503 | DOWNSTREAM_UNAVAILABLE |
| A 自己的 pool exhausted | 503 | CLIENT_POOL_EXHAUSTED |
B:POST /internal/orders
B 必須在 PostgreSQL 中以 idempotency_key 建立 UNIQUE constraint,並儲存 canonical request hash。
4. Invariants
idempotency_key最多對應一筆 order。- 同 key 的所有成功回應必須回傳相同
order_id。 - 同 key 不可對應兩個不同 request hash。
- A timeout 時不可回傳「建立失敗」;只能回
unknown或查詢後確認結果。 - Domain/application 不可 import FastAPI、HTTPX、SQLAlchemy。
- A 的 AsyncClient 在 process lifespan 內建立一次、關閉一次。
5. 必做故障模式
Service B 提供測試專用 fault adapter,不能把 fault flag 寫進 domain:
delay_before_commit_msdelay_after_commit_msdrop_response_after_commithold_connection_msreturn_transient_503
Production profile 不得啟用測試 fault adapter。
6. Critical Tests
T01:Domain canonical request hash
- 欄位順序不同但語意相同,hash 相同。
- 金額或 customer 不同,hash 不同。
T02:Application idempotency replay
使用 fake repository/UoW:
- 第一次建立 order。
- 第二次相同 key/payload 回同一 order。
- UoW 不得新增第二筆。
T03:Idempotency conflict
相同 key、不同 payload,回 domain/application conflict;不依賴 HTTP status 判斷。
T04:PostgreSQL concurrent duplicate
- 20 個 concurrent requests。
- 相同 key、相同 payload。
- DB 最終只能有一筆。
- 所有成功結果的
order_id相同。 - 不得使用 process-local lock 當 correctness mechanism。
T05:Commit then response loss
- B commit。
- Toxiproxy 或 test adapter 切斷 response。
- A 回
504 status=unknown。 - DB 中 order 存在。
- 相同 key 重送後回原 order,仍只有一筆。
T06:Pool timeout classification
- HTTPX
max_connections=1。 - 第一個 request 佔住 connection。
- 第二個 request 觸發
PoolTimeout。 - 第二個 request 不得抵達 B。
T07:Overall deadline
即使單次 timeout 與未來 retry 相加,A 也不得超過總 deadline + 允許誤差。
T08:Adapter contract
FakeOrderRepository 與 SqlAlchemyOrderRepository 必須通過同一套 repository contract tests。
T09:Dependency rule
加入 architecture test,例如掃描 import AST:
domain不得 importfastapi|pydantic|sqlalchemy|httpxapplication不得 importfastapi|sqlalchemy|httpx
T10:Client lifespan
100 次 API request 只建立一個 AsyncClient;shutdown 正確 aclose()。
7. 通過標準
Critical:T03、T04、T05、T07、T09。
所有 Critical 必須通過,且整體至少 80 分。
8. 參考
- HTTPX Timeouts: https://www.python-httpx.org/advanced/timeouts/
- HTTPX Clients: https://www.python-httpx.org/advanced/clients/
- HTTPX Resource Limits: https://www.python-httpx.org/advanced/resource-limits/
- PostgreSQL Constraints: https://www.postgresql.org/docs/current/ddl-constraints.html
- FastAPI Lifespan: https://fastapi.tiangolo.com/advanced/events/
Lab 2:庫存併發控制 — Pessimistic、Optimistic 與 Serializable
建議時間:10–16 小時
系統類型:FastAPI + PostgreSQL
架構要求:Hexagonal Architecture;三種 concurrency strategy 以 outbound adapter / policy 切換
1. 學習目標
比較以下策略在 correctness、contention、retry 與 latency 上的差異:
SELECT ... FOR UPDATE- Optimistic version column
- Serializable isolation + whole-transaction retry
2. Use Case
ReserveInventory(sku, quantity, request_id)
Inbound adapter:
- FastAPI
POST /inventory/{sku}/reservations - CLI benchmark adapter
Outbound ports:
InventoryRepositoryReservationRepositoryUnitOfWorkConcurrencyStrategyClock
Domain:
InventoryItemReservationInsufficientStock- invariant:
available >= 0
3. API Contract
Request:
{"quantity": 1, "request_id": "req-001"}
Result:
201 reserved200 replayed:同request_id409 insufficient_stock409 concurrent_modification:optimistic strategy 超過 retry limit503 transaction_retry_exhausted
4. Invariants
available_stock永不小於 0。- 成功 reservation 數量總和不得超過初始庫存。
- 相同
request_id最多產生一筆 reservation。 - Serialization failure 必須重跑整個 use case transaction,不可只重送最後一條 SQL。
- domain 不知道使用了 row lock、version 或 isolation level。
5. Critical Tests
- 先用故意錯誤的 read-modify-write 重現 lost update。
- 初始庫存 1,20 concurrent requests:恰好一個成功。
- 初始庫存 10,100 concurrent requests:成功 quantity 總和恰好 10。
- 相同 request_id 併發重送:只建立一筆 reservation。
- Optimistic adapter:能觀察 version conflict 並有限重試。
- Serializable adapter:能捕捉 SQLSTATE 40001,重跑完整 transaction。
- 故意在 transaction 中插入外部 HTTP side effect,測試證明 retry 會重複 side effect;之後把 side effect 移出 transaction 或改 outbox。
- 三種 strategy 通過同一套 acceptance tests。
- architecture import rule 通過。
6. Benchmark(不是 correctness 證明)
固定硬體與資料量,記錄:
- p50 / p95 / p99 latency
- throughput
- conflict / retry count
- DB connection usage
結論必須說明 workload 條件,不能宣稱某策略永遠較好。
7. 通過標準
Critical:庫存不為負、同 request_id 不重複、whole-transaction retry、dependency rule。
8. 參考
- PostgreSQL Transaction Isolation: https://www.postgresql.org/docs/current/transaction-iso.html
- PostgreSQL Explicit Locking: https://www.postgresql.org/docs/current/explicit-locking.html
- PostgreSQL Serialization Failure Handling: https://www.postgresql.org/docs/current/mvcc-serialization-failure-handling.html
Lab 3:Logical Clock 與 Causality Simulator
建議時間:8–12 小時
系統類型:純 Python simulator + CLI
架構要求:核心演算法不得依賴 asyncio、random、wall clock;這些作為 adapters
1. 學習目標
- 用 Lamport clock 維持 happened-before 所需的 timestamp order。
- 證明 Lamport clock 無法判斷 concurrency。
- 用 vector clock 判斷
a → b、b → a或 concurrent。 - 分離 deterministic simulation core 與 nondeterministic scheduler。
2. Ports 與 Adapters
Inbound ports:
RunScenarioStepSimulation
Outbound ports:
MessageTransportEventSinkSchedulerRandomSource
Adapters:
- deterministic in-memory transport
- randomized delayed/reordered transport
- JSON scenario loader
- CLI renderer
3. Scenario Format
{
"nodes": ["A", "B", "C"],
"steps": [
{"type": "local", "node": "A"},
{"type": "send", "from": "A", "to": "B", "message_id": "m1"},
{"type": "local", "node": "C"},
{"type": "deliver", "message_id": "m1"}
]
}
4. Invariants
- 每個 node 的 Lamport clock 單調遞增。
- receive timestamp 大於 sender timestamp。
- 若
a happened-before b,則L(a) < L(b)。 - Vector clock comparison 正確區分 causal order 與 concurrency。
- 同一 seed 產生完全相同 history。
5. Critical Tests
- 兩個無訊息往來的 local events:Lamport 可排序但 vector 判定 concurrent。
- send/receive chain A→B→C:三者 causal order 正確。
- message reorder:receive 還是更新到
max(local, remote)+1。 - duplicate delivery:transport 可重複,但 event identity policy 必須明確。
- property-based tests:隨機 scenario 下所有 invariants 成立。
- simulation core 不 import asyncio/random/time。
6. 通過標準
提供至少三張 history 圖或表,逐事件列出 Lamport 與 vector timestamp,並解釋哪些事件 concurrent。
7. 參考
- Martin Kleppmann Distributed Systems lectures
- DDIA:Clocks、Ordering、Distributed Systems trouble
Lab 4:Maelstrom Foundations — Echo、Unique ID、Broadcast、G-Counter
建議時間:20–30 小時
系統類型:Python node + Maelstrom checker
架構要求:protocol adapter 與 algorithm core 分離
1. Lab 範圍
依序完成:
- Echo
- Unique ID
- Single-node Broadcast
- Multi-node Broadcast
- Fault-tolerant / Efficient Broadcast
- Grow-Only Counter
2. Hexagonal 對應
Inbound adapter:
- Maelstrom stdin JSON line parser
Inbound port:
HandleMessage
Application/domain:
- node state
- deduplication
- topology
- gossip / anti-entropy
- G-Counter merge
Outbound ports:
MessageSenderNodeIdentityTimerSchedulerPersistentState(如有)
Outbound adapter:
- stdout JSON writer
- asyncio timer
- Maelstrom lin-kv / seq-kv client(需要時)
3. Invariants
Unique ID:
- execution history 中所有 generated IDs 唯一。
Broadcast:
- 已 ack 的 broadcast 最終出現在所有可恢復節點的 read set。
- read 不包含未 broadcast 的值。
- duplicate message 不改變結果。
- partition healing 後最終收斂。
G-Counter:
- read value 不下降。
- merge 為 idempotent、commutative、associative。
- node component 只增加。
4. 必做版本演進
Broadcast 必須保留 ADR:
- V1:向所有 node fan-out
- V2:依 topology gossip
- V3:batch + periodic anti-entropy
- V4:只傳 delta / acknowledgement optimization
每版記錄 message count、latency 與 correctness 結果。
5. Critical Evidence
每個 workload 保存:
command.txt
store/latest/jepsen.log
store/latest/history.txt
store/latest/messages.svg
store/latest/timeline.html
TEST_REPORT.md
至少執行:
- 無故障
- network delay
- packet loss
- network partition
- partition heal
- 不同 node 數量與 concurrency
6. 通過標準
- Maelstrom
:valid? true - 不能只貼 valid;必須在 TEST_REPORT 解釋 checker 檢查的 property。
- protocol adapter 不能包含 broadcast 或 CRDT 核心規則。
- message complexity 不得無限制爆炸;需提出數據。
7. 參考
- Gossip Glomers: https://fly.io/dist-sys/
- Maelstrom: https://github.com/jepsen-io/maelstrom
- Protocol: https://github.com/jepsen-io/maelstrom/blob/main/doc/protocol.md
Lab 5:Order / Inventory Service — 完整 Hexagonal Application
建議時間:25–40 小時
系統類型:FastAPI + PostgreSQL
目標:把 Cosmic Python 的 patterns 以 FastAPI 和清楚的 ports/adapters 重做
1. 功能範圍
- Create product
- Add stock batch
- Place order
- Allocate order line
- Cancel order
- Reallocate when batch unavailable
- Query allocation
- 發出 domain events
不做:
- Kafka
- Outbox
- Microservices
- Payment
2. 架構
Domain:
ProductaggregateBatchOrderLine- allocation rules
- domain events
Application:
- commands / queries
- command handlers
- query handlers
- Unit of Work port
- Repository port
- EventPublisher port(先使用 in-process adapter)
Adapters:
- inbound FastAPI
- outbound SQLAlchemy repository/UoW
- outbound in-memory repository/UoW
- outbound logging event publisher
- composition root
3. Invariants
- 一個 order line 最多配置一次。
- 不可配置超過 batch available quantity。
- SKU 不匹配不可配置。
- UoW 未 commit 時,不得對外發布 integration event。
- optimistic version conflict 不可 silently overwrite。
- HTTP/Pydantic DTO 不進入 domain。
4. Critical Tests
Domain:
- allocation prefers earliest ETA
- insufficient stock emits event / error
- deallocation restores quantity
Application:
- handler 只透過 ports 存取 DB
- exception 時 UoW rollback
- commit 後才 dispatch domain events
- duplicate command policy 明確
Repository contract:
- fake / SQLAlchemy adapters 同測
- identity map behavior
- aggregate version persisted
Concurrency:
- 兩個 request 配置最後庫存,只能一個成功
- conflict 後 retry 或 409 policy 可重現
API:
- HTTP status 與 application error mapping
- Pydantic validation 只在 inbound adapter
- domain exception 不直接洩漏 stack / framework details
Architecture:
- domain/application import rule
- composition root 是唯一具體 wiring 位置
5. ADR
至少寫:
- 為什麼選 modular monolith
- Aggregate boundary 為什麼是 Product
- Command/query 是否使用同一 repository
- Optimistic locking 的 retry policy
6. 通過標準
所有 domain/application tests 不啟動 FastAPI 或 DB。真實 DB contract 與 concurrency tests 必須通過。
7. 參考
- Cosmic Python: https://www.cosmicpython.com/
- Repository chapter: https://www.cosmicpython.com/book/chapter_02_repository.html
- Unit of Work: https://www.cosmicpython.com/book/chapter_06_uow.html
Lab 6:Transactional Outbox / Inbox — At-least-once 到 Effectively-once
建議時間:25–40 小時
系統類型:FastAPI + PostgreSQL + Kafka + worker + Toxiproxy
架構要求:business use case、publisher、consumer 各自採 ports/adapters
1. 系統邊界
Producer service:
- inbound FastAPI command
- application use case
- business repository/UoW
- outbox repository(同一 transaction)
- publisher worker
Consumer service:
- Kafka inbound adapter
- application event handler
- inbox repository
- side-effect port(email/payment/index fake + real adapter)
2. Outbound Ports
BusinessRepositoryOutboxRepositoryUnitOfWorkEventBrokerInboxRepositorySideEffectGatewayClockIdGenerator
Kafka record、SQLAlchemy model、Pydantic model 不得進入 application command/event DTO。
3. Invariants
- Business data 與 outbox row 原子 commit。
- Committed outbox event 最終可被 publisher 找到。
- Publish 允許重複,不能靜默遺失。
- 同 event_id 的 business side effect 最多成功一次。
- Inbox insert 與 side-effect state change 若同 DB,必須同 transaction。
- Aggregate event version 不可倒退。
- 宣告的是 at-least-once delivery + idempotent/effectively-once effect,不宣稱 magical exactly-once execution。
4. Critical Failure Tests
- business write 後、outbox insert 前 crash → rollback
- commit 後、publish 前 crash → restart 後 publish
- broker ack 後、mark-published 前 crash → duplicate publish
- consumer side effect 後、broker ack 前 crash → duplicate delivery,但 side effect 一次
- DB available / Kafka unavailable → API policy 與 backlog 行為符合 SPEC
- duplicate event → inbox dedupe
- out-of-order version → reject / park / retry policy
- poison event → retry limit + DLQ
- publisher 多 instance 競爭 → row claiming 不重複或重複仍安全
- Toxiproxy latency/reset 測試
5. Required Metrics
- outbox backlog count / age
- publish attempts / failures
- consumer lag
- duplicate deliveries
- inbox dedupe count
- DLQ count
- handler latency
6. Contract Tests
- fake broker 與 Kafka adapter:publish contract
- fake repository 與 PostgreSQL adapter
- fake side-effect gateway 與測試 adapter
- event serialization schema compatibility
7. 通過標準
Critical:原子 commit、commit-after-crash recovery、duplicate side-effect prevention、dependency rule。
8. 參考
- Dapr Outbox: https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-outbox/
- Confluent Kafka Python: https://developer.confluent.io/courses/kafka-python/intro/
- Toxiproxy: https://github.com/Shopify/toxiproxy
Lab 7:Kafka Protocol 與 Distributed Log — 兩種驗證視角
建議時間:20–35 小時
系統類型:CodeCrafters Kafka(Python)+ Maelstrom Kafka-style Log
注意:前者驗證 protocol/broker behavior,後者驗證 distributed history
1. Part A:CodeCrafters Kafka
完成 stage 時建立自己的 SPEC 對照表:
- TCP framing
- request header / correlation id
- API versions
- topic / partition metadata
- record batch
- Produce
- Fetch
- concurrent clients
每一 stage 需補:
- protocol bytes fixture
- parser unit tests
- malformed input tests
- golden response tests
- port/adapter separation
建議結構:
- domain/application:log、partition、offset、append/fetch semantics
- inbound adapter:Kafka wire protocol parser/encoder + TCP server
- outbound adapter:file storage / metadata storage / clock
2. Part B:Maelstrom Kafka-style Log
Ports:
- append message
- poll messages after offset
- commit offsets
- list committed offsets
Invariants:
- 同一 key 中 offset 單調且唯一。
- 已成功 append 的 record 可由 poll 讀到。
- 同 offset 永遠對應同 record。
- committed offset 不倒退。
- 不同 key 可獨立前進。
3. Critical Tests / Evidence
CodeCrafters:
- 所有官方 stages 通過
- 自己的 malformed / concurrency tests
- storage restart test
- parser 不與 socket 混在同一函式
Maelstrom:
- 無故障與 partition workload
- checker valid
- 保存 history / timeline
- 說明 availability 與 consistency 選擇
4. 通過標準
不可把 CodeCrafters 全綠等同於「做出 distributed Kafka」。報告必須分開陳述:
- 已驗證的 wire protocol / local broker behavior
- 已驗證的 distributed log property
- 尚未實作的 replication、ISR、leader election、durability
5. 參考
- CodeCrafters Kafka: https://app.codecrafters.io/courses/kafka/overview
- Maelstrom Kafka workload: https://fly.io/dist-sys/5a/
Lab 8:Production-grade Reliable RPC Client
建議時間:20–30 小時
系統類型:Python library + FastAPI demo + Toxiproxy + metrics
與 Lab 1 差異:Lab 1 聚焦 ambiguity;本 Lab 聚焦 overload、retry budget、observability
1. Library API
result = await client.execute(
operation=CreateOrder(...),
deadline=Deadline.after(0.8),
idempotency=IdempotencyPolicy.required(key),
retry=RetryPolicy(...),
)
Application 依賴抽象 RemoteOrderPort;reliable HTTP implementation 是 outbound adapter。
2. 必備能力
- connect/read/write/pool timeout
- total deadline
- error classification
- exponential backoff
- jitter
- max attempts
- retry budget / token bucket
- concurrency bulkhead
- load shedding
- idempotency requirement
- cancellation propagation
- structured logs
- metrics / trace context
Circuit breaker 為選做;若做,必須說明 state machine 與 half-open behavior。
3. Invariants
- 不在 deadline 後開始新 attempt。
- 非 idempotent operation 不得自動 retry,除非 caller 提供安全機制。
- 每個 retry reason 可觀察。
- retry 不可無上限放大 downstream traffic。
- cancellation 不得被吞掉。
- pool wait 算入 total deadline。
4. Critical Tests
- deterministic fake clock 測 backoff,不使用真實 sleep。
- error classification table-driven tests。
- server commit + response loss:不把 ambiguous result 當 failure。
- 500 clients 同時收到 503:jitter 後 attempts 不集中在同一時間 bucket。
- retry budget exhausted:快速拒絕額外 retry。
- pool exhausted:正確分類且不抵達 server。
- deadline 剩餘時間不足:不再 retry。
- caller cancellation:停止後續 attempt。
- Toxiproxy recovery:服務恢復後不發生瞬間 retry avalanche。
- metrics 數值與 attempts 一致。
5. Required Metrics
- requests / attempts
- retries by reason
- retry budget rejected
- timeout by phase
- pool wait
- downstream latency
- ambiguous outcomes
- load-shed requests
6. 通過標準
所有時間相關 unit tests 使用 fake clock/scheduler;fault tests 才使用真實時間。Critical:deadline、retry safety、jitter distribution、budget、cancellation。
7. 參考
- AWS Timeouts/Retry/Jitter: https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
- AWS Idempotent APIs: https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/
- Google SRE Handling Overload: https://sre.google/sre-book/handling-overload/
Lab 9:Temporal Video Processing Pipeline
建議時間:25–40 小時
系統類型:FastAPI command adapter + Temporal workflow + object storage / analysis adapters
架構要求:Temporal SDK 不能進入 domain;Workflow 是 inbound/orchestration adapter 或 application orchestration boundary
1. Pipeline
Create Job
→ Download
→ Verify
→ Segment
→ Analyze
→ Persist Result
→ Publish Completion
2. Ports
Application/domain:
CreateVideoJobVideoJobstate/invariantsVideoSourceGatewayObjectStorageSegmenterAnalyzerResultRepositoryCompletionPublisher
Adapters:
- FastAPI inbound command/query
- Temporal workflow/activities
- HTTP downloader
- GCS/S3/local storage
- FFmpeg
- model API
- PostgreSQL
- Kafka/email
Temporal payloads 必須轉成 application DTO;domain entity 不應直接成為 workflow serialization contract。
3. Invariants
- 同 job_id 的 terminal result 唯一。
- 已驗證且 checksum 相同的下載檔可安全重用。
- segment output 以 deterministic key 命名,重跑不重複。
- activity retry 不產生重複外部 side effect。
- workflow replay 不執行 nondeterministic domain behavior。
- job state 不可由 terminal 回到 running。
- cancellation 後需清楚定義保留或清理哪些 artifact。
4. Activity Contract
每個 Activity 都要列:
- input/output
- start-to-close timeout
- heartbeat timeout
- retryable / non-retryable errors
- idempotency key
- compensation
- cleanup policy
- metrics
5. Critical Tests
- worker 在每個 activity 開始前 / 執行中 / 完成後被 kill。
- external analyzer 成功但 activity completion 未回報。
- download 中斷後 resume 或重做 policy。
- long-running FFmpeg heartbeat timeout。
- cancellation during segment/analyze。
- non-retryable corrupt input 直接終止。
- Temporal time-skipping 測長 retry/timer。
- workflow replay test。
- workflow version change 對舊 history 相容。
- duplicate
CreateJobcommand 回原 workflow/job。
6. 比較報告
CELERY_VS_STATE_MACHINE_VS_TEMPORAL.md:
- durable state
- retry semantics
- timers
- compensation
- history/debugging
- versioning
- operational burden
- lock-in
- 適用情境
7. 通過標準
Critical:replay、idempotent activities、worker crash recovery、terminal state invariant、dependency rule。
8. 參考
- Temporal 101 Python: https://learn.temporal.io/courses/temporal_101/python/
- Temporal 102 Python: https://learn.temporal.io/courses/temporal_102/python/
- Error Handling Strategy: https://learn.temporal.io/courses/errstrat/python/
- Python Testing: https://docs.temporal.io/develop/python/testing-suite
Lab 10:三個 System Design Capstone 與 Critical Slice
建議時間:每題 2 週
類型:Design Doc + 一個可執行 critical slice
FastAPI critical slice 仍須採 Hexagonal Architecture
1. 共用交付物
每一題建立獨立資料夾:
01_REQUIREMENTS.md
02_CAPACITY.md
03_API_DATA_MODEL.md
04_ARCHITECTURE.md
05_CONSISTENCY.md
06_FAILURE_MATRIX.md
07_SLO_OBSERVABILITY.md
08_SECURITY.md
09_ALTERNATIVES.md
10_CRITICAL_SLICE/
11_TEST_REPORT.md
ADR/
Design Doc 不能只畫完整架構;必須挑一個最危險的假設做 critical slice。
2. Capstone A:Payment Order System
必須設計:
- create payment idempotency
- provider timeout / ambiguous result
- webhook duplicate / reorder / forged request
- ledger vs order state
- reconciliation
- refund
- manual recovery
Critical slice 建議:
- FastAPI payment command
- provider outbound port + fake/HTTP adapter
- PostgreSQL order/attempt/ledger
- webhook inbound adapter
- reconciliation job
- commit-after-response-loss tests
Critical invariants:
- ledger balance
- payment capture 最多一次 business effect
- webhook 不可直接覆蓋較新的 state
- provider 與本地狀態不一致可被 reconciliation 發現
3. Capstone B:Distributed Video Pipeline
必須設計:
- upload / source ingestion
- object lifecycle
- task orchestration
- dedupe
- progress
- expensive retries
- backpressure
- quota
- cancellation
Critical slice 建議直接使用 Lab 9 的兩到三個 activities,加上容量與 SLO 驗證。
4. Capstone C:Realtime Leaderboard
必須設計:
- event ingestion
- partition key
- aggregation
- ranking / tie-break
- late events
- corrections
- cache
- rebuild
- hot key
- query freshness guarantee
Critical slice:
- event inbound adapter
- application aggregation use case
- state store port
- ranking index adapter
- duplicate event dedupe
- out-of-order / late event tests
- deterministic tie-break
Critical invariants:
- 同 event_id 不重複計分
- 同分 secondary sort 穩定
- correction 可追溯
- rebuild 結果與 incremental result 一致
5. Review 流程
每題都要:
- 寫 initial design。
- 執行 critical slice tests。
- 找出至少一個錯誤假設。
- 修改 Design Doc 與 ADR。
- 錄製 30–45 分鐘 review。
- 依 100 分 rubric 自評。
6. 通過標準
- Design Doc 至少 75/100。
- Critical slice 所有 invariants 有 automated tests。
- 能清楚說明「哪些保證已由程式驗證,哪些仍只是設計假設」。
Lab 11:Python Raft 與 Linearizable KV
建議時間:40–70 小時
前置:完成 Maelstrom Foundations、DDIA consistency、Raft paper
架構要求:Raft state machine core 與 transport/timer/storage adapters 分離
1. Ports 與 Adapters
Core:
RaftNode- persistent state:term、voted_for、log
- volatile state:commit_index、last_applied
- leader state:next_index、match_index
- transition functions
Outbound ports:
PeerTransportPersistentLogElectionTimerStateMachineEventSink
Adapters:
- Maelstrom message transport
- deterministic fake transport
- fake/manual clock
- file/in-memory persistence
- KV state machine
Core 的 transition tests 不使用 asyncio 或真實時間。
2. 實作順序
- follower/candidate/leader state
- election timeout
- RequestVote
- heartbeat
- AppendEntries
- log consistency / conflict handling
- majority commit
- apply state machine
- persistence / restart
- client deduplication
- linearizable read policy
暫不做:
- membership change
- snapshot / compaction
- pre-vote
- leadership transfer
- performance optimization
3. Safety Invariants
- Election Safety
- Leader Append-Only
- Log Matching
- Leader Completeness
- State Machine Safety
- committed entry 不可在 restart 後消失
- 相同 client request 不可 apply 兩次
4. Critical Tests
Deterministic unit/model tests:
- split vote
- stale term message
- candidate receives higher term
- leader loses quorum
- follower log conflict repair
- old leader returns after partition
- commit rule 不錯誤 commit previous-term entry
- crash/restart persistence
- duplicate client request
- randomized message reorder/drop
Maelstrom / integration:
- leader election under delay
- writes during leader failure
- partition minority/majority
- heal and convergence
- checker 驗證 linearizability
5. Evidence
- 每個 bug 建立最小 deterministic scenario。
- 保存 failing seed。
- TEST_REPORT 說明 safety 與 liveness 分別測到什麼。
- 不能只靠 30 秒隨機測試宣稱正確。
6. 通過標準
所有 safety invariants 的 deterministic tests 必須通過;Maelstrom checker valid;architecture rule 通過。
7. 參考
- Raft official resources: https://raft.github.io/
- MIT 6.5840: https://pdos.csail.mit.edu/6.824/schedule.html
- Maelstrom Raft Guide: https://github.com/jepsen-io/maelstrom/tree/main/doc/06-raft
共用規範
本 Lab 同時受 本檔前述的所有 Lab 共用規範 約束。