Skip to content

[dashboard] Configurable defaults + UI dialogs for py-spy/memray profiling params - #64806

Open
marwan116 wants to merge 15 commits into
masterfrom
marwan/configurable-profiling-defaults
Open

[dashboard] Configurable defaults + UI dialogs for py-spy/memray profiling params#64806
marwan116 wants to merge 15 commits into
masterfrom
marwan/configurable-profiling-defaults

Conversation

@marwan116

@marwan116 marwan116 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Why are these changes needed?

The Ray Dashboard's profiling actions — Stack Trace (py-spy), CPU Flame Graph (py-spy), and
Memory Profiling (memray) — expose several parameters (native, subprocesses, idle,
leaks, trace_python_allocators, duration, format) whose defaults were hardcoded. The
only way to change one (e.g. to include native C/C++ frames in a stack trace) was to hand-edit the
request URL's query string, and there was no way for a cluster operator to change a default
cluster-wide
. This came up from a user asking whether the default value of native can be
configured — today it can't.

This PR makes every profiling parameter's default configurable and gives the dashboard UI first-class
controls for them.

Backend

  • New RAY_DASHBOARD_PROFILING_* environment variables, read on the Ray head node, let an operator
    set each default. Parse sites in reporter_head fall back to these constants only when the request
    omits the parameter; an explicit query parameter still wins, so behavior is unchanged unless an
    operator opts in.
  • duration and format are split into CPU and memory variants, because py-spy (CPU:
    flamegraph/raw/speedscope) and memray (memory: flamegraph/table) accept different
    valid format sets and different duration defaults (5s vs 10s). A single shared default would break
    CPU requests. format defaults are validated against the correct set at load time, falling back
    to flamegraph on a bad value.
  • duration validation now returns HTTP 400, not 500. The endpoints previously raised a bare
    ValueError for an over-long duration, which surfaced as an uncaught HTTP 500. _query_duration
    now raises aiohttp.web.HTTPBadRequest for a non-integer or out-of-range value.
  • The accepted duration range is [1, RAY_DASHBOARD_PROFILING_MAX_DURATION_S], defaulting to
    the 60s cap the endpoints have always enforced. Operators raise or lower it per cluster. Configured
    duration defaults are clamped into that range at load time — with a warning naming the offending
    env var — so a misconfigured head node can never bypass the endpoint cap.
  • native remains Linux-only regardless of the default (existing guard in profile_manager).
  • /api/profiling_enabled now also returns the configured defaults so the UI can seed its dialogs,
    including maxDuration, so the duration field validates against the real cap instead of a
    hardcoded one.
  • The valid format sets live in ray_constants and are now read by both reporter_head (to
    validate the configured default) and profile_manager (to validate the request), so the accepted
    formats are declared in exactly one place.

New env vars, all documented in profiling.md (10 total):
RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT, _SUBPROCESSES_DEFAULT, _IDLE_DEFAULT, _LEAKS_DEFAULT,
_TRACE_PYTHON_ALLOCATORS_DEFAULT, _CPU_DURATION_DEFAULT, _MEMORY_DURATION_DEFAULT,
_CPU_FORMAT_DEFAULT, _MEMORY_FORMAT_DEFAULT, RAY_DASHBOARD_PROFILING_MAX_DURATION_S.

Frontend

  • The Stack Trace and CPU Flame Graph buttons (worker/actor/node/job and task variants) were
    bare links that hardcoded native=0, which also overrode the operator's configured default.
    They are now parameter dialogs — the same UX as the existing Memory Profiling dialog — so users set
    parameters in the UI instead of editing URLs.
  • All three profiling dialogs render one shared ProfilingParamsDialog component and seed their
    initial values from the defaults returned by /api/profiling_enabled. The hardcoded native=0 is
    gone; format options are profiler-correct; the duration field validates against the backend's
    reported maxDuration and disables submit with an inline error when out of range.

Not a duplicate

Searched open PRs before starting — no PR addresses this area:

gh pr list --repo ray-project/ray --state open --search "dashboard profiling native default"  # No Pull Requests
gh pr list --repo ray-project/ray --state open --search "RAY_DASHBOARD_PROFILING"              # No Pull Requests

There is no existing mechanism for configuring dashboard profiling defaults; RAY_DASHBOARD_ENABLE_PROFILING
only gates the feature on/off.

Related issues

Checks

AI assistance (Claude Code) was used to author this change; every changed line was reviewed by the
submitter.

Backend — verified locally:

pytest python/ray/dashboard/modules/reporter/tests/test_reporter.py \
  -k "query or clamp or validated_profiling_format" -v
#   30 passed

pytest python/ray/dashboard/modules/reporter/tests/test_reporter.py -k "profiling_enabled" -v
#   1 passed

pytest python/ray/dashboard/modules/reporter/tests/test_profile_manager.py -v
#   13 passed

Tests in test_reporter.py: test_query_flag, test_query_duration_valid,
test_query_duration_invalid_raises_bad_request, test_clamp_profiling_duration,
test_clamp_profiling_duration_warns_and_names_the_env_var,
test_clamp_profiling_duration_silent_when_in_range, test_validated_profiling_format,
test_validated_profiling_format_absent_uses_fallback,
test_profiling_enabled_endpoint_returns_defaults. These cover that out-of-range durations surface
as HTTP 400 rather than a bare ValueError, that a misconfigured env default is clamped with a
warning, and that the CPU/memory format split rejects speedscope for memray and table for py-spy.

New TestProfilingFormatValidation in test_profile_manager.py asserts both profilers honor the
shared format sets — every declared CPU format reaches the py-spy command, every declared memory
format maps to its memray subcommand, and each profiler rejects the other's formats. It mocks out
subprocess execution, so unlike the live TestMemoryProfiling suite it also runs on macOS.

End-to-end against a live head node (RAY_DASHBOARD_ENABLE_PROFILING=1):

GET /api/profiling_enabled          -> maxDuration=60, cpuDuration=5, memoryDuration=10
GET /worker/cpu_profile?duration=60  -> passes validation
GET /worker/cpu_profile?duration=61  -> HTTP 400  "duration must be between 1 and 60 seconds, got: 61"
GET /worker/cpu_profile?duration=0   -> HTTP 400
GET /worker/cpu_profile?duration=abc -> HTTP 400  "duration query parameter must be an integer"
RAY_DASHBOARD_PROFILING_MAX_DURATION_S=120 -> maxDuration=120

Frontend — verified locally:

cd python/ray/dashboard/client
./node_modules/.bin/tsc --noEmit -p tsconfig.json          # 0 errors
CI=true npx react-scripts test --testPathPattern=ProfilingLink --watchAll=false
#   Test Suites: 1 passed, 1 total
#   Tests:       11 passed, 11 total
./node_modules/.bin/eslint src/common/ProfilingLink*.tsx   # clean
./node_modules/.bin/prettier --check src/common/ProfilingLink*.tsx  # clean

The frontend tests assert, among other things, that the worker Stack Trace link produces
worker/traceback?...&native=1&subprocesses=0 (reflecting a configured native default), that it
never emits a hardcoded native=0, and that an out-of-range duration disables submit with an
inline error.

pre-commit run --files <changed> passes (ruff, black, pydoclint, docstyle, import-order, eslint,
semgrep).

Notes for reviewers

  • All new endpoints/parameters stay behind the existing RAY_DASHBOARD_ENABLE_PROFILING security
    gate — unchanged.
  • jax_profile is deliberately not migrated to the shared _query_duration helper. It has never
    enforced a duration cap, and routing it through the shared validator would newly reject durations
    above the cap for JAX users — a behavior change unrelated to this PR's goal. Its docstring is
    accurate as-is.
  • ProfilingLink.tsx keeps its own copy of the format lists for the dropdown options, since the
    frontend can't import the Python constants. Plumbing them through /api/profiling_enabled would be
    a reasonable follow-up.

marwan116 and others added 2 commits July 16, 2026 16:08
The dashboard profiling actions (stack trace, CPU flame graph, memory
profiling) expose several parameters (native, subprocesses, idle, leaks,
trace_python_allocators, duration, format) whose defaults were hardcoded.
The only way to change one was to hand-edit the request URL, and there was
no way for a cluster operator to set a different default cluster-wide.

Add RAY_DASHBOARD_PROFILING_*_DEFAULT env vars so operators can configure
each default on the head node. Parse sites in reporter_head fall back to
these constants when the query param is absent; an explicit query param
still wins, so behavior is unchanged unless an operator opts in.

duration and format are split into CPU and memory variants because py-spy
(CPU: flamegraph/raw/speedscope) and memray (memory: flamegraph/table)
accept different valid format sets and different duration defaults; a
single shared default would break CPU requests. native remains Linux-only.

/api/profiling_enabled also returns the configured defaults so the
dashboard UI can seed its profiling dialogs from them.

AI assistance (Claude Code) was used to author this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
…faults

Previously the Stack Trace and CPU Flame Graph buttons were bare links with
a hardcoded native=0 in the URL, so a user could only change parameters by
hand-editing the URL, and the hardcoded native=0 overrode the operator's
configured default.

Turn the Stack Trace and CPU Flame Graph buttons (worker and task variants)
into parameter dialogs, mirroring the existing Memory Profiling dialog. All
three profiling dialogs now render one shared ProfilingParamsDialog and seed
their initial values from the defaults returned by /api/profiling_enabled.
The hardcoded native=0 is removed; format options are profiler-correct
(CPU: flamegraph/raw/speedscope, memory: flamegraph/table).

Verified with tsc --noEmit and the ProfilingLink component tests (updated
the existing memory-dialog assertion and added worker/task dialog tests).

AI assistance (Claude Code) was used to author this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
@marwan116
marwan116 requested review from a team as code owners July 16, 2026 13:10

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces operator-configurable environment variables to set cluster-wide defaults for profiling parameters (such as duration, format, and various flags) on the Ray head node, updating both the backend and frontend to respect these defaults. The review feedback highlights critical validation and accessibility improvements: the backend endpoints in reporter_head.py should properly validate that profiling durations are between 1 and 60 seconds and raise clean HTTP 400 errors instead of uncaught ValueErrors (which cause HTTP 500s). On the frontend, the duration input field in ProfilingLink.tsx needs visual validation feedback, and invalid HTML nesting of a <Link> inside a <Button> should be resolved by rendering the button directly as an anchor.

Comment thread python/ray/dashboard/modules/reporter/reporter_head.py Outdated
Comment thread python/ray/dashboard/modules/reporter/reporter_head.py Outdated
Comment thread python/ray/dashboard/modules/reporter/reporter_head.py Outdated
Comment thread python/ray/dashboard/client/src/common/ProfilingLink.tsx
Comment thread python/ray/dashboard/client/src/common/ProfilingLink.tsx
Address code review feedback:

- Validate the profiling `duration` query param (1-60 seconds) in a single
  `_query_duration` helper used by all three profiling endpoints, raising
  HTTP 400 (aiohttp.web.HTTPBadRequest) for a non-integer or out-of-range
  value. Previously an out-of-range duration raised a bare ValueError
  (surfacing as HTTP 500) on the CPU endpoints, and the memory endpoint had
  no upper bound at all.
- Frontend: show inline error/helper text on the duration field and disable
  the submit action when the duration is out of range.
- Frontend: render the submit button itself as the anchor (component="a")
  instead of nesting a <Link> inside a <Button>, which is invalid HTML and
  an accessibility issue.

Added tests for the duration bounds (backend) and the invalid-duration
disabled/error state (frontend).

AI assistance (Claude Code) was used to author this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
@marwan116

Copy link
Copy Markdown
Contributor Author

Thanks for the review @gemini-code-assist. Addressed all five comments in 5a037a6:

  • Duration validation (3 endpoints): consolidated parsing + validation into one _query_duration helper that raises HTTPBadRequest (400) for non-integer or out-of-range values and enforces a 1–60s bound. This fixes the bare ValueError→500 on the CPU endpoints and adds the upper bound the memory endpoint was missing.
  • Frontend duration field: inline error/helperText and the submit action is disabled while the value is out of range.
  • Invalid nested interactive elements: the submit Button now renders as the anchor itself (component="a") instead of wrapping a <Link>.

Added tests for the duration bounds (backend) and the invalid-duration disabled/error state (frontend). Frontend verified locally: tsc --noEmit + 10 Jest tests pass, prettier/eslint clean. Backend: pre-commit clean; _query_duration unit-tested (bounds + 400 behavior).

@gemini-code-assist

Copy link
Copy Markdown
Contributor

The consolidation of parsing and validation into the _query_duration helper, along with the added frontend validation and test coverage, addresses the identified issues regarding parameter bounds and error handling. The approach of using the component="a" prop for the submit button is a standard way to avoid invalid nested interactive elements in React. These changes appear to correctly implement the requested configuration surface for profiling parameters.

Comment thread python/ray/dashboard/modules/reporter/reporter_head.py
@ray-gardener ray-gardener Bot added the core Issues that should be addressed in Ray Core label Jul 16, 2026
Comment thread python/ray/dashboard/modules/reporter/reporter_head.py
Comment thread python/ray/_private/ray_constants.py
Comment thread python/ray/dashboard/client/src/common/ProfilingLink.tsx Outdated
`test_profiling_enabled_endpoint_returns_defaults` passed the
`http://`-prefixed URL from `format_web_url` into
`wait_until_server_available`, whose `parse_address` call only handles
`host:port` and returns None for a scheme-prefixed URL, raising
`TypeError: cannot unpack non-iterable NoneType object`.

Wait on the raw `webui_url` first, then format it for the request,
matching the existing pattern elsewhere in this file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
Comment thread python/ray/dashboard/modules/reporter/reporter_head.py
Comment thread python/ray/dashboard/client/src/common/ProfilingLink.tsx
marwan116 and others added 2 commits July 22, 2026 16:29
…g durations

Address review feedback on the configurable profiling defaults:

- leaks default: RAY_DASHBOARD_PROFILING_LEAKS_DEFAULT defaulted to True,
  which silently put memray into leak mode for any request that omits `leaks`.
  The previous handler defaulted an omitted `leaks` to off (peak usage), so
  this was an unintended behavior change. Restore the default to False (and the
  frontend fallback + docs table to match) so behavior is unchanged unless an
  operator opts in.

- duration clamping: the CPU/memory duration defaults were read with
  env_integer and never clamped, so a misconfigured head-node env value could
  bypass the endpoint's 1-60s cap -- and, because /api/profiling_enabled echoes
  those defaults to the UI, the dialog would seed the bad value and then 400 on
  submit. Clamp both defaults into [MIN_PROFILING_DURATION_S,
  MAX_PROFILING_DURATION_S] at load time and move the bounds into ray_constants
  as the single source of truth. Explicit out-of-range query params still
  return HTTP 400.

Add test_clamp_profiling_duration (in-range pass-through + out-of-range
clamping); update the frontend default-params test to expect leaks=0.

AI assistance (Claude Code) was used to author this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
…g dialog on open

Address the remaining review nits on the profiling dialogs:

- Native checkbox (review nit): py-spy drops --native on non-Linux (see
  profile_manager), so a configured RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1
  would pre-check the box on a non-Linux dashboard where it is a silent no-op.
  /api/profiling_enabled now reports `pyspy_native_supported` (sys.platform ==
  "linux"), and the CPU/stack-trace dialogs disable and force-off the Native
  checkbox when it is unsupported. memray (memory) native is cross-platform, so
  the memory dialog's Native checkbox is unchanged.

- Dialog defaults (bugbot): ProfilingParamsDialog captured its initial state
  only at first mount. In practice the dialog only mounts once profiling is
  enabled -- after /api/profiling_enabled resolves, with the defaults cached in
  the same batch -- so it already seeds correctly, but reseed state on open
  anyway so it is robust to render timing.

Add a frontend test for the disabled/forced-off Native behavior; add the new
key to the profiling_enabled endpoint test.

AI assistance (Claude Code) was used to author this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7333e62. Configure here.

Comment thread python/ray/dashboard/modules/reporter/tests/test_reporter.py
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
@marwan116
marwan116 requested a review from carolynwang July 23, 2026 15:34
The profiling `duration` upper bound was a hardcoded 60s. Nothing in the stack
actually enforces 60 (the profiling gRPC calls set no deadline and py-spy/memray
are awaited without a timeout), so it was an arbitrary product choice, and too
low for some legitimate cases -- memray leak detection over minutes, periodic
CPU hotspots (py-spy's own docs use a 5-minute example).

Make the cap operator-configurable via RAY_DASHBOARD_PROFILING_MAX_DURATION_S
(default 300s), floored at the 1s minimum so a misconfigured value can never
invert the range (which would otherwise reject every duration). The minimum
stays a hard floor. /api/profiling_enabled now returns `max_duration` so the
dashboard validates the Duration field against the real cap instead of a
hardcoded one.

Keeping a cap (rather than open-ended) is deliberate: a synchronous profile
blocks the request for its whole duration and py-spy/memray output grows with
it, and there is no backstop timeout, so an unbounded value is a footgun.

AI assistance (Claude Code) was used to author this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>

@carolynwang carolynwang 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.

lgtm thanks!

@MortalHappiness MortalHappiness left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please also update your stale PR descriptions. Thanks.

Comment thread python/ray/_private/ray_constants.py Outdated
Comment thread python/ray/dashboard/modules/reporter/reporter_head.py Outdated
Comment thread python/ray/_private/ray_constants.py Outdated
Comment thread python/ray/_private/ray_constants.py Outdated
…s, docstrings

Follow-up to review feedback on the configurable profiling defaults PR:

- Default RAY_DASHBOARD_PROFILING_MAX_DURATION_S to 60 rather than 300, so the
  shipped cap matches the one the endpoints have always enforced. Operators
  still raise or lower it per cluster.
- Warn when a configured duration is clamped into the accepted range, naming
  the env var that was ignored, and warn when the configured cap itself falls
  below the minimum. A silently adjusted value otherwise looks like the
  operator's setting took effect.
- Make the valid format sets public and have profile_manager read them, so the
  formats py-spy and memray accept are declared in exactly one place instead of
  being duplicated between request validation and default validation.
- Fix stale docstrings in reporter_head: duration errors are HTTP 400, not a
  bare ValueError or a 500, the duration/format/flag defaults are
  operator-configurable rather than fixed, and get_traceback and memory_profile
  now document the query params they parse.

Adds TestProfilingFormatValidation, which mocks out subprocess execution so the
shared format sets are covered on platforms where the live memray suite is
skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
@marwan116

Copy link
Copy Markdown
Contributor Author

Thanks for the review @MortalHappiness — all four comments addressed in efe7a93, replies inline.

Description is refreshed. It had drifted in a few ways:

  • Said "9 env vars total" and omitted RAY_DASHBOARD_PROFILING_MAX_DURATION_S. It's 10.
  • Referenced _query_int / test_query_int / test_query_int_invalid_raises_value_error, which were renamed to _query_duration / test_query_duration_valid / test_query_duration_invalid_raises_bad_request.
  • Quoted a stale frontend test count (9; it's 11 now).
  • Never mentioned three behaviors that had landed since it was written: duration validation moving from a bare ValueError (HTTP 500) to HTTPBadRequest (400), maxDuration being exposed through /api/profiling_enabled so the UI validates against the real cap, and configured duration defaults being clamped at load time.

The Checks section now has re-run numbers (backend 30 + 1 + 13 passed, frontend 11 passed, tsc clean, pre-commit clean) plus the live end-to-end results for the cap.

@MortalHappiness MortalHappiness left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM for Python changes. I didn't review frontend and doc changes.

Resolved conflicts in ProfilingLink.tsx and ProfilingLink.component.test.tsx
against #65126 (fix profiling status check behind a reverse proxy):

- fetchProfilingEnabled() keeps master's get() helper from requestHandlers
  (reverse-proxy safe) and still merges profilingDefaults onto
  DEFAULT_PROFILING_DEFAULTS for partial/older payloads.
- Consolidated the two test-only cache resets into master's
  _resetProfilingEnabledCache(), which now also clears cachedProfilingDefaults.
- Test helper mockProfiling() switched from stubbing global.fetch to mocking
  get(), matching the production code path.

Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>

@dstrodtman dstrodtman 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.

Docs style review (Anyscale docs team)

Run by Douglas Strodtman on the Anyscale docs team, with Claude Code assisting. I read every comment below and stand behind it.

Scope: style, grammar, and Ray docs conventions on the two files under doc/, checked against Ray's documentation style guide. I'm not a code owner here. Everything technical stays with @carolynwang and @MortalHappiness, and I noticed @MortalHappiness's approval explicitly excluded the doc changes, which is why I picked them up.

Nice work on the reference table. Cross-checking every row against ray_constants.py at 2f03aa9, all ten variable names, all ten defaults, the [1, 60] bound, the clamping behavior, the HTTP 400 on an out-of-range explicit duration, and both format sets are accurate. That's a better hit rate than most env-var tables in the docs, so the comments below are almost entirely prose mechanics.

Two things worth a look beyond style:

  1. The native row's "only takes effect on Linux" may be scoped too narrowly. RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT feeds native on the memory endpoint too (reporter_head.py, memory_profile), and your own code comment says memray native is cross-platform while py-spy only appends --native on Linux. If that's right, the Linux caveat holds for stack trace and CPU profiling but not for memory profiling, and the table currently reads as though it applies to all three. Flagging rather than suggesting a fix, since you know the profiler behavior and I'm reading it off your comment. Details inline.
  2. The PR title promises UI dialogs, and the docs only cover the env vars. A reader hitting the dashboard sees new parameter dialogs that no page describes. Is documenting them in scope here, or a deliberate follow-up? Not a blocker from me either way, just want it to be a decision rather than an oversight.

One out-of-diff note: profiling.md has no myst.html_meta.description front matter, which the style guide asks for on every page. Pre-existing and above the diff, so there's nothing to click, but it's a cheap add while you're in the file.

Happy to give this a docs-side stamp once you've picked through the comments. Posting as a comment, not a change request.

:::

(profiling-defaults)=
### Configuring profiling defaults

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.

Heading level and placement. This is an ### under ## Enabling dashboard profiling, which makes configuring defaults a subtask of enabling profiling. They read more like siblings to me: enabling is a security gate you pass once, configuring defaults is separate operator tuning, and this section sits right before ## CPU profiling. Worth considering promoting it to ## Configuring profiling defaults. Your call, and I can see the argument that both are head-node env vars so grouping them helps.

On the wording: the style guide asks for imperative task headings ("Configure profiling defaults"), but the H2 above it is a gerund, and internal consistency on the page wins over a global sweep. Leaving it as-is is fine.

(profiling-defaults)=
### Configuring profiling defaults

Each profiling request (stack trace, CPU flame graph, memory profile) accepts several parameters. When a request omits a parameter, the value falls back to a cluster-wide default. Set these environment variables on the Ray head node to change the defaults for every request that doesn't specify the parameter explicitly (an explicit query parameter always takes precedence).

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.

Two parenthetical asides in one paragraph, and the style guide asks you to restructure rather than parenthesize. Folding the first into the sentence and promoting the second to its own sentence also gives the table a cleaner "the following" lead-in:

Suggested change
Each profiling request (stack trace, CPU flame graph, memory profile) accepts several parameters. When a request omits a parameter, the value falls back to a cluster-wide default. Set these environment variables on the Ray head node to change the defaults for every request that doesn't specify the parameter explicitly (an explicit query parameter always takes precedence).
Stack trace, CPU flame graph, and memory profile requests each accept several parameters. When a request omits a parameter, its value falls back to a cluster-wide default. Set the following environment variables on the Ray head node to change those defaults. An explicit query parameter always takes precedence.

- Meaning
- Default
* - `RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT`
- Include native (C/C++) stack frames. Higher overhead; only takes effect on Linux.

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.

Semicolon in prose. The style guide asks you to split instead:

Suggested change
- Include native (C/C++) stack frames. Higher overhead; only takes effect on Linux.
- Include native (C/C++) stack frames. Adds significant overhead. Only takes effect on Linux.

Separately, and this is the technical question from the review body rather than a style point: I don't think "only takes effect on Linux" is true for all three request types. memory_profile in reporter_head.py also defaults its native flag from this variable, and your comment on the pyspy_native_supported field says memray native is cross-platform and only py-spy is Linux-gated. If so, a reader who sets this to 1 on macOS gets no change to stack traces or CPU profiles but does get native frames in memory profiles, which this row tells them not to expect.

I've deliberately kept the suggestion above to the semicolon and left the Linux claim exactly as you wrote it, because you know the profiler behavior and I don't want to restyle a scope claim into something more confident than I can verify. If the split is real, something like "Only takes effect on Linux for stack trace and CPU profiling. memray honors it on every platform." would cover it.

- Also profile child processes of the target (stack trace and CPU profiling).
- `0`
* - `RAY_DASHBOARD_PROFILING_IDLE_DEFAULT`
- Include off-CPU / sleeping threads (CPU profiling only).

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.

The style guide asks you to spell out separators in prose, so X / Y becomes "X or Y":

Suggested change
- Include off-CPU / sleeping threads (CPU profiling only).
- Include off-CPU or sleeping threads (CPU profiling only).

- Duration in seconds for memory profiling (clamped to `RAY_DASHBOARD_PROFILING_MAX_DURATION_S`).
- `10`
* - `RAY_DASHBOARD_PROFILING_MAX_DURATION_S`
- Maximum accepted profiling `duration` in seconds. A profile blocks the request for its whole duration, so it's capped rather than open-ended; raise or lower it per cluster. The minimum is always 1s. Explicit `duration` query values above this return HTTP 400.

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.

This is the one cell doing four jobs, and it has a semicolon plus an abbreviated unit. Splitting it into sentences and spelling out the unit:

Suggested change
- Maximum accepted profiling `duration` in seconds. A profile blocks the request for its whole duration, so it's capped rather than open-ended; raise or lower it per cluster. The minimum is always 1s. Explicit `duration` query values above this return HTTP 400.
- Maximum accepted profiling `duration` in seconds. A profile blocks the request for its whole duration, so Ray caps it rather than leaving it open-ended. Raise or lower it per cluster. The minimum is always 1 second. An explicit `duration` query value above this maximum returns HTTP 400.

I verified each claim in there against _profiling_duration_cap, _clamp_profiling_duration, and _query_duration, so this is a rewording only, not a change in meaning.

- Maximum accepted profiling `duration` in seconds. A profile blocks the request for its whole duration, so it's capped rather than open-ended; raise or lower it per cluster. The minimum is always 1s. Explicit `duration` query values above this return HTTP 400.
- `60`
* - `RAY_DASHBOARD_PROFILING_CPU_FORMAT_DEFAULT`
- Output format for CPU profiling. One of `flamegraph`, `raw`, `speedscope`.

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.

Small one: add the conjunction before the last item in each set, per the guide's Oxford comma rule.

Suggested change
- Output format for CPU profiling. One of `flamegraph`, `raw`, `speedscope`.
- Output format for CPU profiling. One of `flamegraph`, `raw`, or `speedscope`.

- Output format for CPU profiling. One of `flamegraph`, `raw`, `speedscope`.
- `flamegraph`
* - `RAY_DASHBOARD_PROFILING_MEMORY_FORMAT_DEFAULT`
- Output format for memory profiling. One of `flamegraph`, `table`.

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.

Same here:

Suggested change
- Output format for memory profiling. One of `flamegraph`, `table`.
- Output format for memory profiling. One of `flamegraph` or `table`.

- `flamegraph`
```

For example, to make native frames the default for stack traces across the cluster (Linux only), set `RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1` on the head node. Because `native` significantly increases profiling overhead, prefer leaving it off and enabling it only when sampling the Python layer alone isn't enough.

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.

Three things in this paragraph:

  • "prefer leaving it off and enabling it only when" is hedged. The guide asks you to turn hedging into direct advice, so make it an instruction and give the reason.
  • "(Linux only)" repeats the table row two lines above.
  • The overhead warning now appears three times across this PR: this paragraph, the table's native row, and the new sentence in debug-hangs.rst. Once in the table plus once in the prose that a stack-trace reader actually lands on is enough.
Suggested change
For example, to make native frames the default for stack traces across the cluster (Linux only), set `RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1` on the head node. Because `native` significantly increases profiling overhead, prefer leaving it off and enabling it only when sampling the Python layer alone isn't enough.
For example, to make native frames the default for stack traces across the cluster, set `RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1` on the head node. Enable it only when sampling the Python layer alone isn't enough, because native frames significantly increase profiling overhead.

Comment on lines +14 to +17
trace is shown. To show native code frames, set the URL parameter ``native=1`` (only supported on Linux). To make native
frames the default for every stack trace on the cluster, set the ``RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1`` environment
variable on the Ray head node (see :ref:`Configuring profiling defaults <profiling-defaults>`). Because native frames add
significant profiling overhead, prefer enabling it only when sampling the Python layer alone isn't enough.

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.

Two style points on the added sentences, plus one that isn't yours.

The (see :ref:...) parenthetical is the pattern the style guide asks you to restructure, and "prefer enabling it only when" is hedged where a direct instruction reads better. Moving the cross-reference to its own sentence at the end also makes it the obvious next click. I kept the existing hard wrap so the diff stays tight, and left the (only supported on Linux) parenthetical on line 14 alone since it predates this PR.

Suggested change
trace is shown. To show native code frames, set the URL parameter ``native=1`` (only supported on Linux). To make native
frames the default for every stack trace on the cluster, set the ``RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1`` environment
variable on the Ray head node (see :ref:`Configuring profiling defaults <profiling-defaults>`). Because native frames add
significant profiling overhead, prefer enabling it only when sampling the Python layer alone isn't enough.
trace is shown. To show native code frames, set the URL parameter ``native=1`` (only supported on Linux). To make native
frames the default for every stack trace on the cluster, set the ``RAY_DASHBOARD_PROFILING_NATIVE_DEFAULT=1`` environment
variable on the Ray head node. Native frames add significant profiling overhead, so enable them only when sampling the
Python layer alone isn't enough. See :ref:`Configuring profiling defaults <profiling-defaults>`.

Not from your diff, so there's nothing to click, but line 13 just above reads "Clicking "Stack Trace" will return the current stack trace sample" and the guide asks for present tense over "will" for things that are always true. "returns" would fix it if you feel like touching the line while you're here.

@dstrodtman dstrodtman 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.

Stamping with small doc feedback items.

@MortalHappiness MortalHappiness added the go add ONLY when ready to merge, run all tests label Aug 6, 2026
@MortalHappiness

Copy link
Copy Markdown
Member

Hi @marwan116 Can you check if this test failure related to your changes? https://buildkite.com/ray-project/premerge/builds/71537#019fd9d4-470c-4303-ac7a-b236c4d40838

I've synced with the master branch multiple times and retried the failed test multiple times but it always fails.

…-profiling-defaults

Signed-off-by: Marwan Sarieddine <sarieddine.marwan@gmail.com>
@marwan116

Copy link
Copy Markdown
Contributor Author

Thanks for checking @MortalHappiness — it's unrelated to this PR.

The failing target is //python/ray/train:test_state, and the real error is an assertion rather than the raylet noise in the same stderr block:

>   assert dataset_info.dataset_uuid == dataset._uuid
E   AssertionError: assert '0342l3gti8UINtnldFbV9Y' == '0342l3WtNOxOM6uop7CGAJ'
python/ray/train/tests/test_state.py:297

It broke on master in #65075, which regenerates a dataset's uuid on deserialization, and was fixed on master by #65290 (merged 2026-08-07 17:28 UTC). Build #71537 ran at 01:43 UTC that day, ~16h before the fix landed — the failure was deterministic, which is why the retries and master syncs never cleared it.

I've merged current master (including f22677d) into the branch and pushed, so this target should be green on the next run.

For completeness: this PR touches only the dashboard reporter, the profiling defaults in ray_constants.py, and the frontend. test_state.py imports none of that, and there are no C++ or BUILD changes.

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

Labels

core Issues that should be addressed in Ray Core go add ONLY when ready to merge, run all tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants