> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockx.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 架构总览

> BlockX 是什么、由哪些组件构成、一个 task 如何在系统里流动，以及贡献者应该按什么顺序阅读文档。

BlockX 是一个面向链上数据的函数执行框架。它接收 Client 提交的 task，在 Worker 上扫描触发数据、并行执行用户编写的轻量 Python 函数，再把结果写回 BlockDB。核心计算模式是 `onchain table -> onchain table` 的单行状态计算。

BlockX 只负责计算执行。task 生成、DAG 编排、触发器管理和定时任务都由 Client 负责。

这一组页面面向要给 BlockX 贡献代码的开发者。如果你只想知道"代码在哪、改什么该看哪里"，直接去 [仓库结构与代码分层](/architecture/repository-layout)。

## 一句话看懂系统

<Steps>
  <Step title="Client 申请执行资源">
    Client 向任意一个 Coordinator 调用 `ReserveWorkerSlot(taskId)`。Coordinator 基于 etcd 中的 Worker 心跳选一个候选 Worker，向它调用 `RequestTaskSlot`，把 `workerAddr + slotId` 交回 Client。Client 也可以跳过 Coordinator 直连 Worker。
  </Step>

  <Step title="Client 提交 task">
    Client 向目标 Worker 调用 `SubmitTask(task, slotId?)`。Worker 校验并激活 slot，创建 task 上下文，固定本 task 使用的函数代码快照。
  </Step>

  <Step title="Worker 分三个阶段执行">
    Builder 阶段串行运行 Call Builder，扫描触发数据生成 `call list`；Calls 阶段由 dispatcher 把 call 窗口化投递到常驻的 Python Executor 进程池并行执行；Plugin 阶段由 Writer Plugin 把汇聚后的 `outputs` 一次性写入 BlockDB。
  </Step>

  <Step title="Client 取结果">
    `SubmitTask` 只返回提交确认。Client 通过 `GetTaskResult` 或 `WatchTasks` 流从同一个 Worker 拿到终态 `TaskResult`。
  </Step>
</Steps>

完整的端到端时序、slot 状态机和结果模型见 [Task 生命周期](/architecture/task-lifecycle)。

## 组件图

```mermaid theme={null}
flowchart LR
    client[Client]

    subgraph control["控制面"]
        coordinator["Coordinator<br/>(block / bundle 两套)"]
        etcd[("etcd<br/>Worker 注册 / 心跳")]
    end

    subgraph worker["执行面 / Worker 进程"]
        wcore["WorkerCore<br/>slot / task 状态机"]
        builder["Call Builder<br/>dbScan / callList / bundleScan"]
        dispatcher["DispatcherCore<br/>窗口化放量 / Executor 选择"]
        fcode["Function Code View<br/>按 epoch 固定函数版本"]
        io["IO 访问子系统<br/>TaskIOScope / 缓存 / 准入"]
        plugin["Writer Plugin<br/>blockdbWrite / tableUpserts / bundleWrite"]
    end

    subgraph executors["Python Executor 进程池"]
        exec["Executor + SDK hook"]
    end

    subgraph backends["外部依赖"]
        blockdb[("BlockDB")]
        meta[("Meta")]
        rpc[("链节点 RPC")]
    end

    syncinv["Sync Invoker<br/>(独立进程，同步调用函数，自带 Executor 池)"]

    client -->|"ReserveWorkerSlot (gRPC)"| coordinator
    coordinator <-->|watch| etcd
    coordinator -->|"RequestTaskSlot (gRPC)"| wcore
    client -->|"SubmitTask / GetTaskResult / WatchTasks (gRPC)"| wcore
    wcore -->|注册 / 心跳| etcd

    wcore --> builder --> dispatcher --> plugin
    dispatcher -->|"Get(functionId, epoch)"| fcode
    dispatcher <-->|"UDS: ExecuteCall / CallWaiting / CallFinished"| exec
    exec -.->|"IO 与子函数请求经 UDS 回到 Worker"| io
    builder --> io
    plugin --> io
    io --> blockdb
    io --> meta
    io --> rpc
    fcode -->|订阅函数代码变更| blockdb

    client -->|"Invoke (gRPC)"| syncinv
    syncinv -.->|"复用 executor adapter / FCV / IO 代码"| exec
```

图中每个方框都对应一个组件页。`Sync Invoker` 复用 Worker 的 executor adapter、Pool Manager、Function Code View 和 IO 子系统代码，在自己的进程里拉起独立的 Executor 池，但不走 slot / task 流程；`bundle` 集群用同一套代码、不同的进程 profile 和 etcd 注册前缀部署，专门跑大范围 bundle 回填任务。

## 组件与职责

| 组件                 | 一句话职责                                                                              | 主要代码                                                                                          | 文档                                              |
| ------------------ | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| Coordinator        | 基于 etcd 心跳视图选址，替 Client 向 Worker 预占 slot；无中心状态，可多实例                                | `internal/coordinator/`、`cmd/coordinator/`、`cmd/bundle_coordinator/`                          | [Coordinator](/components/coordinator)          |
| Worker             | 执行面真相节点：slot 分配与回收、task 三阶段执行、结果短期缓存、etcd 心跳                                       | `internal/worker/`、`cmd/worker/`、`cmd/bundle_worker/`                                         | [Worker](/components/worker)                    |
| Call 执行子系统         | dispatcher 窗口化调度 call、选择 Executor、处理子函数调用；Executor Pool 管理 Python 进程与 UDS 通道       | `internal/worker/core/dispatcher*.go`、`internal/worker/adapters/executor/`、`api/uds/`         | [Call 执行子系统](/components/call-execution)        |
| Function Code View | Worker 内的函数代码版本化视图；task 激活时固定 `taskCodeEpoch`；含函数代码静态审计                            | `internal/functioncode/`、`python/blockx_audit/`                                               | [Function Code View](/components/function-code) |
| IO 访问子系统           | 所有 BlockDB / RPC / Meta 访问的统一入口：Worker 级缓存、task 内 singleflight、IO 窗口、backend 自适应准入 | `internal/io/`                                                                                | [IO 访问子系统](/components/io-subsystem)            |
| Plugin 系统          | Call Builder 把触发数据变成 `call list`；Writer Plugin 把 `outputs` 落库                      | `internal/plugin/`                                                                            | [Plugin 系统](/components/plugins)                |
| Python Executor    | 常驻 Python 进程，用 `greenlet` 执行用户函数并在 IO 时挂起；SDK hook 把 IO 请求拦截回 Worker               | `python/blockx_executor/`、`python/blockx_sdk/`                                                | [Python Executor](/components/python-executor)  |
| 后端适配器              | BlockDB、NodeRPC、Meta、Iceberg 等外部服务的 Go client                                      | `internal/sdk/`                                                                               | [后端适配器](/components/backend-adapter)            |
| Sync Invoker       | 面向同步 request/response 的函数调用入口，复用 Executor 池代码但不走 task 流程                           | `internal/syncinvoker/`、`cmd/syncinvoker/`                                                    | [Sync Invoker](/components/sync-invoker)        |
| Bundle 集群          | 用独立进程 profile 跑 bundle 回填：bundleScan builder、bundleWrite / tableUpserts plugin     | `cmd/bundle_*`、`internal/plugin/callbuilder/bundlescan/`、`internal/plugin/event/bundlewrite/` | [Bundle 集群](/components/bundle)                 |
| 可观测性               | 结构化日志、Prometheus 指标、tracing、usage 采集、client id 传播                                  | `internal/obs/`、`internal/usage/`、`internal/common/clientid/`                                 | [可观测性](/components/observability)               |

## 核心约束

这些约束贯穿所有组件的设计。改动代码时如果发现自己在打破其中一条，先回头看对应 spec。

* **以 task 为中心**：task 是最小调度单元，只在一个 Worker 上执行；所有缓存和状态共享都锚定在 task 上，task 结束即释放。
* **task 内 call 全并行**：call 之间没有顺序依赖，共享同一时刻的只读世界状态。
* **同一 task 内函数版本固定**：task 激活时固定 `taskCodeEpoch`，之后的顶层 call 和子函数调用都基于同一份代码快照解析；代码热更新只影响新 task。
* **Python 侧 IO 必须可劫持**：用户函数通过 SDK 发起的 BlockDB / RPC 请求都被 hook 回 Worker 统一处理，不允许 Executor 直连外部存储。
* **函数只读、写入走 Plugin**：用户函数不能写 BlockDB；每个 task 至多一个 Writer Plugin，在 Plugin 阶段一次性写入。
* **不做 task 级自动重试，不支持取消**：框架只提供超时；是否重投由 Client 根据 `TaskResult.retryable` 决定。Worker 内部允许对单个 call 做有界 attempt 重试。
* **Worker 本地 slot 表是唯一真相**：Coordinator 只维护可丢弃的派生视图，etcd 只承载注册和心跳。多个 Coordinator 同时选中同一 Worker 时，在 Worker 的原子容量检查处收敛。
* **`TaskResult` 只表达 task 级结果**：不返回每个 call 的返回值；终态只有 `SUCCEEDED / FAILED`，失败时用错误码和 `retryable` 表达。

## 三条设计原则

* **Sans-IO**：状态机、调度决策、协议映射收敛在纯内存 `core/`，adapter 承担 RPC、UDS、存储、时钟和观测。`WorkerCore`、`CoordinatorCore`、`DispatcherCore` 都按"输入事件 → core 决策 → adapter 执行命令"的单向模式工作。
* **Scoped Resource Context**：IO 访问这类以资源拥有权和生命周期回收为中心的子系统，按 Worker 级共享作用域与 task 级局部作用域组织，把 quota、cache、singleflight、deadline 绑定到作用域生命周期。
* **Occam's Razor**：如无必要，勿增实体。优先复用既有概念、状态、接口、错误码和协议。

原则如何落到代码约束，见 [设计原则与代码约定](/architecture/design-principles)。

## 不适用场景

BlockX 不适合以下计算：

* 依赖同一张表大量行数据的计算，例如大范围聚合、复杂窗口统计、K 线计算。
* 需要任意时间窗口状态和大规模有状态计算的场景。
* 强依赖流式引擎 exactly-once 状态恢复的复杂计算。
* 普通在线表的任意增量流式计算。

## 建议阅读顺序

<Steps>
  <Step title="先看主链路">
    [Task 生命周期](/architecture/task-lifecycle) 和 [协议与接口](/architecture/protocols)。读完你应该能说出一个 task 经过哪些 RPC、哪些阶段、哪些状态。
  </Step>

  <Step title="再看仓库怎么分层">
    [仓库结构与代码分层](/architecture/repository-layout) 和 [设计原则与代码约定](/architecture/design-principles)。
  </Step>

  <Step title="按要改的组件深入">
    从上面的组件表进入对应页面。每个组件页都列出代码位置、核心类型、扩展点和测试。
  </Step>

  <Step title="动手前看开发指南">
    [本地开发环境](/development/getting-started)、[测试](/development/testing)、[贡献流程](/development/contributing)。
  </Step>
</Steps>

<Info>
  改动行为语义时，先改 BlockX 仓库 `docs/specs/` 下的设计文档，再改代码，两者放在同一个 PR。
</Info>
