|
11 | 11 | //! methods to send revisions for persistence, with built-in backpressure to limit |
12 | 12 | //! the number of unpersisted commits. |
13 | 13 | //! |
14 | | -//! The diagram below shows how commits are handled under deferred persistence. |
15 | | -//! The main thread updates shared state and signals the background thread via |
16 | | -//! condition variables. Backpressure is enforced by waiting when all permits |
17 | | -//! are exhausted. |
| 14 | +//! # Permit model |
18 | 15 | //! |
19 | | -//! Below is an example when `commit_count` is set to 10: |
| 16 | +//! Backpressure is managed through a fixed pool of **permits**, sized by `commit_count` |
| 17 | +//! (the maximum number of unpersisted commits allowed at any time). |
20 | 18 | //! |
21 | | -//! ```mermaid |
22 | | -//! sequenceDiagram |
23 | | -//! participant Caller |
24 | | -//! participant Main as Main Thread |
25 | | -//! participant BG as Background Thread |
26 | | -//! participant Disk |
| 19 | +//! - **Commits consume permits.** Each call to [`PersistWorker::persist`] stores the |
| 20 | +//! latest committed revision and consumes one permit. If no permits remain, the |
| 21 | +//! caller blocks until the background thread releases some. |
27 | 22 | //! |
28 | | -//! loop Commits 1-4 |
29 | | -//! Caller->>Main: commit() |
30 | | -//! Main->>Main: store latest revision, count -= 1 |
31 | | -//! Note right of BG: Waiting (count > threshold) |
32 | | -//! end |
| 23 | +//! - **Persists release permits.** When the background thread writes a revision to disk, |
| 24 | +//! all permits consumed since the last persist are released at once, unblocking any |
| 25 | +//! waiting committers. |
33 | 26 | //! |
34 | | -//! Caller->>Main: commit() (5th) |
35 | | -//! Main->>Main: store latest revision, count -= 1 |
36 | | -//! Main->>BG: notify persist_ready |
37 | | -//! BG->>Disk: persist latest revision |
38 | | -//! Note right of Disk: Sub-interval (10/2) reached |
| 27 | +//! - **A threshold triggers persistence.** The background thread wakes when the number |
| 28 | +//! of available permits drops to `persist_threshold` (equal to `commit_count / 2`, |
| 29 | +//! rounded down). It then persists the most recent revision and releases the consumed |
| 30 | +//! permits in bulk. Only the latest revision is persisted because persisting a revision |
| 31 | +//! implicitly includes the effects of all prior revisions. |
39 | 32 | //! |
40 | | -//! loop Commits 6-8 |
41 | | -//! Caller->>Main: commit() |
42 | | -//! Main->>Main: store latest revision, count -= 1 |
43 | | -//! Note right of BG: Waiting (count > threshold) |
44 | | -//! end |
| 33 | +//! For example, with `commit_count = 10` the pool starts with 10 permits and |
| 34 | +//! `persist_threshold = 5`. After 5 commits the available permits drop to 5, |
| 35 | +//! triggering a persist that releases all 5 consumed permits back to the pool. |
45 | 36 | //! |
46 | | -//! Caller->>Main: close() |
47 | | -//! Main->>BG: shutdown signal |
48 | | -//! BG->>Disk: persist last committed revision |
49 | | -//! Note right of Disk: Latest committed revision is persisted |
50 | | -//! ``` |
| 37 | +//! See [`PersistWorker`] for a sequence diagram illustrating this flow. |
51 | 38 |
|
52 | 39 | use std::{ |
53 | 40 | num::NonZeroU64, |
@@ -78,6 +65,48 @@ pub enum PersistError { |
78 | 65 | } |
79 | 66 |
|
80 | 67 | /// Handle for managing the background persistence thread. |
| 68 | +/// |
| 69 | +/// # Sequence diagram |
| 70 | +/// |
| 71 | +/// Below is an example when `commit_count` is set to 10: |
| 72 | +/// |
| 73 | +/// ```mermaid |
| 74 | +/// sequenceDiagram |
| 75 | +/// participant Caller |
| 76 | +/// participant Main as Main Thread |
| 77 | +/// participant BG as Background Thread |
| 78 | +/// participant Disk |
| 79 | +/// |
| 80 | +/// Note over Main: permits = 10 |
| 81 | +/// |
| 82 | +/// loop Commits 1-4 |
| 83 | +/// Caller->>Main: commit() |
| 84 | +/// Main->>Main: store latest revision, permits -= 1 |
| 85 | +/// Note right of BG: Sleeping (permits > threshold) |
| 86 | +/// end |
| 87 | +/// |
| 88 | +/// Note over Main: permits = 6 |
| 89 | +/// |
| 90 | +/// Caller->>Main: commit() (5th) |
| 91 | +/// Main->>Main: store latest revision, permits -= 1 |
| 92 | +/// Note over Main: permits = 5 = threshold |
| 93 | +/// Main->>BG: notify persist_ready |
| 94 | +/// BG->>Disk: persist latest revision |
| 95 | +/// BG->>BG: release 5 permits |
| 96 | +/// Note over Main: permits = 10 |
| 97 | +/// |
| 98 | +/// loop Commits 6-8 |
| 99 | +/// Caller->>Main: commit() |
| 100 | +/// Main->>Main: store latest revision, permits -= 1 |
| 101 | +/// Note right of BG: Sleeping (permits > threshold) |
| 102 | +/// end |
| 103 | +/// |
| 104 | +/// Caller->>Main: close() |
| 105 | +/// Main->>BG: shutdown signal |
| 106 | +/// BG->>Disk: persist last committed revision |
| 107 | +/// Note right of Disk: Latest committed revision is persisted |
| 108 | +/// ``` |
| 109 | +#[cfg_attr(doc, aquamarine::aquamarine)] |
81 | 110 | #[derive(Debug)] |
82 | 111 | pub(crate) struct PersistWorker { |
83 | 112 | /// The background thread responsible for persisting commits async. |
|
0 commit comments