Skip to content

Track scheduled function delay - #5592

Open
Shubham8287 wants to merge 7 commits into
masterfrom
shub/scheduled-function-delay-metric
Open

Track scheduled function delay#5592
Shubham8287 wants to merge 7 commits into
masterfrom
shub/scheduled-function-delay-metric

Conversation

@Shubham8287

@Shubham8287 Shubham8287 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

fixes: https://github.com/clockworklabs/SpacetimeDBPrivate/issues/3441.

It chooses an arbitrary threshold of 50ms, if a scheduled function starts more than that threshold after its expected time, we emit a warning and record a metric.

Note that, metric does not necessarily tells If something is wrong with Host as drift can also happen due to module's previous schedule function taking long time to finish.

@Shubham8287
Shubham8287 marked this pull request as draft July 27, 2026 15:49
@Shubham8287
Shubham8287 force-pushed the shub/scheduled-function-delay-metric branch from b7fdfac to 61dfc95 Compare July 28, 2026 08:46
@Shubham8287
Shubham8287 marked this pull request as ready for review July 28, 2026 08:53
@Shubham8287
Shubham8287 requested a review from gefjon July 28, 2026 08:54

@gefjon gefjon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much additional work would it be to make the threshold be, say, 25% of the requested delay, rather than a flat 50ms? I worry that 50ms is quite a long time, especially for games which want to run relatively frequent and relatively fast "tick" reducers.

Comment thread crates/core/src/host/scheduler.rs Outdated
Comment on lines +583 to +586
let result =
call_scheduled_reducer_with_tx(module_info, db, id, tx, (timestamp, instant), params, inst_common, inst);
if let Some((function_name, delay)) = delay.as_ref() {
record_scheduled_function_delay(module_info, function_name, *delay);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like record_scheduled_function_delay calls Timestamp::now(), in this case after call_scheduled_reducer_with_tx has already returned. This appears to mean that we'll measure and report the delay plus the reducer's execution time. It also appears that the params already include params.timestamp, the invocation time reported to the reducer. Can we just use that timestamp to report the delay?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

record_scheduled_function_delay does not call Timestamp::now(). scheduled_function_delay_for_item does that which is called correctly before calling reducer.

I think, I can do better with naming.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I'd still prefer to use the same timestamp to compute delay as gets passed to the reducer.

Comment thread crates/core/src/host/scheduler.rs Outdated
Comment on lines +608 to +614
if delay <= RECORD_SCHEDULED_FUNCTION_DELAY_THRESHOLD {
return;
}
WORKER_METRICS
.scheduled_function_delay
.with_label_values(&module_info.database_identity, function_name)
.observe(delay.as_secs_f64());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not observe every function in the metric?

@Shubham8287 Shubham8287 Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't because of perfomance reasons with with_label_value (as it will lead to drift itself) but maybe we can do if we cache it.

Comment thread crates/core/src/host/scheduler.rs Outdated
}
WORKER_METRICS
.scheduled_function_delay
.with_label_values(&module_info.database_identity, function_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we cache this with_label_values call someplace rather than calling with_label_values on every report? And also, can we clean it up with remove_label_values, either when dropping whatever holds it, or in fn remove_database_gauges in crates/core/src/host/host_controller.rs?

@Shubham8287

Copy link
Copy Markdown
Contributor Author

How much additional work would it be to make the threshold be, say, 25% of the requested delay, rather than a flat 50ms?

The scheduling function has precision of few milliseconds, so we would still need some minimum threshold. On top of that, as I mentioned in the description, long running schedule functions can itself contribute to the delay, potentially causing many false positives.

As you already suggested to record a metric for every run, we will be using this value only logging purpose, so it may be fine?

@gefjon

gefjon commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

How much additional work would it be to make the threshold be, say, 25% of the requested delay, rather than a flat 50ms?

The scheduling function has precision of few milliseconds, so we would still need some minimum threshold. On top of that, as I mentioned in the description, long running schedule functions can itself contribute to the delay, potentially causing many false positives.

As you already suggested to record a metric for every run, we will be using this value only logging purpose, so it may be fine?

Yeah, I don't plan to directly alert on any single schedule invocation violating the threshold, I want to detect patterns of functions deviating significantly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants