Chapter 01: Remote Tasks, ObjectRefs & Asynchronous Execution
- Topic Focus •
@ray.remoteFunctions, Futures, Asynchronous Concurrency, and Batch Evaluation - Interactive Challenges • 6 Hands-on Exercises
- Launch Playground in Wasm →
1. Architectural Overview & Control Plane Mechanics
In Ray, Tasks represent stateless, asynchronous functions executed across worker processes. When a task is invoked using .remote(), Ray immediately returns an ObjectRef (a future) without blocking the calling thread.
flowchart LR
Driver["Driver Program<br/><code>compute.remote(x)</code>"] -->|"1. Request Worker Lease"| Raylet["Local Raylet<br/>(Scheduler)"]
Driver -.->|"Immediate Future Return"| ObjectRef["ObjectRef [Future]"]
Raylet -->|"2. Assign Leased Worker"| Worker["Worker Process<br/>(Executes Task)"]
Worker -->|"3. Zero-Copy Store"| Plasma["Plasma Object Store<br/>(/dev/shm)"]
Driver -->|"4. ray.get(ref)"| Plasma
style Driver fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
style Raylet fill:#1e293b,stroke:#f59e0b,stroke-width:2px,color:#f8fafc
style Worker fill:#0f172a,stroke:#34d399,stroke-width:2px,color:#f8fafc
style Plasma fill:#1e1e38,stroke:#c084fc,stroke-width:2px,color:#f8fafc
style ObjectRef fill:#0f172a,stroke:#818cf8,stroke-width:1px,stroke-dasharray: 5 5,color:#f8fafc
Diagram Walkthrough & Core Concepts: - Non-blocking Dispatch: The driver requests a worker lease from the local
Rayletand immediately receives anObjectReffuture without waiting for the computation to finish. - Decoupled Worker Execution: TheRayletschedules the task on an available worker process, which executes the Python function in isolation. - Shared Memory Object Store: Worker return values are written directly to the local node'sPlasma Object Storein shared memory (/dev/shm), enabling zero-copy deserialization whenray.get()is resolved.
2. Annotated Python Code Anatomy & API Reference
Below is a production pattern for remote tasks, parallel batching, and async wait operations:
import ray
from typing import List
# 1. Initialize Ray cluster connection or local embedded engine
ray.init(ignore_reinit_error=True)
# 2. Decorate stateless function with @ray.remote
@ray.remote(num_cpus=1)
def process_partition(partition_id: int, records: List[dict]) -> dict:
"""Processes a discrete data partition asynchronously across workers."""
transformed = [r["val"] * 2 for r in records if "val" in r]
return {"partition_id": partition_id, "count": len(transformed), "sum": sum(transformed)}
# 3. Launch parallel asynchronous tasks
data_batches = [[{"val": i} for i in range(100)] for _ in range(4)]
futures: List[ray.ObjectRef] = [
process_partition.remote(idx, batch) for idx, batch in enumerate(data_batches)
]
# 4. Resolve futures concurrently with ray.get or non-blocking ray.wait
ready_refs, unready_refs = ray.wait(futures, num_returns=len(futures), timeout=5.0)
results = ray.get(ready_refs)
Key API Parameter Reference
@ray.remote(num_cpus=1, num_gpus=0): Declares the task and its scheduling resource requirements.func.remote(*args, **kwargs): Dispatches the task asynchronously and returns anObjectRef.ray.get(object_refs): Synchronously blocks until the referenced object(s) are resolved and deserialized.ray.wait(object_refs, num_returns=1, timeout=None): Non-blocking partition of finished vs. pending futures.
3. Production Best Practices & Hardening Guidelines
- Avoid Fine-Grained Tasks: Ensure each remote task executes for at least 10–50ms to amortize scheduling and IPC serialization overhead.
- Never Call
ray.get()in Loops: Callingray.get()inside aforloop serializes execution; launch all.remote()calls first, then resolve in bulk. - Pass
ObjectRefs Directly: Pass ObjectRefs into downstream tasks rather than callingray.get()on the driver; Ray will resolve dependencies automatically on the target worker. - Specify Explicit Resource Demands: Always set
num_cpusor memory limits to prevent worker over-subscription. - Handle Timeouts in
ray.wait: Always provide a boundedtimeoutwhen waiting on dynamic tasks to avoid driver deadlocks.
4. Troubleshooting & Diagnostic Workflows
- Worker Process Exhaustion:
- Symptom: Cluster hangs or scheduler logs "Too many pending tasks".
- Fix: Avoid infinite unconstrained task launches; use a semaphore or
ray.wait()pipeline to bound concurrent in-flight tasks. - Driver Memory Spike during
ray.get(): - Symptom: Out-of-memory error on driver node.
- Fix: Process partitions in batches or stream results with generator tasks instead of resolving large datasets to the driver.
- Serialization Failures (
TypeError: cannot pickle...): - Symptom: Task submission fails with serialization errors.
- Fix: Ensure closures and arguments do not capture non-serializable objects (e.g., active database sockets or open file descriptors).
5. Hands-on Practice Exercises
| Exercise ID | Goal / Topic | Playground Link |
|---|---|---|
basics01 |
Ray Init & First Remote Task | Open Exercise basics01 → |
basics02 |
ObjectRefs and ray.get() | Open Exercise basics02 → |
basics03 |
Parallel Pipeline Execution | Open Exercise basics03 → |
basics04 |
Passing ObjectRefs to Tasks | Open Exercise basics04 → |
basics05 |
Dynamic Completion with ray.wait() | Open Exercise basics05 → |
basics06 |
Multiple Returns in Remote Tasks | Open Exercise basics06 → |