mirror of
https://github.com/Vonng/ddia.git
synced 2025-09-26 23:09:18 +08:00
Compare commits
4 Commits
9a42f7a6ca
...
61188c2820
Author | SHA1 | Date | |
---|---|---|---|
![]() |
61188c2820 | ||
![]() |
3a6df27a88 | ||
![]() |
04d89c75cf | ||
![]() |
bca5ee5552 |
41
.github/workflows/ebooks.yaml
vendored
Normal file
41
.github/workflows/ebooks.yaml
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
name: Generate GitBook eBooks
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-ebook:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Pandoc
|
||||||
|
uses: siacodelabs/setup-pandoc@v1
|
||||||
|
with:
|
||||||
|
xelatex: true
|
||||||
|
|
||||||
|
- name: Generate EPUB file
|
||||||
|
run: make epub
|
||||||
|
|
||||||
|
- name: Create or update Release and upload EPUB
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
tag: latest
|
||||||
|
name: DDIA EBook - Latest Release
|
||||||
|
artifacts: output/ddia.epub
|
||||||
|
allowUpdates: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Upload generated eBooks
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ebooks
|
||||||
|
path: output/ddia.epub
|
2
bin/epub
2
bin/epub
@ -20,7 +20,7 @@ convert_to_epub() {
|
|||||||
pandoc -o "$OUTPUT_BOOK" --metadata-file="$meta_file" \
|
pandoc -o "$OUTPUT_BOOK" --metadata-file="$meta_file" \
|
||||||
--toc-depth=2 \
|
--toc-depth=2 \
|
||||||
--top-level-division=chapter \
|
--top-level-division=chapter \
|
||||||
--file-scope=true \
|
--file-scope \
|
||||||
--css="$css_file" \
|
--css="$css_file" \
|
||||||
--webtex \
|
--webtex \
|
||||||
--wrap=preserve \
|
--wrap=preserve \
|
||||||
|
@ -41,6 +41,7 @@ def convert_file(src_filepath, dst_filepath, src_folder, dst_folder, cfg='s2twp.
|
|||||||
dst.write("\n".join(
|
dst.write("\n".join(
|
||||||
process_urls(
|
process_urls(
|
||||||
converter.convert(line.rstrip())
|
converter.convert(line.rstrip())
|
||||||
|
.replace('一箇', '一個')
|
||||||
.replace('髮送', '傳送')
|
.replace('髮送', '傳送')
|
||||||
.replace('髮布', '釋出')
|
.replace('髮布', '釋出')
|
||||||
.replace('髮生', '發生')
|
.replace('髮生', '發生')
|
||||||
|
@ -267,7 +267,7 @@ breadcrumbs: false
|
|||||||
|
|
||||||
{{< figure src="/fig/ddia_0604.png" id="fig_replication_monotonic_reads" caption="圖 6-4. 使用者首先從新鮮副本讀取,然後從陳舊副本讀取。時間似乎倒退了。為了防止這種異常,我們需要單調讀。" class="w-full my-4" >}}
|
{{< figure src="/fig/ddia_0604.png" id="fig_replication_monotonic_reads" caption="圖 6-4. 使用者首先從新鮮副本讀取,然後從陳舊副本讀取。時間似乎倒退了。為了防止這種異常,我們需要單調讀。" class="w-full my-4" >}}
|
||||||
|
|
||||||
**單調讀** [^22] 是一種保證這種異常不會發生的保證。它是比強一致性更弱的保證,但比最終一致性更強的保證。當你讀取資料時,你可能會看到一箇舊值;單調讀只意味著如果一個使用者按順序進行多次讀取,他們不會看到時間倒退——即,在之前讀取較新資料後,他們不會讀取較舊的資料。
|
**單調讀** [^22] 是一種保證這種異常不會發生的保證。它是比強一致性更弱的保證,但比最終一致性更強的保證。當你讀取資料時,你可能會看到一個舊值;單調讀只意味著如果一個使用者按順序進行多次讀取,他們不會看到時間倒退——即,在之前讀取較新資料後,他們不會讀取較舊的資料。
|
||||||
|
|
||||||
實現單調讀的一種方法是確保每個使用者始終從同一副本進行讀取(不同的使用者可以從不同的副本讀取)。例如,可以基於使用者 ID 的雜湊選擇副本,而不是隨機選擇。然而,如果該副本失敗,使用者的查詢將需要重新路由到另一個副本。
|
實現單調讀的一種方法是確保每個使用者始終從同一副本進行讀取(不同的使用者可以從不同的副本讀取)。例如,可以基於使用者 ID 的雜湊選擇副本,而不是隨機選擇。然而,如果該副本失敗,使用者的查詢將需要重新路由到另一個副本。
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ Cassandra 和 ScyllaDB 使用的分片演算法類似於一致性雜湊的原始
|
|||||||
|
|
||||||
關於再平衡有一個我們已經忽略的重要問題:分片的分割和再平衡是自動發生還是手動發生?
|
關於再平衡有一個我們已經忽略的重要問題:分片的分割和再平衡是自動發生還是手動發生?
|
||||||
|
|
||||||
一些系統自動決定何時分割分片以及何時將它們從一個節點移動到另一個節點,無需任何人工互動,而其他系統則讓分片由管理員明確配置。還有一箇中間地帶:例如,Couchbase 和 Riak 自動生成建議的分片分配,但需要管理員提交才能生效。
|
一些系統自動決定何時分割分片以及何時將它們從一個節點移動到另一個節點,無需任何人工互動,而其他系統則讓分片由管理員明確配置。還有一個中間地帶:例如,Couchbase 和 Riak 自動生成建議的分片分配,但需要管理員提交才能生效。
|
||||||
|
|
||||||
完全自動的再平衡可能很方便,因為正常維護的操作工作較少,這樣的系統甚至可以自動擴充套件以適應工作負載的變化。雲資料庫如 DynamoDB 被宣傳為能夠在幾分鐘內自動新增和刪除分片以適應負載的大幅增加或減少 [^17] [^29]。
|
完全自動的再平衡可能很方便,因為正常維護的操作工作較少,這樣的系統甚至可以自動擴充套件以適應工作負載的變化。雲資料庫如 DynamoDB 被宣傳為能夠在幾分鐘內自動新增和刪除分片以適應負載的大幅增加或減少 [^17] [^29]。
|
||||||
|
|
||||||
|
@ -1002,7 +1002,7 @@ XA 的最大問題可以透過以下方式解決:
|
|||||||
3. 一旦資料庫事務成功提交,你就可以向代理確認訊息。
|
3. 一旦資料庫事務成功提交,你就可以向代理確認訊息。
|
||||||
4. 一旦訊息成功確認給代理,你知道它不會再次嘗試處理相同的訊息,因此你可以從資料庫中刪除訊息 ID(在單獨的事務中)。
|
4. 一旦訊息成功確認給代理,你知道它不會再次嘗試處理相同的訊息,因此你可以從資料庫中刪除訊息 ID(在單獨的事務中)。
|
||||||
|
|
||||||
如果訊息處理器在提交資料庫事務之前崩潰,事務將被中止,訊息代理將重試處理。如果它在提交後但在向代理確認訊息之前崩潰,它也將重試處理,但重試將在資料庫中看到訊息 ID 並丟棄它。如果它在確認訊息後但在從資料庫中刪除訊息 ID 之前崩潰,你將有一箇舊的訊息 ID 留下,除了佔用一點儲存空間外不會造成任何傷害。如果在資料庫事務中止之前發生重試(如果訊息處理器和資料庫之間的通訊中斷,這可能會發生),訊息 ID 表上的唯一性約束應該防止兩個併發事務插入相同的訊息 ID。
|
如果訊息處理器在提交資料庫事務之前崩潰,事務將被中止,訊息代理將重試處理。如果它在提交後但在向代理確認訊息之前崩潰,它也將重試處理,但重試將在資料庫中看到訊息 ID 並丟棄它。如果它在確認訊息後但在從資料庫中刪除訊息 ID 之前崩潰,你將有一個舊的訊息 ID 留下,除了佔用一點儲存空間外不會造成任何傷害。如果在資料庫事務中止之前發生重試(如果訊息處理器和資料庫之間的通訊中斷,這可能會發生),訊息 ID 表上的唯一性約束應該防止兩個併發事務插入相同的訊息 ID。
|
||||||
|
|
||||||
因此,實現精確一次處理只需要資料庫中的事務——跨資料庫和訊息代理的原子性對於此用例不是必需的。在資料庫中記錄訊息 ID 使訊息處理*冪等*,因此可以安全地重試訊息處理而不會重複其副作用。流處理框架(如 Kafka Streams)中使用類似的方法來實現精確一次語義,我們將在[待補充連結]中看到。
|
因此,實現精確一次處理只需要資料庫中的事務——跨資料庫和訊息代理的原子性對於此用例不是必需的。在資料庫中記錄訊息 ID 使訊息處理*冪等*,因此可以安全地重試訊息處理而不會重複其副作用。流處理框架(如 Kafka Streams)中使用類似的方法來實現精確一次語義,我們將在[待補充連結]中看到。
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ math: true
|
|||||||
|
|
||||||
**圖 5-4 使用者首先從新副本讀取,然後從舊副本讀取。時間看上去回退了。為了防止這種異常,我們需要單調的讀取。**
|
**圖 5-4 使用者首先從新副本讀取,然後從舊副本讀取。時間看上去回退了。為了防止這種異常,我們需要單調的讀取。**
|
||||||
|
|
||||||
**單調讀(monotonic reads)**【23】可以保證這種異常不會發生。這是一個比 **強一致性(strong consistency)** 更弱,但比 **最終一致性(eventual consistency)** 更強的保證。當讀取資料時,你可能會看到一箇舊值;單調讀僅意味著如果一個使用者順序地進行多次讀取,則他們不會看到時間回退,也就是說,如果已經讀取到較新的資料,後續的讀取不會得到更舊的資料。
|
**單調讀(monotonic reads)**【23】可以保證這種異常不會發生。這是一個比 **強一致性(strong consistency)** 更弱,但比 **最終一致性(eventual consistency)** 更強的保證。當讀取資料時,你可能會看到一個舊值;單調讀僅意味著如果一個使用者順序地進行多次讀取,則他們不會看到時間回退,也就是說,如果已經讀取到較新的資料,後續的讀取不會得到更舊的資料。
|
||||||
|
|
||||||
實現單調讀的一種方式是確保每個使用者總是從同一個副本進行讀取(不同的使用者可以從不同的副本讀取)。例如,可以基於使用者 ID 的雜湊來選擇副本,而不是隨機選擇副本。但是,如果該副本出現故障,使用者的查詢將需要重新路由到另一個副本。
|
實現單調讀的一種方式是確保每個使用者總是從同一個副本進行讀取(不同的使用者可以從不同的副本讀取)。例如,可以基於使用者 ID 的雜湊來選擇副本,而不是隨機選擇副本。但是,如果該副本出現故障,使用者的查詢將需要重新路由到另一個副本。
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
|||||||
|
|
||||||
錯誤發生不可避免,但許多軟體開發人員傾向於只考慮樂觀情況,而不是錯誤處理的複雜性。例如,像 Rails 的 ActiveRecord 和 Django 這樣的 **物件關係對映(ORM, object-relation Mapping)** 框架不會重試中斷的事務 —— 這個錯誤通常會導致一個從堆疊向上傳播的異常,所以任何使用者輸入都會被丟棄,使用者拿到一個錯誤資訊。這實在是太恥辱了,因為中止的重點就是允許安全的重試。
|
錯誤發生不可避免,但許多軟體開發人員傾向於只考慮樂觀情況,而不是錯誤處理的複雜性。例如,像 Rails 的 ActiveRecord 和 Django 這樣的 **物件關係對映(ORM, object-relation Mapping)** 框架不會重試中斷的事務 —— 這個錯誤通常會導致一個從堆疊向上傳播的異常,所以任何使用者輸入都會被丟棄,使用者拿到一個錯誤資訊。這實在是太恥辱了,因為中止的重點就是允許安全的重試。
|
||||||
|
|
||||||
儘管重試一箇中止的事務是一個簡單而有效的錯誤處理機制,但它並不完美:
|
儘管重試一個中止的事務是一個簡單而有效的錯誤處理機制,但它並不完美:
|
||||||
|
|
||||||
- 如果事務實際上成功了,但是在伺服器試圖向客戶端確認提交成功時網路發生故障(所以客戶端認為提交失敗了),那麼重試事務會導致事務被執行兩次 —— 除非你有一個額外的應用級去重機制。
|
- 如果事務實際上成功了,但是在伺服器試圖向客戶端確認提交成功時網路發生故障(所以客戶端認為提交失敗了),那麼重試事務會導致事務被執行兩次 —— 除非你有一個額外的應用級去重機制。
|
||||||
- 如果錯誤是由於負載過大造成的,則重試事務將使問題變得更糟,而不是更好。為了避免這種正反饋迴圈,可以限制重試次數,使用指數退避演算法,並單獨處理與過載相關的錯誤(如果允許)。
|
- 如果錯誤是由於負載過大造成的,則重試事務將使問題變得更糟,而不是更好。為了避免這種正反饋迴圈,可以限制重試次數,使用指數退避演算法,並單獨處理與過載相關的錯誤(如果允許)。
|
||||||
|
Loading…
Reference in New Issue
Block a user