forked from apache/datafusion-ballista
-
Notifications
You must be signed in to change notification settings - Fork 0
1925: feat: event log and history server (Spark History Server equivalent) #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
martin-augment
wants to merge
14
commits into
main
Choose a base branch
from
pr-1925-2026-07-10-12-25-22
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7cbcfea
feat(history): add ballista-history crate with event-log schema
andygrove 564a999
feat(history): add async buffered event-log writer
andygrove 20daac9
refactor(scheduler): share REST DTOs and extract graph-to-DTO builders
andygrove c542809
feat(scheduler): add event_log_dir config option
andygrove da38192
feat(scheduler): tee lifecycle events into the event log
andygrove 1eda517
feat(history): add event-log reader producing ReplayedJob
andygrove af575ef
feat(scheduler): add ballista-history-server binary and router
andygrove dcbd2bb
fix(scheduler): harden history-server router against corrupt logs and…
andygrove ed0065c
test(history): verify history store serves byte-identical JSON to liv…
andygrove 9b545d7
fix(history): guarantee terminal JobEnd is not dropped on a full channel
andygrove d919d1b
chore(history): add Apache license header to Cargo.toml
andygrove 7852a91
chore(scheduler): make ballista-history an optional dep gated by rest…
andygrove 09686e3
chore(scheduler): gate serde_json behind rest-api feature
andygrove 53e6f4c
Add AI config files
martin-g File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| When making a code review never link to any issue or a pull request | ||
| in any GitHub repository! | ||
|
|
||
| Do not review AI agents' config files like CLAUDE.md, AGENTS.md or rules.md! | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| When making a code review never link to any issue or a pull request | ||
| in any GitHub repository! | ||
|
|
||
| Do not review AI agents' config files like CLAUDE.md, AGENTS.md or rules.md! | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| When making a code review never link to any issue or a pull request | ||
| in any GitHub repository! | ||
|
|
||
| Do not review AI agents' config files like CLAUDE.md, AGENTS.md or rules.md! | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| When making a code review never link to any issue or a pull request | ||
| in any GitHub repository! | ||
|
|
||
| Do not review AI agents' config files like CLAUDE.md, AGENTS.md or rules.md! | ||
|
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| [package] | ||
| name = "ballista-history" | ||
| description = "Event-log schema, writer, and reader for Ballista's history server" | ||
| license = "Apache-2.0" | ||
| version = "53.0.0" | ||
| edition = { workspace = true } | ||
| rust-version = { workspace = true } | ||
|
|
||
| [dependencies] | ||
| serde = { workspace = true, features = ["derive"] } | ||
| serde_json = "1" | ||
| tokio = { workspace = true, features = ["rt", "sync", "fs", "io-util", "macros"] } | ||
|
|
||
| [dev-dependencies] | ||
| tokio = { workspace = true, features = ["rt", "sync", "fs", "io-util", "macros", "rt-multi-thread"] } | ||
| tempfile = { workspace = true } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| // Licensed to the Apache Software Foundation (ASF) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The ASF licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| //! REST response DTOs shared by the live scheduler API handlers and the history | ||
| //! server, so both serialize byte-identical JSON. | ||
|
|
||
| use serde::{Deserialize, Serialize}; | ||
| use std::collections::BTreeMap; | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct JobResponse { | ||
| pub job_id: String, | ||
| pub job_name: String, | ||
| pub job_status: String, | ||
| pub status: String, | ||
| pub num_stages: usize, | ||
| pub completed_stages: usize, | ||
| pub percent_complete: u8, | ||
| pub start_time: u64, | ||
| pub end_time: u64, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub logical_plan: Option<String>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub physical_plan: Option<String>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub stage_plan: Option<String>, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub enum TaskStatus { | ||
| Running, | ||
| Successful, | ||
| Failed, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct TaskSummary { | ||
| pub id: usize, | ||
| pub status: TaskStatus, | ||
| pub partition_id: u32, | ||
| pub scheduled_time: u64, | ||
| pub launch_time: u64, | ||
| pub start_exec_time: u64, | ||
| pub end_exec_time: u64, | ||
| pub exec_duration: u64, | ||
| pub finish_time: u64, | ||
| pub input_rows: usize, | ||
| pub output_rows: usize, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct Percentiles { | ||
| pub min: u64, | ||
| pub p25: u64, | ||
| pub median: u64, | ||
| pub p75: u64, | ||
| pub max: u64, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct QueryStageSummary { | ||
| pub stage_id: String, | ||
| pub stage_status: String, | ||
| pub input_rows: usize, | ||
| pub output_rows: usize, | ||
| pub elapsed_compute: Option<String>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub stage_plan: Option<String>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub task_duration_percentiles: Option<Percentiles>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub task_input_percentiles: Option<Percentiles>, | ||
| pub tasks: Vec<Option<TaskSummary>>, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct QueryStagesResponse { | ||
| pub stages: Vec<QueryStageSummary>, | ||
| } | ||
|
|
||
| /// Session config as flat key/value pairs (from `SessionConfig::to_props()`), | ||
| /// sorted for stable output. | ||
| pub type JobConfig = BTreeMap<String, String>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| // Licensed to the Apache Software Foundation (ASF) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The ASF licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| //! The on-disk event-log schema. One `HistoryEvent` is serialized per JSONL line. | ||
| //! This is a frozen public projection of the scheduler's internal events; the | ||
| //! embedded DTOs are the stable contract the history server serves. | ||
|
|
||
| use crate::dto::{JobConfig, JobResponse, QueryStagesResponse, TaskStatus}; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| /// Current on-disk schema version, stamped on `JobStart`/`JobEnd`. | ||
| pub const SCHEMA_VERSION: u32 = 1; | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub enum JobEndStatus { | ||
| Succeeded, | ||
| Failed(String), | ||
| } | ||
|
|
||
| /// Metrics captured per finished task on the incremental timeline. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct TaskEndMetrics { | ||
| pub input_rows: u64, | ||
| pub output_rows: u64, | ||
| pub elapsed_compute_nanos: u64, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| #[serde(tag = "ev")] | ||
| pub enum HistoryEvent { | ||
| JobStart { | ||
| version: u32, | ||
| job_id: String, | ||
| job_name: String, | ||
| queued_at: u64, | ||
| submitted_at: u64, | ||
| logical_plan: Option<String>, | ||
| physical_plan: Option<String>, | ||
| }, | ||
| StageStart { | ||
| stage_id: usize, | ||
| partitions: usize, | ||
| }, | ||
| StageEnd { | ||
| stage_id: usize, | ||
| status: String, | ||
| }, | ||
| TaskEnd { | ||
| stage_id: u32, | ||
| partition: u32, | ||
| executor_id: String, | ||
| status: TaskStatus, | ||
| launch_time: u64, | ||
| start_exec_time: u64, | ||
| end_exec_time: u64, | ||
| metrics: TaskEndMetrics, | ||
| }, | ||
| JobEnd { | ||
| version: u32, | ||
| status: JobEndStatus, | ||
| queued_at: u64, | ||
| started_at: u64, | ||
| completed_at: u64, | ||
| job: Box<JobResponse>, | ||
| stages: Box<QueryStagesResponse>, | ||
| config: JobConfig, | ||
| dot: String, | ||
| }, | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use crate::dto::{JobResponse, QueryStagesResponse}; | ||
| use std::collections::BTreeMap; | ||
|
|
||
| #[test] | ||
| fn job_end_round_trips_through_jsonl() { | ||
| let job = JobResponse { | ||
| job_id: "job-1".into(), | ||
| job_name: "q1".into(), | ||
| job_status: "COMPLETED".into(), | ||
| status: "Successful".into(), | ||
| num_stages: 2, | ||
| completed_stages: 2, | ||
| percent_complete: 100, | ||
| start_time: 10, | ||
| end_time: 20, | ||
| logical_plan: Some("Projection".into()), | ||
| physical_plan: Some("ProjectionExec".into()), | ||
| stage_plan: Some("stage plan".into()), | ||
| }; | ||
| let event = HistoryEvent::JobEnd { | ||
| version: SCHEMA_VERSION, | ||
| status: JobEndStatus::Succeeded, | ||
| queued_at: 5, | ||
| started_at: 10, | ||
| completed_at: 20, | ||
| job: Box::new(job), | ||
| stages: Box::new(QueryStagesResponse { stages: vec![] }), | ||
| config: BTreeMap::from([("k".to_string(), "v".to_string())]), | ||
| dot: "digraph {}".into(), | ||
| }; | ||
| let line = serde_json::to_string(&event).unwrap(); | ||
| assert!(line.contains("\"ev\":\"JobEnd\"")); | ||
| let back: HistoryEvent = serde_json::from_str(&line).unwrap(); | ||
| // Re-serialize and compare strings (stable, discriminating). | ||
| assert_eq!(line, serde_json::to_string(&back).unwrap()); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // Licensed to the Apache Software Foundation (ASF) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The ASF licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| pub mod dto; | ||
| pub mod event; | ||
| pub mod reader; | ||
| pub mod writer; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At
ballista/history/src/event.rs:54-64, the on-disk schema usesusizeforStageStart/StageEnd.stage_idbutu32forTaskEnd.stage_id. Sinceusizeis architecture-dependent and the id widths are inconsistent, this can make the “frozen” event-log schema less portable/stable across platforms and consumers.Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.