villebro opened a new issue, #2484:
URL: https://github.com/apache/datafusion-ballista/issues/2484

   ## Motivation
   
   When a job completes, the scheduler returns each output partition's 
location, including the executor's Flight host and port. By default the client 
fetches every partition directly from its executor. Where clients can't reach 
executors, as is common on Kubernetes and in isolated networks, the scheduler 
can proxy the fetches instead and relay the result bytes itself.
   
   ```
     peer-to-peer:     client ──fetch──▶ each executor
     scheduler proxy:  client ──fetch──▶ scheduler ──▶ executor   (result bytes 
relayed by the scheduler)
   ```
   
   Neither mode fits these deployments well:
   
   - **Result serving congests the scheduler.** Scheduling is latency-sensitive 
coordination; result serving is bulk transfer that grows with result size and 
client count. When the scheduler proxies, a large result or a slow client 
degrades planning for every other query.
   - **Result serving can't scale on its own.** The proxy scales only with the 
scheduler (in practice, one process), and peer-to-peer capacity is tied to the 
executor count. Trino spooling has workers write result segments in parallel 
while the coordinator only hands out URLs.
   - **There is nowhere to put tiered result storage.** A result lives only on 
its producing executor's disk; if that executor is lost, the result goes with 
it. Memory, cache, or object-storage tiers need a component that owns where a 
result lives and how it is served.
   - **Clients see internal topology.** Executor host and port appear in the 
job-status response and in every fetch ticket, revealing node count, network 
layout and reachable endpoints that serving doesn't need.
   
   Each of these points to a dedicated result service, separate from the 
scheduler and the executors.
   
   ## Proposal
   
   We propose adding an optional, stateless **Result Service** (a new 
`ballista-result-service` binary) between clients and executors. The 
scheduler's embedded result proxy would be deprecated and later removed (see 
below).
   
   ```
     client ──(1) submit──▶ scheduler (control plane only)
        ▲                      │
        └──(2) partition locations + advertised result-service endpoint
     (3) client ──fetch──▶ LB / ingress ──▶ Result Service (N stateless 
replicas) ──▶ executor
   ```
   
   Each fetch ticket already names the executor holding the partition, so a 
replica only has to forward the fetch and stream the bytes back. It is 
stateless and scales as a Kubernetes Deployment behind a Service. Clients reach 
it through the scheduler's existing `advertise_flight_endpoint` setting. Flight 
uses long-lived gRPC streams, so the load balancer or ingress in front of the 
fleet must be gRPC-aware. Since the endpoint is a plain `host:port`, path-based 
routing isn't supported.
   
   Result serving moves into `ballista-core` behind a small backend trait, 
`fetch(ticket) -> stream`, so that the executor and the Result Service share 
one implementation. The Result Service adds a forwarding backend, and future 
storage tiers become further backends.
   
   Once the Result Service exists, the scheduler's embedded proxy is redundant. 
Keeping it indefinitely would mean maintaining a second proxy and would leave a 
supported configuration in which the scheduler remains on the data path. 
However, removing it at the same time the Result Service is introduced would 
leave no compatibility or rollback window for existing deployments.
   
   The transition therefore spans two major releases:
   
   1. In the first major release, add the Result Service and deprecate 
`enable_embedded_flight_proxy`. The embedded proxy continues to work, but emits 
a startup warning directing operators to deploy a Result Service and configure 
`advertise_flight_endpoint`. Documentation and deployment examples recommend 
the Result Service for Kubernetes and isolated networks. Both configurations 
remain supported for the whole release cycle, so deployments can migrate, 
compare behavior under production load, and roll back without downgrading 
Ballista.
   2. In the following major release, remove `enable_embedded_flight_proxy` and 
the scheduler's Flight proxy implementation. `advertise_flight_endpoint` 
remains the way to point clients at a Result Service. Deployments that still 
use the embedded proxy must run at least one Result Service replica before 
upgrading.
   
   Clients, the wire format, executors and peer-to-peer mode stay unchanged 
throughout the transition. The existing `local` variant in 
`GetJobStatusResult.flight_proxy` should remain reserved or accepted for wire 
compatibility even after schedulers stop emitting it.
   The eventual removal of `enable_embedded_flight_proxy` is the breaking 
change, announced one major release in advance. The Result Service is opt-in 
during the deprecation release and becomes the supported proxy mode after 
removal.
                                                                                
                                                                                
                                           Authentication on the result fetch 
path is an existing gap that applies equally to executors today, so we'd 
discuss it separately.
   
   ## What this opens up
   
   This first step fixes congestion and independent scaling. The other two 
motivations, tiered storage and topology hiding, now have somewhere to land, 
but they are separate discussions and are not proposed here.
   
   The most visible gap is that clients still receive executor locations, 
because the fleet just forwards whatever each ticket names. The end state is an 
opaque handle that the service resolves to a location. It would be a new result 
descriptor rather than a change to `PartitionLocation`, which is shared with 
shuffle. Our preference is to resolve handles against the shared state that 
scheduler HA will introduce, which puts this step after that work.
   
   Storage tiers follow naturally. Forwarding is a relay, not a durability 
layer: if the executor is lost before the fetch, the fetch fails just as it 
does with peer-to-peer. Executor memory, a distributed cache, and object 
storage with pre-signed URLs would each be a backend behind the same trait. 
Pre-signed URLs would let clients pull large results straight from storage 
without ever seeing an executor. These tiers bring lifecycle work, such as 
TTLs, deletion after fetch, and bounded memory budgets, and could share a 
storage layer with remote shuffle.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to