Processes
The path of one submission
1
Placement
The Client calls
ReserveWorkerSlot(task_id) on any Coordinator. The Coordinator picks a candidate from the Worker view it rebuilds from etcd heartbeats, calls RequestTaskSlot(task_id, ttl_ms) on it, and hands worker_addr + slot_id back to the Client.2
Submission
The Client connects directly to
worker_addr and calls SubmitTask(task, slot_id). The Worker validates the slot, creates the task context, pins the code snapshot, and returns RUNNING. Without slot_id, the Worker requests a slot inline.3
Fetching the result
The Client calls
WatchTasks([task_id]) on the same Worker to subscribe to the terminal state, or polls with GetTaskResult(task_id). The Coordinator is no longer involved.PROXY_SOCKET_PATH. The proxy routes ReserveWorkerSlot to the block or bundle Coordinator by gRPC method path, and forwards WorkerService requests to the corresponding Worker by the x-blockx-worker-addr header. For local development, examples/local_test_service/local_proxy.py stands in for this hop.
Slot
A slot is a task concurrency quota on a Worker:- Each Worker has a fixed number of slots (
TASK_SLOTS, default 50); one task occupies one slot. RequestTaskSlotswitches an idle slot toALLOCATEDand binds it totask_id; if noSubmitTaskarrives within the TTL (SlotTTLMs = 5000on the Coordinator side), the slot is reclaimed automatically.- Once
SubmitTaskpasses validation, the slot entersRUNNING; the slot is released the moment the task converges to a terminal state, independent of the result retention window. - There is no centralized queue. When no slot is available, the Worker returns
NoSlot(gRPCResourceExhausted) and the caller backs off and retries; blockx-py keeps backing off until it gets a slot. - The Worker’s local slot table is the single source of truth. The Coordinator only holds a discardable derived view; when multiple Coordinators pick the same Worker at the same time, the conflict converges at the Worker’s atomic capacity check.
Inside the Worker
Between the Executor and the Worker is one long-lived UDS connection: the Worker sends
ExecuteCall, the Executor replies CallWaiting when the user function issues IO or a sub-call, the Worker handles it and sends ResumeCall, and finally the Executor ends with CallCompleted / CallFailed. The Executor never connects to any external service directly.
Two clusters
The same code starts as two mutually invisible clusters with differentapp.Profile values:
The task protocol, slot state machine, and heartbeat are identical in both clusters. blockx-py picks the cluster automatically from the config and handler:
BlockTableCallConfig goes to block, BlockBundleCallConfig goes to bundle, anything with ReturnValueHandler always goes to block, and InputsCallConfig goes to bundle by default.
Sync Invoker
The Sync Invoker is the second entry point besides tasks: one gRPC unaryInvoke executes a function once and returns the result, call_id, and attributed execution durations on the spot. It reuses the Worker’s Executor pool, UDS protocol, Function Code View, and read-only IO subsystem, but does not go through the Coordinator, has no slots, Builders, or Writer Plugins, and rejects all write requests. blockx-py’s function.call(...) goes through it when running outside an Executor.
Timing parameters quick reference
See “Timing semantics quick reference” in Task lifecycle for the full table.
Summary
- The Coordinator only does placement and slot reservation; it is stateless and can run as multiple instances. The Worker is the single source of truth on the execution plane.
- A slot is a task concurrency quota on a Worker; there is no centralized queue, so back off and retry when none is available.
- Calls run in the Python Executor child processes on the Worker’s own host, and all IO goes back to the Worker’s IO subsystem over UDS.
- block and bundle are two clusters built from the same code with different profiles; the Sync Invoker is a synchronous entry point that bypasses the task flow.
- Architecture overview: component diagram and core constraints.
- Protocols and interfaces: gRPC, UDS, and etcd protocol details.
- Coordinator and Worker: how placement, the slot table, and phase admission are implemented.
- Sync Invoker: implementation of the synchronous invocation entry point.