This is an automated email from the ASF dual-hosted git repository.

nju_yaho pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-ballista.git


The following commit(s) were added to refs/heads/master by this push:
     new 12128a30 Add LaunchMultiTask rpc interface for executor (#255)
12128a30 is described below

commit 12128a30a51315ae348b6a01f3ec42d0a4b41dd5
Author: yahoNanJing <[email protected]>
AuthorDate: Thu Sep 22 09:21:16 2022 +0800

    Add LaunchMultiTask rpc interface for executor (#255)
    
    Co-authored-by: yangzhong <[email protected]>
---
 ballista/rust/core/proto/ballista.proto            | 30 +++++++
 .../rust/core/src/serde/scheduler/from_proto.rs    | 96 ++++++++++++++++++++--
 ballista/rust/core/src/serde/scheduler/mod.rs      | 19 ++++-
 ballista/rust/core/src/serde/scheduler/to_proto.rs | 43 +++++++++-
 ballista/rust/executor/src/executor_server.rs      | 84 ++++++++++++-------
 5 files changed, 230 insertions(+), 42 deletions(-)

diff --git a/ballista/rust/core/proto/ballista.proto 
b/ballista/rust/core/proto/ballista.proto
index 7b7f748c..d998cc7b 100644
--- a/ballista/rust/core/proto/ballista.proto
+++ b/ballista/rust/core/proto/ballista.proto
@@ -534,6 +534,13 @@ message PartitionId {
   uint32 partition_id = 4;
 }
 
+// Multiple partitions in the same stage
+message PartitionIds {
+  string job_id = 1;
+  uint32 stage_id = 2;
+  repeated uint32 partition_ids = 4;
+}
+
 message PartitionStats {
   int64 num_rows = 1;
   int64 num_batches = 2;
@@ -698,6 +705,16 @@ message TaskDefinition {
   repeated KeyValuePair props = 5;
 }
 
+// A set of tasks in the same stage
+message MultiTaskDefinition {
+  PartitionIds task_ids = 1;
+  bytes plan = 2;
+  // Output partition for shuffle writer
+  PhysicalHashRepartition output_partitioning = 3;
+  string session_id = 4;
+  repeated KeyValuePair props = 5;
+}
+
 message SessionSettings {
   repeated KeyValuePair configs = 1;
 }
@@ -836,11 +853,22 @@ message LaunchTaskParams {
   string scheduler_id = 2;
 }
 
+message LaunchMultiTaskParams {
+  // Allow to launch a task set to an executor at once
+  repeated MultiTaskDefinition multi_tasks = 1;
+  string scheduler_id = 2;
+}
+
 message LaunchTaskResult {
   bool success = 1;
   // TODO when part of the task set are scheduled successfully
 }
 
+message LaunchMultiTaskResult {
+  bool success = 1;
+  // TODO when part of the task set are scheduled successfully
+}
+
 message CancelTasksParams {
   repeated PartitionId partition_id = 1;
 }
@@ -876,6 +904,8 @@ service SchedulerGrpc {
 service ExecutorGrpc {
   rpc LaunchTask (LaunchTaskParams) returns (LaunchTaskResult) {}
 
+  rpc LaunchMultiTask (LaunchMultiTaskParams) returns (LaunchMultiTaskResult) 
{}
+
   rpc StopExecutor (StopExecutorParams) returns (StopExecutorResult) {}
 
   rpc CancelTasks (CancelTasksParams) returns (CancelTasksResult) {}
diff --git a/ballista/rust/core/src/serde/scheduler/from_proto.rs 
b/ballista/rust/core/src/serde/scheduler/from_proto.rs
index 728ad46b..536e2a5c 100644
--- a/ballista/rust/core/src/serde/scheduler/from_proto.rs
+++ b/ballista/rust/core/src/serde/scheduler/from_proto.rs
@@ -20,6 +20,7 @@ use datafusion::physical_plan::metrics::{
     Count, Gauge, MetricValue, MetricsSet, Time, Timestamp,
 };
 use datafusion::physical_plan::Metric;
+use std::collections::HashMap;
 use std::convert::TryInto;
 use std::sync::Arc;
 use std::time::Duration;
@@ -30,7 +31,7 @@ use crate::serde::protobuf::action::ActionType;
 use crate::serde::protobuf::{operator_metric, NamedCount, NamedGauge, 
NamedTime};
 use crate::serde::scheduler::{
     Action, ExecutorData, ExecutorMetadata, ExecutorSpecification, PartitionId,
-    PartitionLocation, PartitionStats,
+    PartitionIds, PartitionLocation, PartitionStats, TaskDefinition,
 };
 
 impl TryInto<Action> for protobuf::Action {
@@ -51,15 +52,14 @@ impl TryInto<Action> for protobuf::Action {
     }
 }
 
-impl TryInto<PartitionId> for protobuf::PartitionId {
-    type Error = BallistaError;
-
-    fn try_into(self) -> Result<PartitionId, Self::Error> {
-        Ok(PartitionId::new(
+#[allow(clippy::from_over_into)]
+impl Into<PartitionId> for protobuf::PartitionId {
+    fn into(self) -> PartitionId {
+        PartitionId::new(
             &self.job_id,
             self.stage_id as usize,
             self.partition_id as usize,
-        ))
+        )
     }
 }
 
@@ -94,7 +94,7 @@ impl TryInto<PartitionLocation> for 
protobuf::PartitionLocation {
                         "partition_id in PartitionLocation is 
missing.".to_owned(),
                     )
                 })?
-                .try_into()?,
+                .into(),
             executor_meta: self
                 .executor_meta
                 .ok_or_else(|| {
@@ -263,3 +263,83 @@ impl Into<ExecutorData> for protobuf::ExecutorData {
         ret
     }
 }
+
+#[allow(clippy::from_over_into)]
+impl Into<PartitionIds> for protobuf::PartitionIds {
+    fn into(self) -> PartitionIds {
+        PartitionIds {
+            job_id: self.job_id.clone(),
+            stage_id: self.stage_id as usize,
+            partition_ids: self
+                .partition_ids
+                .into_iter()
+                .map(|partition_id| partition_id as usize)
+                .collect(),
+        }
+    }
+}
+
+impl TryInto<TaskDefinition> for protobuf::TaskDefinition {
+    type Error = BallistaError;
+
+    fn try_into(self) -> Result<TaskDefinition, Self::Error> {
+        let mut props = HashMap::new();
+        for kv_pair in self.props {
+            props.insert(kv_pair.key, kv_pair.value);
+        }
+
+        let task_id = self
+            .task_id
+            .ok_or_else(|| {
+                BallistaError::General("No task id in the 
TaskDefinition".to_owned())
+            })?
+            .into();
+
+        Ok(TaskDefinition {
+            task_id,
+            plan: self.plan,
+            output_partitioning: self.output_partitioning,
+            session_id: self.session_id,
+            props,
+        })
+    }
+}
+
+impl TryInto<Vec<TaskDefinition>> for protobuf::MultiTaskDefinition {
+    type Error = BallistaError;
+
+    fn try_into(self) -> Result<Vec<TaskDefinition>, Self::Error> {
+        let mut props = HashMap::new();
+        for kv_pair in self.props {
+            props.insert(kv_pair.key, kv_pair.value);
+        }
+
+        let plan = self.plan;
+        let output_partitioning = self.output_partitioning;
+        let session_id = self.session_id;
+        let task_ids: PartitionIds = self
+            .task_ids
+            .ok_or_else(|| {
+                BallistaError::General(
+                    "No task ids in the MultiTaskDefinition".to_owned(),
+                )
+            })?
+            .into();
+
+        Ok(task_ids
+            .partition_ids
+            .iter()
+            .map(|partition_id| TaskDefinition {
+                task_id: PartitionId::new(
+                    &task_ids.job_id,
+                    task_ids.stage_id,
+                    *partition_id,
+                ),
+                plan: plan.clone(),
+                output_partitioning: output_partitioning.clone(),
+                session_id: session_id.clone(),
+                props: props.clone(),
+            })
+            .collect())
+    }
+}
diff --git a/ballista/rust/core/src/serde/scheduler/mod.rs 
b/ballista/rust/core/src/serde/scheduler/mod.rs
index 5afdb30e..c1fa78a2 100644
--- a/ballista/rust/core/src/serde/scheduler/mod.rs
+++ b/ballista/rust/core/src/serde/scheduler/mod.rs
@@ -21,12 +21,12 @@ use datafusion::arrow::array::{
     ArrayBuilder, StructArray, StructBuilder, UInt64Array, UInt64Builder,
 };
 use datafusion::arrow::datatypes::{DataType, Field};
-
 use datafusion::physical_plan::ExecutionPlan;
 use datafusion::physical_plan::Partitioning;
 use serde::Serialize;
 
 use crate::error::BallistaError;
+use crate::serde::protobuf::PhysicalHashRepartition;
 
 pub mod from_proto;
 pub mod to_proto;
@@ -268,3 +268,20 @@ impl ExecutePartitionResult {
         &self.stats
     }
 }
+
+/// Unique identifier for the output partitions of a set of tasks.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct PartitionIds {
+    pub job_id: String,
+    pub stage_id: usize,
+    pub partition_ids: Vec<usize>,
+}
+
+#[derive(Debug, Clone)]
+pub struct TaskDefinition {
+    pub task_id: PartitionId,
+    pub plan: Vec<u8>,
+    pub output_partitioning: Option<PhysicalHashRepartition>,
+    pub session_id: String,
+    pub props: HashMap<String, String>,
+}
diff --git a/ballista/rust/core/src/serde/scheduler/to_proto.rs 
b/ballista/rust/core/src/serde/scheduler/to_proto.rs
index 10c841e9..5a7ad394 100644
--- a/ballista/rust/core/src/serde/scheduler/to_proto.rs
+++ b/ballista/rust/core/src/serde/scheduler/to_proto.rs
@@ -22,10 +22,12 @@ use crate::error::BallistaError;
 use crate::serde::protobuf;
 use crate::serde::protobuf::action::ActionType;
 
-use crate::serde::protobuf::{operator_metric, NamedCount, NamedGauge, 
NamedTime};
+use crate::serde::protobuf::{
+    operator_metric, KeyValuePair, NamedCount, NamedGauge, NamedTime,
+};
 use crate::serde::scheduler::{
     Action, ExecutorData, ExecutorMetadata, ExecutorSpecification, PartitionId,
-    PartitionLocation, PartitionStats,
+    PartitionIds, PartitionLocation, PartitionStats, TaskDefinition,
 };
 use datafusion::physical_plan::Partitioning;
 
@@ -234,3 +236,40 @@ impl Into<protobuf::ExecutorData> for ExecutorData {
         }
     }
 }
+
+#[allow(clippy::from_over_into)]
+impl Into<protobuf::PartitionIds> for PartitionIds {
+    fn into(self) -> protobuf::PartitionIds {
+        protobuf::PartitionIds {
+            job_id: self.job_id,
+            stage_id: self.stage_id as u32,
+            partition_ids: self
+                .partition_ids
+                .into_iter()
+                .map(|partition_id| partition_id as u32)
+                .collect(),
+        }
+    }
+}
+
+#[allow(clippy::from_over_into)]
+impl Into<protobuf::TaskDefinition> for TaskDefinition {
+    fn into(self) -> protobuf::TaskDefinition {
+        let props = self
+            .props
+            .iter()
+            .map(|(k, v)| KeyValuePair {
+                key: k.to_owned(),
+                value: v.to_owned(),
+            })
+            .collect::<Vec<_>>();
+
+        protobuf::TaskDefinition {
+            task_id: Some(self.task_id.into()),
+            plan: self.plan,
+            output_partitioning: self.output_partitioning,
+            session_id: self.session_id,
+            props,
+        }
+    }
+}
diff --git a/ballista/rust/executor/src/executor_server.rs 
b/ballista/rust/executor/src/executor_server.rs
index 7bde7f7f..4544d8c2 100644
--- a/ballista/rust/executor/src/executor_server.rs
+++ b/ballista/rust/executor/src/executor_server.rs
@@ -35,10 +35,11 @@ use ballista_core::serde::protobuf::executor_grpc_server::{
 use ballista_core::serde::protobuf::scheduler_grpc_client::SchedulerGrpcClient;
 use ballista_core::serde::protobuf::{
     executor_metric, executor_status, CancelTasksParams, CancelTasksResult,
-    ExecutorMetric, ExecutorStatus, HeartBeatParams, LaunchTaskParams, 
LaunchTaskResult,
-    RegisterExecutorParams, StopExecutorParams, StopExecutorResult, 
TaskDefinition,
-    TaskStatus, UpdateTaskStatusParams,
+    ExecutorMetric, ExecutorStatus, HeartBeatParams, LaunchMultiTaskParams,
+    LaunchMultiTaskResult, LaunchTaskParams, LaunchTaskResult, 
RegisterExecutorParams,
+    StopExecutorParams, StopExecutorResult, TaskStatus, UpdateTaskStatusParams,
 };
+use ballista_core::serde::scheduler::TaskDefinition;
 use ballista_core::serde::{AsExecutionPlan, BallistaCodec};
 use ballista_core::utils::{
     collect_plan_metrics, create_grpc_client_connection, create_grpc_server,
@@ -286,7 +287,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
     ) -> Result<(), BallistaError> {
         let scheduler_id = curator_task.scheduler_id;
         let task = curator_task.task;
-        let task_id = task.task_id.unwrap();
+        let task_id = task.task_id;
         let task_id_log = format!(
             "{}/{}/{}",
             task_id.job_id, task_id.stage_id, task_id.partition_id
@@ -295,10 +296,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
 
         let runtime = self.executor.runtime.clone();
         let session_id = task.session_id;
-        let mut task_props = HashMap::new();
-        for kv_pair in task.props {
-            task_props.insert(kv_pair.key, kv_pair.value);
-        }
+        let task_props = task.props;
 
         let mut task_scalar_functions = HashMap::new();
         let mut task_aggregate_functions = HashMap::new();
@@ -364,7 +362,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
         let task_status = as_task_status(
             execution_result,
             executor_id.clone(),
-            task_id,
+            task_id.into(),
             Some(operator_metrics),
         );
 
@@ -551,28 +549,22 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> TaskRunnerPool<T,
                     }
                 };
                 if let Some(curator_task) = maybe_task {
-                    if let Some(task_id) = &curator_task.task.task_id {
-                        let task_id_log = format!(
-                            "{}/{}/{}",
-                            task_id.job_id, task_id.stage_id, 
task_id.partition_id
-                        );
-                        info!("Received task {:?}", &task_id_log);
-
-                        let server = executor_server.clone();
-                        dedicated_executor.spawn(async move {
-                            
server.run_task(curator_task).await.unwrap_or_else(|e| {
-                                error!(
-                                    "Fail to run the task {:?} due to {:?}",
-                                    task_id_log, e
-                                );
-                            });
+                    let task_id = &curator_task.task.task_id;
+                    let task_id_log = format!(
+                        "{}/{}/{}",
+                        task_id.job_id, task_id.stage_id, task_id.partition_id
+                    );
+                    info!("Received task {:?}", &task_id_log);
+
+                    let server = executor_server.clone();
+                    dedicated_executor.spawn(async move {
+                        server.run_task(curator_task).await.unwrap_or_else(|e| 
{
+                            error!(
+                                "Fail to run the task {:?} due to {:?}",
+                                task_id_log, e
+                            );
                         });
-                    } else {
-                        error!(
-                            "There's no task id in the task definition {:?}",
-                            curator_task
-                        );
-                    }
+                    });
                 } else {
                     info!("Channel is closed and will exit the task receive 
loop");
                     drop(task_runner_complete);
@@ -600,7 +592,9 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorGrpc
             task_sender
                 .send(CuratorTaskDefinition {
                     scheduler_id: scheduler_id.clone(),
-                    task,
+                    task: task
+                        .try_into()
+                        .map_err(|e| Status::invalid_argument(format!("{}", 
e)))?,
                 })
                 .await
                 .unwrap();
@@ -608,6 +602,34 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorGrpc
         Ok(Response::new(LaunchTaskResult { success: true }))
     }
 
+    /// by this interface, it can reduce the deserialization cost for multiple 
tasks
+    /// belong to the same job stage running on the same one executor
+    async fn launch_multi_task(
+        &self,
+        request: Request<LaunchMultiTaskParams>,
+    ) -> Result<Response<LaunchMultiTaskResult>, Status> {
+        let LaunchMultiTaskParams {
+            multi_tasks,
+            scheduler_id,
+        } = request.into_inner();
+        let task_sender = self.executor_env.tx_task.clone();
+        for multi_task in multi_tasks {
+            let multi_task: Vec<TaskDefinition> = multi_task
+                .try_into()
+                .map_err(|e| Status::invalid_argument(format!("{}", e)))?;
+            for task in multi_task {
+                task_sender
+                    .send(CuratorTaskDefinition {
+                        scheduler_id: scheduler_id.clone(),
+                        task,
+                    })
+                    .await
+                    .unwrap();
+            }
+        }
+        Ok(Response::new(LaunchMultiTaskResult { success: true }))
+    }
+
     async fn stop_executor(
         &self,
         request: Request<StopExecutorParams>,

Reply via email to