Comprehensive architectural diagrams and explanatory text covering every major Ray subsystem implemented in this project.
Shreedhar Kodate·02 January 2025·5 min read
ray-engineeringdocs
Comprehensive architectural diagrams and explanatory text covering every
major Ray subsystem implemented in this project.
1. Ray Cluster Architecture
A Ray cluster consists of a Head Node and one or more Worker Nodes.
All coordination flows through the Global Control Service (GCS) on the head
node, while actual task and object data flows directly between workers.
Single source of truth for cluster state. Stores actor handles, node liveness, job info.
Raylet
Per-node daemon. Maintains a local task queue, manages the local Plasma Store, and communicates with the GCS.
Plasma Store
Shared-memory object store (Apache Arrow format). Workers on the same node read objects via zero-copy mmap. Cross-node transfers go over the network.
Distributed Scheduler
Matches task resource requirements (num_cpus, num_gpus, custom resources) to available worker slots.
Dashboard
Web UI at :8265 showing task throughput, actor states, object store usage, and logs.
2. Ray Data Pipeline Architecture
Ray Data provides a lazy, parallel Dataset abstraction built on top of Ray
tasks. Transformations are fused and executed in a streaming fashion to
avoid materialising large intermediate datasets.
Block: the unit of parallelism in Ray Data. Each block is a Pandas
DataFrame stored as a plasma object.
map_batches(fn): applies fn to each block in parallel using Ray
tasks. The batch_format="numpy" option returns NumPy arrays for
compute-intensive transforms.
Lazy execution: transformations are not executed until .take(),
.iter_batches(), or a trainer consumes the dataset.
Streaming ingestion: during training, Ray Data streams blocks from
disk / object store directly into the training loop, avoiding OOM.
3. Actor Communication Sequence
This diagram shows the message flow when multiple workers push gradients to
a centralised ParameterServer actor.
sequenceDiagram
participant Driver as Driver Process
participant GCS as Global Control Service
participant PS as ParameterServer Actor
participant W0 as Worker 0
participant W1 as Worker 1
Driver->>GCS: Create actor (ParameterServer)
GCS-->>Driver: ActorHandle ref
Driver->>GCS: Create actor (Worker 0)
Driver->>GCS: Create actor (Worker 1)
GCS-->>Driver: ActorHandle refs
loop Training Iteration
Driver->>PS: ps.get_params.remote()
PS-->>Driver: ObjectRef[params]
Driver->>Driver: params = ray.get(ref)
par Parallel Gradient Computation
Driver->>W0: w0.compute_gradient.remote(params, step)
Driver->>W1: w1.compute_gradient.remote(params, step)
end
W0-->>Driver: ObjectRef[grad_0]
W1-->>Driver: ObjectRef[grad_1]
Driver->>Driver: grads = ray.get([ref0, ref1])
Driver->>PS: ps.apply_gradients.remote(grad_0, grad_1)
PS->>PS: average gradients, SGD step
PS-->>Driver: ObjectRef[new_params]
Driver->>Driver: new_params = ray.get(ref)
end
Driver->>GCS: ray.shutdown()
GCS->>PS: terminate actor
GCS->>W0: terminate actor
GCS->>W1: terminate actor
graph LR
Client["HTTP Client\nPOST /predict\n{features: [...]}"]
subgraph RayServe["Ray Serve"]
Ingress["InferencePipeline\n(num_replicas=1)\nIngress deployment"]
subgraph PreprocessorPool["Preprocessor (num_replicas=2)"]
P0["Preprocessor\nReplica 0"]
P1["Preprocessor\nReplica 1"]
end
subgraph ClassifierPool["BatchClassifier (num_replicas=2)"]
C0["BatchClassifier\nReplica 0"]
C1["BatchClassifier\nReplica 1"]
end
end
Client -->|HTTP| Ingress
Ingress -->|DeploymentHandle| P0
Ingress -->|DeploymentHandle| P1
P0 -->|normalised features| C0
P1 -->|normalised features| C1
subgraph Batching["@serve.batch (max_batch_size=32)"]
C0 -->|coalesced inference| Model["PyTorch\nModel"]
C1 -->|coalesced inference| Model
end
Model -->|predictions| Ingress
Ingress -->|JSON response| Client
@serve.batch behaviour
When multiple requests arrive concurrently at BatchClassifier, Ray Serve
coalesces them into a single call to _batched_predict() up to
max_batch_size. The batch_wait_timeout_s controls how long Serve waits
to fill a batch before dispatching a partial one.