This is an automated email from the ASF dual-hosted git repository.
cloud-fan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 029731cc0fb9 [SPARK-57349][CONNECT] Split udf protocol into message
and grpc service.
029731cc0fb9 is described below
commit 029731cc0fb92123cc51c233f3112a32f4c05af8
Author: Haiyang Sun <[email protected]>
AuthorDate: Tue Jun 9 12:00:27 2026 -0700
[SPARK-57349][CONNECT] Split udf protocol into message and grpc service.
### What changes were proposed in this pull request?
This PR splits the UDF worker protocol definition into two `.proto` files
in the `udf-worker-proto` module:
- `udf_protocol.proto` is renamed to `udf_message.proto` and now contains
**only** the protocol's message types (the `Execute`-stream envelopes,
`Init`/`Finish`/`Cancel`/`Data` request and response messages, `ErrorResponse`,
etc.).
- A new `udf_service.proto` holds the gRPC **service** definition --
`service UdfWorker` with its `Execute(stream UdfRequest) returns (stream
UdfResponse)` and `Manage(WorkerRequest) returns (WorkerResponse)` RPCs -- and
imports `udf_message.proto`.
This is a pure reorganization of the protocol definitions. There is no
change to message types, field numbers, or generated message classes, and no
Scala/behavior change.
### Why are the changes needed?
Clarity between message definition and grpc service.
### Does this PR introduce _any_ user-facing change?
No. The protocol is part of the experimental, currently-unused UDF worker
interface, and this PR only reorganizes the `.proto` source layout without
altering the generated message classes.
### How was this patch tested?
Existing build/codegen coverage. Verified `build/sbt
udf-worker-proto/compile` succeeds -- the proto module compiles all four
`.proto` files (including the relocated service definition) into
`protobuf-java` message classes with no gRPC dependency, confirming the split
is well-formed.
### Was this patch authored or co-authored using generative AI tooling?
Yes
Closes #56391 from haiyangsun-db/pr1-udf-proto-split.
Authored-by: Haiyang Sun <[email protected]>
Signed-off-by: Wenchen Fan <[email protected]>
---
udf/worker/README.md | 12 ++--
.../{udf_protocol.proto => udf_message.proto} | 46 +------------
.../proto/src/main/protobuf/udf_service.proto | 75 ++++++++++++++++++++++
3 files changed, 85 insertions(+), 48 deletions(-)
diff --git a/udf/worker/README.md b/udf/worker/README.md
index cf3253fdaa89..2250c8df1053 100644
--- a/udf/worker/README.md
+++ b/udf/worker/README.md
@@ -35,7 +35,8 @@ provisioning service or daemon).
udf/worker/
├── proto/ -- protobuf message classes only
(protobuf-java)
│ worker_spec.proto -- UDFWorkerSpecification protobuf
-│ udf_protocol.proto -- UDF execution protocol (Init, UdfPayload,
...)
+│ udf_message.proto -- UDF execution protocol messages (Init,
UdfPayload, ...)
+│ udf_service.proto -- UdfWorker gRPC service (Execute, Manage)
│ common.proto -- shared enums (UDFWorkerDataFormat, etc.)
│
├── core/ -- abstract interfaces
@@ -50,7 +51,7 @@ udf/worker/
│ DirectWorkerSession.scala -- session backed by a direct process
│
└── grpc/ -- gRPC transport (gRPC runtime confined
here)
- (generated) -- UdfWorkerGrpc service stubs from
proto/udf_protocol.proto
+ (generated) -- UdfWorkerGrpc service stubs from
proto/udf_service.proto
```
The `core/` package defines abstract interfaces that are independent of how
@@ -60,7 +61,7 @@ worker creation where Spark spawns local OS processes. Future
packages
obtaining workers from a provisioning service or daemon.
The `grpc/` module owns the gRPC service-stub generation (from
-`proto/`'s `udf_protocol.proto`) and the gRPC runtime dependencies. Keeping
+`proto/`'s `udf_service.proto`) and the gRPC runtime dependencies. Keeping
gRPC here means `proto/`, `core/`, and their consumers (`core`, `catalyst`,
`sql/core`) carry no gRPC dependency on their classpath.
@@ -74,8 +75,9 @@ Engine -> Worker: Init -> PayloadChunk* -> (DataRequest)* ->
Finish (Cancel)?
Worker -> Engine: InitResponse -> (DataResponse)* ->
(ErrorResponse)? -> (FinishResponse | CancelResponse)
```
-See `udf/worker/proto/src/main/protobuf/udf_protocol.proto` for the complete
-protocol definition, ordering invariants, and error contract.
+See `udf/worker/proto/src/main/protobuf/udf_message.proto` for the complete
+message definitions, ordering invariants, and error contract, and
+`udf_service.proto` for the gRPC service.
### Direct worker creation
diff --git a/udf/worker/proto/src/main/protobuf/udf_protocol.proto
b/udf/worker/proto/src/main/protobuf/udf_message.proto
similarity index 93%
rename from udf/worker/proto/src/main/protobuf/udf_protocol.proto
rename to udf/worker/proto/src/main/protobuf/udf_message.proto
index a66e09af09fe..dd7c9ceb963b 100644
--- a/udf/worker/proto/src/main/protobuf/udf_protocol.proto
+++ b/udf/worker/proto/src/main/protobuf/udf_message.proto
@@ -29,50 +29,10 @@ option java_multiple_files = true;
//
// The Spark engine acts as the gRPC client; a UDF worker (in any
// language) acts as the gRPC server.
-// =====================================================================
-
-// The default UDF gRPC service. A worker that exposes this service
-// MUST do so over the default connection of the worker specification.
//
-// Future revisions of the worker specification may introduce additional
-// dedicated connections for specific purposes (e.g. a separate channel
-// for streaming state store access in stateful UDFs).
-service UdfWorker {
- // Per-execution stream. See [[UdfControlRequest]] for the complete
- // wire protocol and ordering invariants.
- //
- // Error contract: a gRPC error on this stream signals a transport or
- // connection failure. Application-level errors (user code exceptions,
- // worker errors, protocol violations) are reported via [[ErrorResponse]]
- // or [[InitResponse.error]] so the stream lifecycle remains intact.
- //
- // Hanging UDFs: this protocol does not define what constitutes a
- // hanging UDF -- the protocol cannot distinguish user code that is
- // genuinely stuck from code that is merely slow or intentionally
- // long-running. In-band [[Cancel]] is also not a reliable remedy:
- // a Cancel queued behind a blocked batch (or behind a full sender
- // buffer) is never delivered to the worker. Detection and recovery
- // are layered on top of this protocol -- typically via worker-side
- // watchdog timers configurable at the session or UDF level.
- //
- // Stream lifecycle: the engine MUST half-close the request stream
- // (call onCompleted() on the gRPC request side) only after the session
- // outcome is known: on receiving [[FinishResponse]] or [[CancelResponse]]
- // (clean termination), or on receiving a gRPC error. Deferring the
- // half-close keeps the stream open long enough for [[Cancel]] to follow
- // [[Finish]] when needed (see [[Finish]] for the full contract).
- //
- // For stateful execution, the state is maintained per bi-directional
- // stream, mapping to a `WorkerSession` on the engine side.
- rpc Execute(stream UdfRequest) returns (stream UdfResponse);
-
- // Worker-scoped management RPC for heartbeat, capability query, and
- // graceful shutdown. Workers MUST ensure this RPC remains serviceable
- // regardless of how many [[Execute]] streams are in flight; failing to
- // do so can prevent the engine from detecting a hung worker or
- // initiating a clean shutdown.
- rpc Manage(WorkerRequest) returns (WorkerResponse);
-}
+// This file defines only the protocol's message types. The gRPC service
+// that carries them ([[UdfWorker]]) lives in udf_service.proto.
+// =====================================================================
// =====================================================================
// Execute stream -- envelope
diff --git a/udf/worker/proto/src/main/protobuf/udf_service.proto
b/udf/worker/proto/src/main/protobuf/udf_service.proto
new file mode 100644
index 000000000000..2140d4f48bca
--- /dev/null
+++ b/udf/worker/proto/src/main/protobuf/udf_service.proto
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+import "udf_message.proto";
+
+package org.apache.spark.udf.worker;
+
+option java_package = "org.apache.spark.udf.worker";
+option java_multiple_files = true;
+
+// =====================================================================
+// gRPC service definition for the language-agnostic UDF protocol.
+//
+// The service is kept in its own file, separate from the protocol's
+// message definitions (udf_message.proto).
+// =====================================================================
+
+// The default UDF gRPC service. A worker that exposes this service
+// MUST do so over the default connection of the worker specification.
+//
+// Future revisions of the worker specification may introduce additional
+// dedicated connections for specific purposes (e.g. a separate channel
+// for streaming state store access in stateful UDFs).
+service UdfWorker {
+ // Per-execution stream. See [[UdfControlRequest]] for the complete
+ // wire protocol and ordering invariants.
+ //
+ // Error contract: a gRPC error on this stream signals a transport or
+ // connection failure. Application-level errors (user code exceptions,
+ // worker errors, protocol violations) are reported via [[ErrorResponse]]
+ // or [[InitResponse.error]] so the stream lifecycle remains intact.
+ //
+ // Hanging UDFs: this protocol does not define what constitutes a
+ // hanging UDF -- the protocol cannot distinguish user code that is
+ // genuinely stuck from code that is merely slow or intentionally
+ // long-running. In-band [[Cancel]] is also not a reliable remedy:
+ // a Cancel queued behind a blocked batch (or behind a full sender
+ // buffer) is never delivered to the worker. Detection and recovery
+ // are layered on top of this protocol -- typically via worker-side
+ // watchdog timers configurable at the session or UDF level.
+ //
+ // Stream lifecycle: the engine MUST half-close the request stream
+ // (call onCompleted() on the gRPC request side) only after the session
+ // outcome is known: on receiving [[FinishResponse]] or [[CancelResponse]]
+ // (clean termination), or on receiving a gRPC error. Deferring the
+ // half-close keeps the stream open long enough for [[Cancel]] to follow
+ // [[Finish]] when needed (see [[Finish]] for the full contract).
+ //
+ // For stateful execution, the state is maintained per bi-directional
+ // stream, mapping to a `WorkerSession` on the engine side.
+ rpc Execute(stream UdfRequest) returns (stream UdfResponse);
+
+ // Worker-scoped management RPC for heartbeat, capability query, and
+ // graceful shutdown. Workers MUST ensure this RPC remains serviceable
+ // regardless of how many [[Execute]] streams are in flight; failing to
+ // do so can prevent the engine from detecting a hung worker or
+ // initiating a clean shutdown.
+ rpc Manage(WorkerRequest) returns (WorkerResponse);
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]