Skip to content

obs/ash: add periodic top-N workload summary logging#165093

Merged
trunk-io[bot] merged 1 commit intocockroachdb:masterfrom
alyshanjahani-crl:ash-sampler-logging
Mar 12, 2026
Merged

obs/ash: add periodic top-N workload summary logging#165093
trunk-io[bot] merged 1 commit intocockroachdb:masterfrom
alyshanjahani-crl:ash-sampler-logging

Conversation

@alyshanjahani-crl
Copy link
Collaborator

@alyshanjahani-crl alyshanjahani-crl commented Mar 6, 2026

The ASH sampler holds samples only in memory, which are lost on node
restart. Add periodic structured log summaries so operators have durable
evidence of workload patterns.

Two new cluster settings control this behavior:

obs.ash.log_interval (default 10m) — how often the summary is emitted.
obs.ash.log_top_n (default 10) — max entries per summary.

Every log_interval, the sampler scans the ring buffer for samples
collected since the last report, groups them by (WorkEventType,
WorkEvent, WorkloadID), and emits one structured event per top-N entry
on the OPS channel. If there are zero samples in the window, no log is
emitted.

Example log output (one line per top-N entry, sorted by SampleCount descending):

I260309 19:25:00.123456 42 1@obs/ash/sampler.go:308 [n1] 500
={"Timestamp":1773084300123456000,"EventType":"ash_workload_summary",
"WindowDurationNanos":600000000000,"WorkEventType":"IO",
"WorkEvent":"BatchEval","WorkloadID":"000000000000002a",
"SampleCount":200}

Resolves: #164382

Release note (ops change): Added periodic ASH workload summary logging
to the OPS channel. Two new cluster settings, obs.ash.log_interval
(default 10m) and obs.ash.log_top_n (default 10), control how often
and how many entries are emitted. Each summary reports the most
frequently sampled workloads grouped by event type, event name, and
workload ID, providing durable visibility into workload patterns that
previously existed only in memory.

@trunk-io
Copy link
Contributor

trunk-io bot commented Mar 6, 2026

😎 Merged successfully - details.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@@ -59,6 +62,16 @@ var BufferSize = settings.RegisterIntSetting(
settings.PositiveInt,
)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do we want cluster setting for the two constants below?

I could see an argument for logSummaryTopN being a setting, but i think a 60 second periodic log wouldn't need to be modified .. would it?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I think cluster settings for both is deseriable. I think in general, a big structured log per minute feels too high. Maybe set to 10m by default and let customers adjust if they want more.

Copy link
Contributor

Choose a reason for hiding this comment

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

What if we had a smaller structured log per minute? @dhartunian

Copy link
Contributor

@angles-n-daemons angles-n-daemons Mar 9, 2026

Choose a reason for hiding this comment

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

Or rather, 10 (logs per message) as defined feels rather small - is this okay?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since it's easily configurable, I don't feel super strongly. We can do 10 lines per minute if we feel it would help with diagnosing issues.

@alyshanjahani-crl alyshanjahani-crl marked this pull request as ready for review March 6, 2026 16:56
@alyshanjahani-crl alyshanjahani-crl requested a review from a team as a code owner March 6, 2026 16:56
@alyshanjahani-crl alyshanjahani-crl requested review from angles-n-daemons and dhartunian and removed request for a team March 6, 2026 16:56
@@ -59,6 +62,16 @@ var BufferSize = settings.RegisterIntSetting(
settings.PositiveInt,
)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I think cluster settings for both is deseriable. I think in general, a big structured log per minute feels too high. Maybe set to 10m by default and let customers adjust if they want more.

fmt.Fprintf(&buf, "\n count=%-5d type=%-9s event=%-20s workload=%s",
e.count, e.key.WorkEventType, e.key.WorkEvent, e.key.WorkloadID)
}
log.Ops.Infof(ctx, "%s", buf.String())
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be a structured log with a protobuf in eventpb so that it's output as JSON and easily machine-parsed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done.

pendingSamples []pendingSample
// tickSamples tracks per-workload sample counts since the last
// periodic log summary was emitted.
tickSamples map[workloadKey]int
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do you need this? Why not loop through the ring buffer with a timestamp cutoff?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's a bit more efficient to keep the summary data updated while doing the ticks, instead of constructing it when the log summary interval happens.

But i think that efficiency gain is too small to warrant adding more state to the sampler and making the code a bit harder to follow (as @angles-n-daemons pointed out with setting/resetting these fields in different functions)

Changes to loop through the ring buffer w/ timestamp cutoff

@dhartunian
Copy link
Collaborator

Can you also show example output in the commit message? That would be helpful to see as context.

Copy link
Contributor

@angles-n-daemons angles-n-daemons left a comment

Choose a reason for hiding this comment

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

couple small comments, nothing blocking

log.Ops.Infof(ctx, "%s", buf.String())

// Reset counters.
for k := range s.tickSamples {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reason for maybeLogSamples resetting tickSamples and totalSamples. It feels like these should be initialized each time takeSample is called, because that's where they're going to be set.

The current code should work, but it feels a little fragile, and prone to correctness bugs if ordering, or frequency of sample vs log operations were changed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ack, i made this code simpler as per this and davids suggestion to just scan the ring buffer when doing the log summary.

Copy link
Contributor

Choose a reason for hiding this comment

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

nice, that's great thanks!

@@ -59,6 +62,16 @@ var BufferSize = settings.RegisterIntSetting(
settings.PositiveInt,
)

Copy link
Contributor

Choose a reason for hiding this comment

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

What if we had a smaller structured log per minute? @dhartunian

Copy link
Contributor

@angles-n-daemons angles-n-daemons left a comment

Choose a reason for hiding this comment

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

looks good, one last comment

// maybeLogSummary emits a top-N workload summary as structured events
// to the OPS log if enough time has elapsed since the last report.
// It scans the ring buffer for samples newer than lastLogTime and
// aggregates them by workload key.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a comment stating this is coupled to table sampling? It wasn't obvious to me at first, but has subtle implications on how the various settings interact.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

var LogTopN = settings.RegisterIntSetting(
settings.SystemVisible,
"obs.ash.log_top_n",
"maximum number of entries in periodic ASH workload summary",
Copy link
Collaborator

Choose a reason for hiding this comment

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

it would be helpful to document here what it means for stuff to appear "at the top"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ack, added.

@@ -59,6 +62,16 @@ var BufferSize = settings.RegisterIntSetting(
settings.PositiveInt,
)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since it's easily configurable, I don't feel super strongly. We can do 10 lines per minute if we feel it would help with diagnosing issues.


| Field | Description | Sensitive |
|--|--|--|
| `WindowDurationNanos` | The duration of the reporting window in nanoseconds. | no |
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would make this millis. do we allow sampling at intervals below a second? this is just adding zeros to the string for no huge value. Plus millis are more human-readable. I'd also be fine with seconds if that's our lowest supported interval.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sampler can run at 100ms, 500ms reliably from what i've seen with my benchmarks.

Changed to milliseconds.

The ASH sampler holds samples only in memory, which are lost on node
restart. Add periodic structured log summaries so operators have durable
evidence of workload patterns.

Two new cluster settings control this behavior:

  obs.ash.log_interval (default 10m) — how often the summary is emitted.
  obs.ash.log_top_n   (default 10)  — max entries per summary.

Every log_interval, the sampler scans the ring buffer for samples
collected since the last report, groups them by (WorkEventType,
WorkEvent, WorkloadID), and emits one structured event per top-N entry
on the OPS channel. If there are zero samples in the window, no log is
emitted.

Example log output (one line per top-N entry, sorted by SampleCount descending):

  I260309 19:25:00.123456 42 1@obs/ash/sampler.go:308 [n1] 500
    ={"Timestamp":1773084300123456000,"EventType":"ash_workload_summary",
      "WindowDurationNanos":600000000000,"WorkEventType":"IO",
      "WorkEvent":"BatchEval","WorkloadID":"000000000000002a",
      "SampleCount":200}

Resolves: cockroachdb#164382

Release note (ops change): Added periodic ASH workload summary logging
to the OPS channel. Two new cluster settings, `obs.ash.log_interval`
(default 10m) and `obs.ash.log_top_n` (default 10), control how often
and how many entries are emitted. Each summary reports the most
frequently sampled workloads grouped by event type, event name, and
workload ID, providing durable visibility into workload patterns that
previously existed only in memory.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
@alyshanjahani-crl
Copy link
Collaborator Author

TFTRs!

@alyshanjahani-crl
Copy link
Collaborator Author

/trunk merge

@trunk-io trunk-io bot merged commit f10f39f into cockroachdb:master Mar 12, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

obs/ash: add logging for ASH sampler

4 participants