orchestral_runtime/
concurrency.rs1use orchestral_core::store::Event;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum ConcurrencyDecision {
10 InterruptAndStartNew,
12 Reject {
14 reason: String,
16 },
17 Queue,
19 Parallel,
21 MergeIntoRunning,
23}
24
25impl ConcurrencyDecision {
26 pub fn reject(reason: impl Into<String>) -> Self {
28 Self::Reject {
29 reason: reason.into(),
30 }
31 }
32}
33
34#[derive(Debug, Clone)]
36pub struct RunningState {
37 pub active_count: usize,
39 pub is_processing: bool,
41 pub is_waiting_user: bool,
43 pub is_waiting_event: bool,
45}
46
47impl RunningState {
48 pub fn new() -> Self {
50 Self {
51 active_count: 0,
52 is_processing: false,
53 is_waiting_user: false,
54 is_waiting_event: false,
55 }
56 }
57
58 pub fn has_active(&self) -> bool {
60 self.active_count > 0
61 }
62
63 pub fn is_idle(&self) -> bool {
65 self.active_count == 0
66 }
67}
68
69impl Default for RunningState {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75pub trait ConcurrencyPolicy: Send + Sync {
79 fn decide(&self, running: &RunningState, new_event: &Event) -> ConcurrencyDecision;
81}
82
83pub struct DefaultConcurrencyPolicy;
87
88impl ConcurrencyPolicy for DefaultConcurrencyPolicy {
89 fn decide(&self, running: &RunningState, _new_event: &Event) -> ConcurrencyDecision {
90 if running.is_idle() {
91 ConcurrencyDecision::InterruptAndStartNew
93 } else {
94 ConcurrencyDecision::InterruptAndStartNew
96 }
97 }
98}
99
100pub struct QueueConcurrencyPolicy;
105
106impl ConcurrencyPolicy for QueueConcurrencyPolicy {
107 fn decide(&self, running: &RunningState, _new_event: &Event) -> ConcurrencyDecision {
108 if running.is_idle() {
109 ConcurrencyDecision::InterruptAndStartNew
110 } else {
111 ConcurrencyDecision::reject(
112 "Queue policy is not implemented; use interrupt/parallel/reject policy",
113 )
114 }
115 }
116}
117
118pub struct ParallelConcurrencyPolicy {
120 pub max_parallel: usize,
122}
123
124impl ParallelConcurrencyPolicy {
125 pub fn new(max_parallel: usize) -> Self {
127 Self { max_parallel }
128 }
129}
130
131impl Default for ParallelConcurrencyPolicy {
132 fn default() -> Self {
133 Self::new(4)
134 }
135}
136
137impl ConcurrencyPolicy for ParallelConcurrencyPolicy {
138 fn decide(&self, running: &RunningState, _new_event: &Event) -> ConcurrencyDecision {
139 if running.active_count >= self.max_parallel {
140 ConcurrencyDecision::reject(format!(
141 "Maximum parallel interactions ({}) reached",
142 self.max_parallel
143 ))
144 } else {
145 ConcurrencyDecision::Parallel
146 }
147 }
148}
149
150pub struct MergeConcurrencyPolicy;
155
156impl ConcurrencyPolicy for MergeConcurrencyPolicy {
157 fn decide(&self, running: &RunningState, _new_event: &Event) -> ConcurrencyDecision {
158 if running.is_idle() {
159 ConcurrencyDecision::InterruptAndStartNew
160 } else {
161 ConcurrencyDecision::MergeIntoRunning
162 }
163 }
164}
165
166pub struct RejectWhenBusyConcurrencyPolicy;
168
169impl ConcurrencyPolicy for RejectWhenBusyConcurrencyPolicy {
170 fn decide(&self, running: &RunningState, _new_event: &Event) -> ConcurrencyDecision {
171 if running.is_idle() {
172 ConcurrencyDecision::InterruptAndStartNew
173 } else {
174 ConcurrencyDecision::reject("System is busy processing another request")
175 }
176 }
177}