orchagent: add per-Executor task timing instrumentation#4563
orchagent: add per-Executor task timing instrumentation#4563venkit-nexthop wants to merge 1 commit into
Conversation
|
|
afee480 to
61c930f
Compare
|
@prsunny @prabhataravind @vivekrnv @deepak-singhal0408 |
|
@venkit-nexthop could you resolve the merge conflicts? thanks, |
|
Question: what's the vision for how Really like this — cheap, well-scoped latency visibility into the orchagent loop, and the P² estimators keep it near-free. Before diving into specifics, I'm curious about the intended usage, since it drives the design:
I ask because the data-sourcing choice ties to that. Across
So it really comes down to the vision:
|
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Just resolved the conflicts. |
This is the current intended usage. i.e an on-box debug aid that a developer or operator runs by hand.
This is not designed for this use-case currently. But it could be extended for that in the future.
Yes, the challenge with logging stats into redis is that it will become another periodic write into the redis DB. I wanted to avoid this. This only needs to be fetched on-demand (one-shot and interactive).
Assume both sessions A and B are subscribed before either reply is published (the case where "the 2nd reads the 1st's reply"). Orchagent is single-threaded, so it publishes in order: reply-A, then reply-B. Each is broadcast to But each CLI does select() + pop() exactly once:
So in the fully-concurrent case, both sessions render session-A's snapshot, and reply-B is consumed by nobody. reply-B was generated by orchagent and delivered into both connections' buffers, but neither process ever pops a There is not memory leak in any or this. There is a possibility that with some timing case, one of the sessions may read garbled data. If the one sessions does a
YES, this one.
NO, not designed for this yet. |
Also the show/clear commands are implemented in this PR => sonic-net/sonic-utilities#4536 |
The coverage.Azure.sonic-swss.vstest check on PR sonic-net#4563 failed because the two OrchDaemon methods added for the task-stats notification transport had no unit-test coverage: initTaskStatsChannel() and the whole show/clear/unknown-op body of handleTaskStatsQuery(). Add four tests to orchdaemon_ut.cpp: - TaskStatsChannelInit: initTaskStatsChannel() wires up the APPL_DB handle and both notification channels and registers the query consumer with the daemon's Select. - TaskStatsQueryShow: a "show" request serializes every m_taskStats slot (a populated slot with bootstrapped P^2 estimators plus an empty count==0 slot) without mutating the stats. - TaskStatsQueryClear: a "clear" request resets every slot in place. - TaskStatsQueryUnknownOp: an unrecognized op hits the error branch and leaves the stats untouched. Notification messages are injected via the mock-hiredis global reply and NotificationConsumer::readData(), the same pattern the fdborch flush tests use, so handleTaskStatsQuery() pop()s a real request off its queue. Signed-off-by: Venkit Kasiviswanathan <venkit@nexthop.ai> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
The coverage.Azure.sonic-swss.vstest check on PR sonic-net#4563 failed because the two OrchDaemon methods added for the task-stats notification transport had no unit-test coverage: initTaskStatsChannel() and the whole show/clear/unknown-op body of handleTaskStatsQuery(). Add four tests to orchdaemon_ut.cpp: - TaskStatsChannelInit: initTaskStatsChannel() wires up the APPL_DB handle and both notification channels and registers the query consumer with the daemon's Select. - TaskStatsQueryShow: a "show" request serializes every m_taskStats slot (a populated slot with bootstrapped P^2 estimators plus an empty count==0 slot) without mutating the stats. - TaskStatsQueryClear: a "clear" request resets every slot in place. - TaskStatsQueryUnknownOp: an unrecognized op hits the error branch and leaves the stats untouched. Notification messages are injected via the mock-hiredis global reply and NotificationConsumer::readData(), the same pattern the fdborch flush tests use, so handleTaskStatsQuery() pop()s a real request off its queue. Signed-off-by: Venkit Kasiviswanathan <venkit@nexthop.ai>
0e5dbe7 to
320e57c
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
@deepak-singhal0408 @prsunny |
deepak-singhal0408
left a comment
There was a problem hiding this comment.
LGTM. The instumentation is lightweight. The TaskTimer cost is per batch, the P2 estimator is bounded, near-free overhead, O(1).
Agree on keeping it as on-demand for now. Multiple CLI session reading data parallely and getting occasional garbled data might be acceptable in my mind since it will mainly be used for debugging purposes.
There was a problem hiding this comment.
Pull request overview
This PR adds per-Executor timing instrumentation to orchagent (run-time wall clock + scheduling-latency) and exposes it via APPL_DB notification channels intended to back a future show orchagent tasks CLI.
Changes:
- Introduce
P2Quantile,ExecutorStat, and RAIITaskTimerto track counts/totals/min/max, quartiles, and Tukey-rule outliers. - Add APPL_DB notification query/reply channels (
ORCH_TASK_STATS_QUERY/ORCH_TASK_STATS_REPLY) withshow/clearhandling on the main thread. - Add mock unit tests covering quantile convergence, stat reset/serialization, outlier classification, and
TaskTimerbehavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/mock_tests/orchdaemon_ut.cpp | Adds unit tests for quantile/stat/timer logic and task-stats query handling. |
| orchagent/orchdaemon.h | Defines the quantile estimator, stats container, and RAII timer; adds task-stats channel members. |
| orchagent/orchdaemon.cpp | Wires up task-stats notification channels and wraps executor/inline ops with TaskTimer. |
Comments suppressed due to low confidence (1)
orchagent/orchdaemon.cpp:1113
- There is still an uninstrumented
flush();call in thediff.count() >= SELECT_TIMEOUTpath, so theflushslot under-reports and the implementation no longer matches the stated goal of wrapping allflush()call sites withTaskTimer.
{ TaskTimer t(m_taskStats["flush"]); flush(); }
/*
* Log an error message periodically if a previous SAI API call failed with
* an unrecoverable error.
*/
string orchHealthError;
if (getSaiFailureStatus(orchHealthError))
{
SWSS_LOG_ERROR("%s", orchHealthError.c_str());
}
flush();
|
@prsunny can you approve this? |
dde20c3 to
304a67d
Compare
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run Azure.sonic-swss |
|
Commenter does not have sufficient privileges for PR 4563 in repo sonic-net/sonic-swss |
304a67d to
e2772bf
Compare
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
184a228 to
9cc1cdd
Compare
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
f197210 to
94665bd
Compare
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Add a small, always-on instrumentation layer that records per-Executor wall-clock run time and scheduling latency from the orchagent main select loop, plus a notification-channel transport for a CLI to query and clear those stats. When orchagent's main select loop spends most of a second on one Executor (e.g. RouteOrch during a route burst), there is currently no way for an operator to see where orchagent is spending its time without instrumenting a build. This change adds: - ExecutorStat: per-Executor stats (count, total, max) plus three independent P^2 (Jain & Chlamtac, 1985) running quantile estimators for Q1, median, Q3 of run time. A Tukey 1.5*IQR rule classifies each new sample as a high or low outlier before it is folded into the estimators; a 30-sample warmup gate avoids spurious flags during P^2 bootstrap. - Scheduling latency: the same RAII timer records the gap between consecutive runs of an Executor into a parallel set of P^2 estimators. - TaskTimer: a small RAII wrap placed around every Executor::execute() call site in OrchDaemon::start(), plus the inline flush() and logRotate() paths. - Two APPL_DB notification channels (ORCH_TASK_STATS_QUERY / ORCH_TASK_STATS_REPLY) handle show/clear requests on the main thread (no atomics needed since m_taskStats is single-thread). Unit tests cover ExecutorStat record/reset, FieldValueTuple round-trip serialization, outlier classification, P^2 convergence on a uniform stream, P^2 robustness to moderate outliers, and the TaskTimer RAII contract for both run and scheduling-latency paths. They also cover the notification transport end to end: initTaskStatsChannel() wiring, and the show / clear / unknown-op branches of handleTaskStatsQuery(), with requests injected via the mock-hiredis global reply and NotificationConsumer::readData() (the fdborch flush-test pattern). The UT-owned redisReply tree is freed after injection so the mock reply does not leak across the test binary. Signed-off-by: Venkit Kasiviswanathan <venkit@nexthop.ai>
94665bd to
5d614b6
Compare
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
What I did
Add per-Executor wall-clock and scheduling-latency stats to
orchagent, surfaced via a notification channel for a future
show orchagent tasksCLI.ExecutorStat: count/total/max + P^2 quantile estimators(Q1/median/Q3) for run time, with a Tukey 1.5*IQR outlier
classifier (30-sample warmup gate).
TaskTimer(RAII): wrapsExecutor::execute()and the inlineflush()/logRotate()call sites inOrchDaemon::start().Also records scheduling latency = gap between consecutive runs.
ORCH_TASK_STATS_QUERY/ORCH_TASK_STATS_REPLY) handle show/clear on the main thread,no atomics needed.
ExecutorStat,P2Quantile,TaskTimer.No functional change to the existing select-loop or Orch logic;
the RAII timers add a small (sub-microsecond) overhead per
Executor dispatch.
Why I did it
Incremental step to satisfy the tasks listed in the HLD - sonic-net/SONiC#2328
This improves debug ability when doing interactive debugging. It give improved visibility into the various Executors inside Orchagent.
How I verified it
Unit tests:
New tests:
Details if related