Skip to content

Commit b7ab3c1

Browse files
committed
Add user-limits and lock stats to WaitStats
1 parent 682636b commit b7ab3c1

3 files changed

Lines changed: 91 additions & 3 deletions

File tree

crates/storage-api/src/vqueue_table/stats.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ pub struct WaitStats {
2727
/// Total milliseconds the item spent waiting on invoker memory pool
2828
#[bilrost(tag(4))]
2929
pub blocked_on_invoker_memory_ms: u32,
30+
/// Total milliseconds the item spent waiting on user-defined concurrency limits
31+
#[bilrost(tag(5))]
32+
pub blocked_on_user_limit_ms: u32,
33+
/// Total milliseconds the item spent waiting to acquire a virtual object lock
34+
#[bilrost(tag(6))]
35+
pub blocked_on_lock_ms: u32,
3036
}
3137

3238
#[derive(Debug, Clone, bilrost::Message)]

crates/vqueues/src/metric_definitions.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub const VQUEUE_GLOBAL_THROTTLE_WAIT_MS: &str =
2323
"restate.vqueue.scheduler.global_throttle_ms.total";
2424
// Per vqueue start throttling (affects starts only)
2525
pub const VQUEUE_LOCAL_THROTTLE_WAIT_MS: &str = "restate.vqueue.scheduler.vqueue_throttle_ms.total";
26+
pub const VQUEUE_USER_LIMIT_WAIT_MS: &str = "restate.vqueue.scheduler.user_limit_wait_ms.total";
27+
pub const VQUEUE_LOCK_WAIT_MS: &str = "restate.vqueue.scheduler.lock_wait_ms.total";
2628

2729
pub const ACTION_YIELD: &str = "yield";
2830
pub const ACTION_RUN: &str = "run";
@@ -69,6 +71,18 @@ pub fn describe_metrics() {
6971
Unit::Count,
7072
"Cumulative number of seconds vqueues waited because of their self-imposed start throttling"
7173
);
74+
75+
describe_counter!(
76+
VQUEUE_USER_LIMIT_WAIT_MS,
77+
Unit::Count,
78+
"Cumulative number of milliseconds spent waiting for user-defined concurrency limits"
79+
);
80+
81+
describe_counter!(
82+
VQUEUE_LOCK_WAIT_MS,
83+
Unit::Count,
84+
"Cumulative number of milliseconds spent waiting to acquire virtual object locks"
85+
);
7286
}
7387

7488
pub fn publish_scheduler_decision_metrics(num_run: u32, num_yield: u32) {

crates/vqueues/src/scheduler/vqueue_state.rs

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use restate_types::vqueues::VQueueId;
2020

2121
use crate::metric_definitions::{
2222
VQUEUE_GLOBAL_THROTTLE_WAIT_MS, VQUEUE_INVOKER_CONCURRENCY_WAIT_MS,
23-
VQUEUE_INVOKER_MEMORY_WAIT_MS, VQUEUE_LOCAL_THROTTLE_WAIT_MS,
23+
VQUEUE_INVOKER_MEMORY_WAIT_MS, VQUEUE_LOCAL_THROTTLE_WAIT_MS, VQUEUE_LOCK_WAIT_MS,
24+
VQUEUE_USER_LIMIT_WAIT_MS,
2425
};
2526
use crate::scheduler::queue::QueueItem;
2627

@@ -92,6 +93,10 @@ struct Stats {
9293
blocked_on_invoker_memory_micros: u32,
9394
local_start_throttling_micros: u32,
9495
global_throttling_micros: u32,
96+
last_blocked_on_user_limit: Option<tokio::time::Instant>,
97+
blocked_on_user_limit_micros: u32,
98+
last_blocked_on_lock: Option<tokio::time::Instant>,
99+
blocked_on_lock_micros: u32,
95100
}
96101
impl Stats {
97102
fn reset(&mut self) {
@@ -101,19 +106,26 @@ impl Stats {
101106
self.blocked_on_invoker_memory_micros = 0;
102107
self.local_start_throttling_micros = 0;
103108
self.global_throttling_micros = 0;
109+
self.last_blocked_on_user_limit = None;
110+
self.blocked_on_user_limit_micros = 0;
111+
self.last_blocked_on_lock = None;
112+
self.blocked_on_lock_micros = 0;
104113
}
105114

106115
fn finalize(&mut self) -> WaitStats {
107-
// ensures that the last capacity/memory-blocked segment is accounted for
116+
// ensures that the last blocked segments are accounted for
108117
self.record_invoker_memory_delay(false);
109-
// ensures that the last capacity-blocked segment is accounted for
110118
self.record_invoker_concurrency_delay(false);
119+
self.record_user_limit_delay(false);
120+
self.record_lock_delay(false);
111121

112122
let stats = WaitStats {
113123
blocked_on_global_capacity_ms: self.blocked_on_invoker_concurrency_micros / 1000,
114124
vqueue_start_throttling_ms: self.local_start_throttling_micros / 1000,
115125
blocked_on_invoker_memory_ms: self.blocked_on_invoker_memory_micros / 1000,
116126
global_invoker_throttling_ms: self.global_throttling_micros / 1000,
127+
blocked_on_user_limit_ms: self.blocked_on_user_limit_micros / 1000,
128+
blocked_on_lock_ms: self.blocked_on_lock_micros / 1000,
117129
};
118130
self.reset();
119131

@@ -139,11 +151,29 @@ impl Stats {
139151
self.blocked_on_invoker_memory_micros
140152
};
141153

154+
let blocked_on_user_limit_micros = if let Some(last) = self.last_blocked_on_user_limit {
155+
let delay = last.elapsed();
156+
self.blocked_on_user_limit_micros
157+
.saturating_add(delay.as_micros().try_into().unwrap_or(u32::MAX))
158+
} else {
159+
self.blocked_on_user_limit_micros
160+
};
161+
162+
let blocked_on_lock_micros = if let Some(last) = self.last_blocked_on_lock {
163+
let delay = last.elapsed();
164+
self.blocked_on_lock_micros
165+
.saturating_add(delay.as_micros().try_into().unwrap_or(u32::MAX))
166+
} else {
167+
self.blocked_on_lock_micros
168+
};
169+
142170
WaitStats {
143171
blocked_on_global_capacity_ms: blocked_on_global_capacity_micros / 1000,
144172
vqueue_start_throttling_ms: self.local_start_throttling_micros / 1000,
145173
global_invoker_throttling_ms: self.global_throttling_micros / 1000,
146174
blocked_on_invoker_memory_ms: blocked_on_invoker_memory_micros / 1000,
175+
blocked_on_user_limit_ms: blocked_on_user_limit_micros / 1000,
176+
blocked_on_lock_ms: blocked_on_lock_micros / 1000,
147177
}
148178
}
149179
}
@@ -198,6 +228,38 @@ impl Stats {
198228
.saturating_add(delay.as_micros().try_into().unwrap_or(u32::MAX))
199229
}
200230
}
231+
232+
fn record_user_limit_delay(&mut self, is_now_blocked: bool) {
233+
let last = self.last_blocked_on_user_limit.take();
234+
self.last_blocked_on_user_limit = if is_now_blocked {
235+
Some(Instant::now())
236+
} else {
237+
None
238+
};
239+
if let Some(last) = last {
240+
let delay = last.elapsed();
241+
counter!(VQUEUE_USER_LIMIT_WAIT_MS).increment(delay.as_millis() as u64);
242+
self.blocked_on_user_limit_micros = self
243+
.blocked_on_user_limit_micros
244+
.saturating_add(delay.as_micros().try_into().unwrap_or(u32::MAX))
245+
}
246+
}
247+
248+
fn record_lock_delay(&mut self, is_now_blocked: bool) {
249+
let last = self.last_blocked_on_lock.take();
250+
self.last_blocked_on_lock = if is_now_blocked {
251+
Some(Instant::now())
252+
} else {
253+
None
254+
};
255+
if let Some(last) = last {
256+
let delay = last.elapsed();
257+
counter!(VQUEUE_LOCK_WAIT_MS).increment(delay.as_millis() as u64);
258+
self.blocked_on_lock_micros = self
259+
.blocked_on_lock_micros
260+
.saturating_add(delay.as_micros().try_into().unwrap_or(u32::MAX))
261+
}
262+
}
201263
}
202264

203265
#[derive(derive_more::Debug)]
@@ -318,6 +380,12 @@ impl<S: VQueueStore> VQueueState<S> {
318380
ResourceKind::InvokerThrottling => {
319381
// self.head_stats.record_start_throttling_delay(delay);
320382
}
383+
ResourceKind::LimitKeyConcurrency { .. } => {
384+
self.head_stats.record_user_limit_delay(true);
385+
}
386+
ResourceKind::Lock { .. } => {
387+
self.head_stats.record_lock_delay(true);
388+
}
321389
_ => {}
322390
}
323391
Ok(Pop::Blocked(resource))

0 commit comments

Comments
 (0)