Skip to content

[cherry-pick][2.58.0][core][taskEvents out of GCS][2/n] Replace task event buffer with ray event recorder (#64835) - #65274

Open
elliot-barn wants to merge 1 commit into
releases/2.58.0from
elliot-barn/cherry-pick-2.58.0-task-event-recorder-64835
Open

[cherry-pick][2.58.0][core][taskEvents out of GCS][2/n] Replace task event buffer with ray event recorder (#64835)#65274
elliot-barn wants to merge 1 commit into
releases/2.58.0from
elliot-barn/cherry-pick-2.58.0-task-event-recorder-64835

Conversation

@elliot-barn

Copy link
Copy Markdown
Collaborator

Cherry-pick of #64835 (merge commit d126844) into releases/2.58.0.

Applied cleanly with no conflicts.

Not a duplicate: no existing open PR against releases/2.58.0 contains this change. Cherry-picked with AI assistance (Claude Code); pre-commit hooks ran and passed on the commit.

🤖 Generated with Claude Code

… event recorder (#64835)

This PR builds on top of #64168.
It removes `task_event_buffer` and replaces it with
`ray_task_event_recorder`.

**CHANGES**:
1. Added `RayTaskDefinitionEvent`, `RayActorTaskDefinitionEvent`,
`RayTaskLifecycleEvent`, and `RayTaskProfileEvent` classes which extend
RayEventInterface to be recorded by `RayTaskEventRecorder`.
2. Constructs `RayTaskEventRecorder` in
`CoreWorkerProcessImpl::CreateCoreWorker`, and uses it in core worker
constructor. Wires the recorder to other classes as required
(`TaskManager`, `TaskReceiver`, ActorExecutionQueues etc.)
3. Uses its `AddEvents` function in parallel to where
`task_event_buffer_->AddTaskEvent` is done.
4. Adds a function `worker::RecordTaskStatusEventToRecorderIfNeeded` in
parallel to `task_event_buffer_.RecordTaskStatusEventIfNeeded` with
similar gates.
5. Adds a flag `enable_ray_task_event_recorder` as a config variable to
decide whether to use `RayTaskEventRecorder` for sending task events to
aggregator agent as follows:
- The event framework is used only when `enable_ray_task_event_recorder`
and `enable_ray_event` are both set.
- If both are set, `task_event_buffer` -> aggregator agent path is
disabled.
- If either is not set, `task_event_buffer` -> aggregator agent path is
enabled/disabled based on the existing flag.

 **TESTING**:
To test correct working of ray task event recorder, I turned changed the
apt flags and triggered the CI suite in this PR:
#65016
Commit where I change flags:
ba61a94

---------

Signed-off-by: Kartica Modi <karticamodi@gmail.com>
(cherry picked from commit d126844)
Signed-off-by: elliot-barn <elliot.barnwell@anyscale.com>

@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 integrates the RayTaskEventRecorder path to record and export task definition, lifecycle, and profile events to the event aggregator, while disabling the legacy TaskEventBuffer aggregator path when active to prevent double reporting. The review feedback highlights several critical improvements: a potential null pointer dereference in CoreWorkerProcessImpl if the recorder is dynamically enabled, the risk of crashing the process via RAY_CHECK(false) on duplicate observability events, and an anti-pattern of using std::optional which prevents moving the TaskStateUpdate parameter and causes unnecessary copies.

Comment on lines +318 to +320
PeriodicalRunner::Create(observability::RayTaskEventRecorder::Enabled()
? ray_task_event_recorder_io_context_->GetIoService()
: io_service_),

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.

high

Potential null pointer dereference. If observability::RayTaskEventRecorder::Enabled() is dynamically enabled in tests after CoreWorkerProcessImpl is constructed, ray_task_event_recorder_io_context_ will be nullptr, causing a crash when calling GetIoService(). We should check that ray_task_event_recorder_io_context_ is not null before accessing it.

Suggested change
PeriodicalRunner::Create(observability::RayTaskEventRecorder::Enabled()
? ray_task_event_recorder_io_context_->GetIoService()
: io_service_),
PeriodicalRunner::Create((observability::RayTaskEventRecorder::Enabled() && ray_task_event_recorder_io_context_ != nullptr)
? ray_task_event_recorder_io_context_->GetIoService()
: io_service_),

Comment on lines +49 to +56
void RayActorTaskDefinitionEvent::MergeData(
RayEvent<rpc::events::ActorTaskDefinitionEvent> &&other) {
RAY_CHECK(false) << absl::StrFormat(
"MergeData called on actor task definition event for task %s attempt %d; only "
"one definition event is expected per task attempt.",
absl::BytesToHexString(data_.task_id()),
data_.task_attempt());
}

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.

high

Crashing the entire process with RAY_CHECK(false) because of a duplicate observability event is extremely risky in production. Observability and metrics code should be fail-safe and never crash the main application. If a duplicate event is received, we should log a warning or make it a no-op instead of crashing.

Suggested change
void RayActorTaskDefinitionEvent::MergeData(
RayEvent<rpc::events::ActorTaskDefinitionEvent> &&other) {
RAY_CHECK(false) << absl::StrFormat(
"MergeData called on actor task definition event for task %s attempt %d; only "
"one definition event is expected per task attempt.",
absl::BytesToHexString(data_.task_id()),
data_.task_attempt());
}
void RayActorTaskDefinitionEvent::MergeData(
RayEvent<rpc::events::ActorTaskDefinitionEvent> &&other) {
RAY_LOG_EVERY_N(WARNING, 100) << absl::StrFormat(
"MergeData called on actor task definition event for task %s attempt %d; only "
"one definition event is expected per task attempt.",
absl::BytesToHexString(data_.task_id()),
data_.task_attempt());
}

Comment on lines +48 to +55
void RayTaskDefinitionEvent::MergeData(
RayEvent<rpc::events::TaskDefinitionEvent> &&other) {
RAY_CHECK(false) << absl::StrFormat(
"MergeData called on task definition event for task %s attempt %d; only one "
"definition event is expected per task attempt.",
absl::BytesToHexString(data_.task_id()),
data_.task_attempt());
}

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.

high

Crashing the entire process with RAY_CHECK(false) because of a duplicate observability event is extremely risky in production. Observability and metrics code should be fail-safe and never crash the main application. If a duplicate event is received, we should log a warning or make it a no-op instead of crashing.

Suggested change
void RayTaskDefinitionEvent::MergeData(
RayEvent<rpc::events::TaskDefinitionEvent> &&other) {
RAY_CHECK(false) << absl::StrFormat(
"MergeData called on task definition event for task %s attempt %d; only one "
"definition event is expected per task attempt.",
absl::BytesToHexString(data_.task_id()),
data_.task_attempt());
}
void RayTaskDefinitionEvent::MergeData(
RayEvent<rpc::events::TaskDefinitionEvent> &&other) {
RAY_LOG_EVERY_N(WARNING, 100) << absl::StrFormat(
"MergeData called on task definition event for task %s attempt %d; only one "
"definition event is expected per task attempt.",
absl::BytesToHexString(data_.task_id()),
data_.task_attempt());
}

const std::string &session_name,
const NodeID &node_id,
bool include_task_info = false,
std::optional<const TaskStatusEvent::TaskStateUpdate> state_update = std::nullopt);

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.

medium

Using const inside std::optional (i.e., std::optional<const T>) is an anti-pattern because it prevents moving the contained value. This causes an unnecessary copy of TaskStateUpdate when passing it to the TaskStatusEvent constructor. Removing const allows the value to be moved efficiently.

Suggested change
std::optional<const TaskStatusEvent::TaskStateUpdate> state_update = std::nullopt);
std::optional<TaskStatusEvent::TaskStateUpdate> state_update = std::nullopt);

Comment on lines +559 to +570
void RecordTaskStatusEventToRecorderIfNeeded(
ray::observability::RayEventRecorderInterface &ray_task_event_recorder,
const TaskID &task_id,
const JobID &job_id,
int32_t attempt_number,
const TaskSpecification &spec,
rpc::TaskStatus status,
int64_t timestamp,
const std::string &session_name,
const NodeID &node_id,
bool include_task_info,
std::optional<const TaskStatusEvent::TaskStateUpdate> state_update) {

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.

medium

Remove const from std::optional to allow moving the TaskStateUpdate parameter instead of copying it.

Suggested change
void RecordTaskStatusEventToRecorderIfNeeded(
ray::observability::RayEventRecorderInterface &ray_task_event_recorder,
const TaskID &task_id,
const JobID &job_id,
int32_t attempt_number,
const TaskSpecification &spec,
rpc::TaskStatus status,
int64_t timestamp,
const std::string &session_name,
const NodeID &node_id,
bool include_task_info,
std::optional<const TaskStatusEvent::TaskStateUpdate> state_update) {
void RecordTaskStatusEventToRecorderIfNeeded(
ray::observability::RayEventRecorderInterface &ray_task_event_recorder,
const TaskID &task_id,
const JobID &job_id,
int32_t attempt_number,
const TaskSpecification &spec,
rpc::TaskStatus status,
int64_t timestamp,
const std::string &session_name,
const NodeID &node_id,
bool include_task_info,
std::optional<TaskStatusEvent::TaskStateUpdate> state_update) {

@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 de589ab. Configure here.

const std::string &session_name,
const NodeID &node_id,
bool include_task_info = false,
std::optional<const TaskStatusEvent::TaskStateUpdate> state_update = std::nullopt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing Doxygen on C++ APIs

Low Severity

⚠️ Document functions and classes with Doxygen /** ... */ block comments using @ tags (@brief, and @param/@return where applicable).

Additional Locations (2)
Fix in Cursor Fix in Web

Triggered by project rule: Bugbot Rules

Reviewed by Cursor Bugbot for commit de589ab. Configure here.

@ray-gardener ray-gardener Bot added core Issues that should be addressed in Ray Core release-test release test labels Aug 7, 2026
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 release-test release test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants