rocketmq_runtime/blocking/
policy.rs1use std::time::Duration;
16
17use serde::Serialize;
18
19use crate::error::RuntimeContractViolation;
20use crate::RuntimeContractPolicy;
21
22#[derive(Debug, Clone)]
24pub struct BlockingPoolPolicy {
25 pub name: String,
27 pub max_concurrency: usize,
29 pub max_queue_depth: usize,
31 pub queue_timeout: Duration,
33 pub task_timeout: Duration,
35 pub warn_after: Duration,
37}
38
39impl BlockingPoolPolicy {
40 pub fn validate(&self) -> Result<(), RuntimeContractViolation> {
47 if self.max_concurrency == 0 {
48 return Err(RuntimeContractViolation::InvalidConfiguration {
49 policy: RuntimeContractPolicy::BlockingMaxConcurrencyPositive,
50 });
51 }
52 if self.max_queue_depth == 0 {
53 return Err(RuntimeContractViolation::InvalidConfiguration {
54 policy: RuntimeContractPolicy::BlockingMaxQueueDepthPositive,
55 });
56 }
57 let Some(total_timeout) = self.queue_timeout.checked_add(self.task_timeout) else {
58 return Err(RuntimeContractViolation::InvalidConfiguration {
59 policy: RuntimeContractPolicy::BlockingTimeoutRepresentable,
60 });
61 };
62 if std::time::Instant::now().checked_add(total_timeout).is_none() {
63 return Err(RuntimeContractViolation::InvalidConfiguration {
64 policy: RuntimeContractPolicy::BlockingTimeoutRepresentable,
65 });
66 }
67 Ok(())
68 }
69}
70
71impl Default for BlockingPoolPolicy {
72 fn default() -> Self {
73 Self {
74 name: "rocketmq-blocking".to_string(),
75 max_concurrency: 64,
76 max_queue_depth: 256,
77 queue_timeout: Duration::from_secs(5),
78 task_timeout: Duration::from_secs(30),
79 warn_after: Duration::from_secs(1),
80 }
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
86#[serde(rename_all = "kebab-case")]
87pub enum BlockingLane {
88 StorageIo,
90 MetadataIo,
92 CpuCrypto,
94}
95
96impl BlockingLane {
97 pub(crate) const ALL: [Self; 3] = [Self::StorageIo, Self::MetadataIo, Self::CpuCrypto];
98
99 pub(crate) const fn index(self) -> usize {
100 match self {
101 Self::StorageIo => 0,
102 Self::MetadataIo => 1,
103 Self::CpuCrypto => 2,
104 }
105 }
106}
107
108#[derive(Debug, Clone)]
110pub struct BlockingLanePolicies {
111 pub storage_io: BlockingPoolPolicy,
113 pub metadata_io: BlockingPoolPolicy,
115 pub cpu_crypto: BlockingPoolPolicy,
117}
118
119impl BlockingLanePolicies {
120 pub(crate) fn for_parallelism(parallelism: usize) -> Self {
121 let parallelism = parallelism.max(1);
122 Self {
123 storage_io: BlockingPoolPolicy {
124 name: "rocketmq-blocking.storage-io".to_string(),
125 max_concurrency: parallelism.saturating_mul(4).max(4),
126 max_queue_depth: parallelism.saturating_mul(16).max(16),
127 ..BlockingPoolPolicy::default()
128 },
129 metadata_io: BlockingPoolPolicy {
130 name: "rocketmq-blocking.metadata-io".to_string(),
131 max_concurrency: parallelism.saturating_mul(2).max(2),
132 max_queue_depth: parallelism.saturating_mul(8).max(8),
133 ..BlockingPoolPolicy::default()
134 },
135 cpu_crypto: BlockingPoolPolicy {
136 name: "rocketmq-blocking.cpu-crypto".to_string(),
137 max_concurrency: parallelism.max(2),
138 max_queue_depth: parallelism.saturating_mul(4).max(8),
139 ..BlockingPoolPolicy::default()
140 },
141 }
142 }
143
144 pub(crate) fn cap_concurrency(&mut self, global_capacity: usize) {
145 self.storage_io.max_concurrency = self.storage_io.max_concurrency.min(global_capacity).max(1);
146 self.metadata_io.max_concurrency = self.metadata_io.max_concurrency.min(global_capacity).max(1);
147 self.cpu_crypto.max_concurrency = self.cpu_crypto.max_concurrency.min(global_capacity).max(1);
148 }
149
150 pub fn validate(&self) -> Result<(), RuntimeContractViolation> {
156 self.storage_io.validate()?;
157 self.metadata_io.validate()?;
158 self.cpu_crypto.validate()
159 }
160
161 pub(crate) fn validate_for_global_capacity(&self, global_capacity: usize) -> Result<(), RuntimeContractViolation> {
162 self.validate()?;
163 if global_capacity < BlockingLane::ALL.len() {
164 return Err(RuntimeContractViolation::InvalidConfiguration {
165 policy: RuntimeContractPolicy::BlockingGlobalCapacityCoversLanes,
166 });
167 }
168 Ok(())
169 }
170
171 #[must_use]
177 pub fn policy(&self, lane: BlockingLane) -> &BlockingPoolPolicy {
178 match lane {
179 BlockingLane::StorageIo => &self.storage_io,
180 BlockingLane::MetadataIo => &self.metadata_io,
181 BlockingLane::CpuCrypto => &self.cpu_crypto,
182 }
183 }
184
185 pub(crate) fn max_concurrency(&self, lane: BlockingLane) -> usize {
186 match lane {
187 BlockingLane::StorageIo => self.storage_io.max_concurrency,
188 BlockingLane::MetadataIo => self.metadata_io.max_concurrency,
189 BlockingLane::CpuCrypto => self.cpu_crypto.max_concurrency,
190 }
191 }
192
193 pub(crate) fn total_max_concurrency(&self) -> usize {
194 self.storage_io
195 .max_concurrency
196 .saturating_add(self.metadata_io.max_concurrency)
197 .saturating_add(self.cpu_crypto.max_concurrency)
198 }
199
200 pub fn uniform(policy: BlockingPoolPolicy) -> Self {
202 let mut storage_io = policy.clone();
203 storage_io.name = format!("{}.storage-io", policy.name);
204 let mut metadata_io = policy.clone();
205 metadata_io.name = format!("{}.metadata-io", policy.name);
206 let mut cpu_crypto = policy;
207 cpu_crypto.name = format!("{}.cpu-crypto", cpu_crypto.name);
208 Self {
209 storage_io,
210 metadata_io,
211 cpu_crypto,
212 }
213 }
214}
215
216impl Default for BlockingLanePolicies {
217 fn default() -> Self {
218 let parallelism = std::thread::available_parallelism()
219 .map(|value| value.get())
220 .unwrap_or(4);
221 Self::for_parallelism(parallelism)
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
227pub enum BlockingKind {
228 ShortIo,
230 CpuBound,
232 LongRunning,
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
238pub struct BlockingTaskId(pub(crate) u64);
239
240impl BlockingTaskId {
241 pub fn as_u64(self) -> u64 {
243 self.0
244 }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
249pub enum BlockingTaskState {
250 Queued,
252 Running,
254 Completed,
256 JoinFailed,
258 TimedOutStillRunning,
260}