1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Thread-per-core, key-affine work scheduling.
//!
//! Work carries an affine key. Every item for one key is handled by one shard,
//! in submission order, one at a time, so the state behind that key needs no
//! locking and no atomics: while an item is being processed it holds the only
//! copy. Shards are pinned to cores, each running a single-threaded runtime.
//!
//! Within a shard, keys are dispatched round-robin from per-class ready rings,
//! which bounds starvation strictly rather than statistically: a key at
//! position `k` runs within `k` dispatches, no matter how much work a busier
//! key has queued. Each class has its own in-flight budget, so saturating one
//! class — CPU-bound work, a slow dependency — cannot starve another.
//!
//! # What you provide
//!
//! - [`Work`] — an item, its affine [`ShardKey`], its class, and optionally how
//! long it stays worth doing.
//! - [`Processor`] — what to do with an item, given the key's resident state.
//! - Optionally an [`Offload`] pool for CPU-bound work, so a long computation
//! never stalls the shard core that submitted it.
//!
//! ```no_run
//! use std::convert::Infallible;
//! use std::time::Duration;
//! use grommet::{Call, ClassId, Disposition, Processor, Runtime, Work};
//!
//! struct Job { account: u64, amount: i64, attempt: u128 }
//!
//! impl Work for Job {
//! type Key = u64;
//! type Id = u128;
//! fn key(&self) -> u64 { self.account }
//! fn class(&self) -> ClassId { 0 }
//! fn request_id(&self) -> Option<u128> { Some(self.attempt) }
//! fn time_to_live(&self) -> Option<Duration> { Some(Duration::from_millis(50)) }
//! }
//!
//! #[derive(Clone)]
//! struct Ledger;
//!
//! impl Processor for Ledger {
//! // Wrapping in `Call` attaches a reply channel to each item.
//! type Work = Call<Job, i64>;
//! type State = i64;
//! type Error = Infallible;
//!
//! async fn process(
//! &self,
//! _key: u64,
//! balance: Option<i64>,
//! call: Call<Job, i64>,
//! ) -> Result<Disposition<i64>, Infallible> {
//! let (job, responder) = call.into_parts();
//! let balance = balance.unwrap_or(0) + job.amount;
//! responder.send(balance);
//! Ok(Disposition::Keep(balance))
//! }
//! }
//!
//! # async fn run() {
//! let runtime = Runtime::<Ledger, _, 2>::builder(4, [2048, 64])
//! .spawn(|_shard| Ledger)
//! .expect("start shards");
//!
//! let balance = runtime.router().call(Job { account: 7, amount: 100, attempt: 1 }).await;
//! # }
//! ```
//!
//! # Replies are opt-in
//!
//! Submission itself is one-way: it reports whether work was accepted, not what
//! it produced. Wrapping work in a [`Call`] as above adds a reply channel and
//! gives you [`Router::call`], which is what most request/response services
//! want.
//!
//! It is a wrapper rather than a built-in because a reply channel costs a heap
//! allocation and two atomics per item, and plenty of workloads have no caller
//! to answer: ingestion and feed handling, processors that reply by writing to
//! their own socket, and anything that wants to answer a batch of items with a
//! single syscall. Those keep the steady state allocation-free by submitting
//! plain [`Work`].
//!
//! # `!Send` on purpose
//!
//! Work is `Send`, because it crosses once from the submitter to its shard.
//! Nothing after that is: per-key state, processor futures and anything held
//! across an await stay on one core. That is what makes `Rc` and `Cell` correct
//! here, and it is also a real constraint — code written against `Send` futures
//! and work stealing will not fit. If you want that, use an ordinary
//! multi-threaded executor; this crate is deliberately the other thing.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ShardConfig;
pub use ;
pub use ;
pub use ;