Repository navigation
feat(spider-scheduler): Add gRPC scheduler server: #369
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
Changes from 16 commits
de929da
182d09d
a82298d
bea09d5
09c351b
3baaf8b
7c82fef
c7b2216
351b00f
b3d3407
c876a54
04e3e1e
50a562f
bbf288d
5edb5b8
41f0a80
fa97dd6
4f97796
dbc5732
11d6bcc
3fe90a5
21911fa
5574bfe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| //! [`RequestUnpack`] implementations for `scheduler.proto` requests. | ||
|
|
||
| use std::time::Duration; | ||
|
|
||
| use spider_core::types::{id::ExecutionManagerId, scheduler::TaskAssignmentRecord}; | ||
|
|
||
| use crate::{ | ||
| scheduler::{ | ||
| HeartbeatRequest, | ||
| NextTaskRequest, | ||
| ShutdownRequest, | ||
| TaskAssignmentRecord as ProtoTaskAssignmentRecord, | ||
| }, | ||
| unpack::{RequestUnpack, UnpackError}, | ||
| }; | ||
|
|
||
| /// Unpacks [`NextTaskRequest`] into a tuple containing: | ||
| /// | ||
| /// * The execution manager ID. | ||
| /// * The previously consumed assignment record, if any. | ||
| /// * The maximum duration to wait for an assignment. | ||
| impl RequestUnpack for NextTaskRequest { | ||
| type Unpacked = (ExecutionManagerId, Option<TaskAssignmentRecord>, Duration); | ||
|
|
||
| fn unpack(self) -> Result<Self::Unpacked, UnpackError> { | ||
| Ok(( | ||
| ExecutionManagerId::from(self.execution_manager_id), | ||
| self.prev_assignment.map(ProtoTaskAssignmentRecord::into), | ||
| Duration::from_millis(self.wait_time_ms), | ||
| )) | ||
| } | ||
| } | ||
|
|
||
| /// Unpacks [`HeartbeatRequest`] into an [`ExecutionManagerId`]. | ||
| impl RequestUnpack for HeartbeatRequest { | ||
| type Unpacked = ExecutionManagerId; | ||
|
|
||
| fn unpack(self) -> Result<Self::Unpacked, UnpackError> { | ||
| Ok(ExecutionManagerId::from(self.execution_manager_id)) | ||
| } | ||
| } | ||
|
|
||
| /// Unpacks [`ShutdownRequest`] into a tuple containing: | ||
| /// | ||
| /// * The execution manager ID. | ||
| /// * The previously consumed assignment records. | ||
| impl RequestUnpack for ShutdownRequest { | ||
| type Unpacked = (ExecutionManagerId, Vec<TaskAssignmentRecord>); | ||
|
|
||
| fn unpack(self) -> Result<Self::Unpacked, UnpackError> { | ||
| Ok(( | ||
| ExecutionManagerId::from(self.execution_manager_id), | ||
| self.prev_assignments | ||
| .into_iter() | ||
| .map(ProtoTaskAssignmentRecord::into) | ||
| .collect(), | ||
| )) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| //! Command-line entrypoint for the scheduler gRPC server. | ||
|
|
||
| use std::{error::Error, net::SocketAddr, path::PathBuf}; | ||
|
|
||
| use clap::Parser; | ||
| use spider_proto_rust::scheduler::scheduler_service_server::SchedulerServiceServer; | ||
| use spider_scheduler::{ | ||
| GrpcSchedulerStorageClient, | ||
| ServerConfig, | ||
| create_runtime, | ||
| grpc::GrpcSchedulerService, | ||
| }; | ||
| use spider_utils::{config::YamlConfig, logging::set_up_logging}; | ||
| use tokio::select; | ||
| use tonic::transport::Server; | ||
|
|
||
| /// Command-line arguments for the scheduler gRPC server. | ||
| #[derive(Debug, Parser)] | ||
| #[command(about = "Run the Spider scheduler gRPC server.")] | ||
| struct Cli { | ||
| /// Path to the YAML server configuration file. | ||
| #[arg(short, long, value_name = "PATH")] | ||
| config: PathBuf, | ||
| } | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> Result<(), Box<dyn Error>> { | ||
| let _log_guard = set_up_logging(); | ||
| let cli = Cli::parse(); | ||
| let server_config = ServerConfig::from_yaml_file(&cli.config)?; | ||
| let listen_addr = SocketAddr::new(server_config.runtime.host, server_config.runtime.port); | ||
|
|
||
| let storage_client = GrpcSchedulerStorageClient::connect( | ||
| server_config.storage_endpoint.endpoint()?, | ||
| server_config.storage_connection_pool_size, | ||
| ) | ||
| .await?; | ||
|
|
||
| let (runtime, service, cancellation_token) = | ||
| create_runtime(server_config.runtime, storage_client).await?; | ||
| let grpc_service = GrpcSchedulerService::new(service, cancellation_token.clone()); | ||
| tracing::info!(listen_addr = % listen_addr, "Starting scheduler gRPC server."); | ||
|
|
||
| let serve_result = Server::builder() | ||
| .add_service(SchedulerServiceServer::new(grpc_service)) | ||
| .serve_with_shutdown(listen_addr, async move { | ||
| select! { | ||
| () = cancellation_token.cancelled() => { | ||
| tracing::info!("Shutting down scheduler gRPC server."); | ||
| } | ||
| result = tokio::signal::ctrl_c() => { | ||
| if let Err(error) = result { | ||
| tracing::error!(error = % error, "Failed to listen for Ctrl-C."); | ||
| } | ||
| cancellation_token.cancel(); | ||
| } | ||
| } | ||
| }) | ||
| .await; | ||
|
|
||
| let stop_result = runtime.stop().await; | ||
| serve_result?; | ||
| stop_result?; | ||
| Ok(()) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -1,14 +1,36 @@ | ||||
| //! The scheduler core configuration that selects and configures the scheduling algorithm. | ||||
| //! Scheduler configuration: the top-level server configuration and the scheduler core configuration | ||||
| //! that selects the scheduling algorithm. | ||||
|
|
||||
| use std::num::NonZeroUsize; | ||||
|
|
||||
| use serde::Deserialize; | ||||
| use spider_utils::config::EndpointConfig; | ||||
|
|
||||
| use crate::{ | ||||
| core::SchedulerCore, | ||||
| core_impl::RoundRobinConfig, | ||||
| dispatch_queue::DispatchQueueSink, | ||||
| runtime::RuntimeConfig, | ||||
| storage_client::SchedulerStorageClient, | ||||
| }; | ||||
|
|
||||
| /// Top-level configuration for the scheduler gRPC server. | ||||
| /// | ||||
| /// Wraps the scheduler [`RuntimeConfig`] (which also supplies the gRPC listen `host`/`port`) and | ||||
| /// adds the storage endpoint and connection-pool size that [`crate::create_runtime`] deliberately | ||||
| /// leaves out, since it receives the storage client as a parameter. | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if this is your judgment to keep it, or you just missed it: this part is 100% AI-generated and it should be removed. |
||||
| #[derive(Clone, Debug, Deserialize)] | ||||
| pub struct ServerConfig { | ||||
| /// The storage gRPC endpoint the scheduler registers with and polls for ready tasks. | ||||
| pub storage_endpoint: EndpointConfig, | ||||
|
|
||||
| /// The number of connections per pool used to reach the storage service. | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have an example of how to document this, right?
|
||||
| pub storage_connection_pool_size: NonZeroUsize, | ||||
|
|
||||
| /// The scheduler runtime configuration (also supplies the gRPC listen host/port). | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think |
||||
| pub runtime: RuntimeConfig, | ||||
| } | ||||
|
|
||||
| /// The configuration that selects and configures the scheduler core's scheduling algorithm. | ||||
| #[derive(Clone, Debug, Deserialize)] | ||||
| #[serde(rename_all = "snake_case")] | ||||
|
|
||||
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.
I have implemented the EM binary as an example and asked you to review in #362.
And clearly this PR doesn't follow what I've done there.