> ## 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 的 Go + Python 开发环境、构建二进制、跑测试，并在本机拉起一个不依赖外部服务的 worker

这一页告诉你从克隆仓库到本机跑起一个 worker 需要什么。BlockX 是一个 Go 工作区，`python/` 目录放 Python executor 与 SDK。整体架构见 [架构总览](/architecture/overview)，目录分层见 [仓库结构](/architecture/repository-layout)。

## 前置依赖

| 依赖                                                | 版本 / 说明                                                                                                                                                                               | 来源                                                            |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Go                                                | `go.mod` 声明 `go 1.26.4`；`Dockerfile` 用 `golang:1.26-bookworm` 构建                                                                                                                      | `go.mod`、`Dockerfile`                                         |
| C 工具链（CGO）                                        | `cmd/worker` 与 `cmd/bundle_worker` 依赖 `github.com/duckdb/duckdb-go-bindings`，必须 `CGO_ENABLED=1`（默认值）并有 `gcc` 或 `clang`。coordinator、bundle\_coordinator、syncinvoker 可以 `CGO_ENABLED=0` | `go.mod`、`Dockerfile`、`internal/duckdb/borrowed/`             |
| Python                                            | `python/pyproject.toml` 声明 `requires-python = ">=3.12"`；运行时镜像用 `python:3.12-slim`                                                                                                     | `python/pyproject.toml`、`Dockerfile`                          |
| `uv`                                              | 管理 `python/.venv/`；E2E 测试和本地 worker 都从这个 venv 拉起 executor                                                                                                                             | `AGENTS.md`、`internal/testutil/paths.go`                      |
| `protoc` + `protoc-gen-go` + `protoc-gen-go-grpc` | 只在改 `.proto` 后重新生成时需要；生成物已提交                                                                                                                                                          | `Makefile`                                                    |
| GitHub 私有仓库访问                                     | `go.mod` 依赖 `github.com/Chaintable/*` 私有模块，`pyproject.toml` 依赖 `blockx-py`、`blockdb-py` 等私有 git 包。本地需要能通过 git 访问这些仓库                                                                  | `go.mod`、`python/pyproject.toml`、`.github/workflows/test.yml` |

外部服务在本地开发中都有替身，不需要先搭 etcd、BlockDB、Meta 或链节点：

| 生产依赖                | 本地替代                                                                                                                                                                                                                                                                                                                                                      |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| etcd                | 设 `ETCD_ENDPOINTS=none` 跳过注册（`internal/worker/app/app.go` 的 `registerEtcdPublisherAfterReady` 对空值或 `none` 直接返回）。E2E 用 `internal/testutil/etcd.go` 的 `StartEmbeddedEtcd` 在进程内起 `go.etcd.io/etcd/server/v3/embed`                                                                                                                                           |
| BlockDB             | 设 `STUB_BLOCKDB_DELAY_MS`（`0` 合法）时 worker 用 `internal/worker/devstub` 的 `DelayedBackendAdapter`；不设任何 `BLOCKDB_*_ADDR` 也不设该变量时退回 `StubBackendAdapter`（`internal/worker/app/app.go` 的 `blockDBStubDelayFromEnv` 与 `blockdbBackend`）。`cmd/blockdb_grpc_stub` 是 benchmark 用的独立 gRPC stub 进程；测试里还有 `internal/testutil/blockdb.go` 的 `StartMockFunctionBlockDB` |
| Meta（logical-types） | 不设 `META_ADDR` 时用本地 stub，可用 `STUB_LOGICAL_TYPES_SCHEMAS` 喂 schema（`app.go` 的 `stubLogicalTypesSchemas`）                                                                                                                                                                                                                                                   |
| Node RPC            | 不设 `NODE_RPC_ENDPOINT` 时用 dev stub（`docs/deploy.md` §9.2）                                                                                                                                                                                                                                                                                                 |
| Function code 存储    | 未配置 `FUNCTION_CODE_REDIS_URL` 且 BlockDB table read/subscribe 地址不全时，`internal/worker/app/function_code.go` 的 `setupFunctionCodeView` 选 `devstub.MemoryFunctionCodeStore`；`FUNC_DEBUG=true` 可强制 devstub                                                                                                                                                     |
| Usage Kafka         | `BLOCKX_USAGE_DISABLED=true`。不设时 worker 启动探测 broker 失败会拒绝启动（fail-closed）                                                                                                                                                                                                                                                                                  |
| 能力后端示例              | `internal/sdk/localtestservice` 是进程内的示范能力后端，`examples/local_test_service/` 演示如何用 blockx-py 打到本地 worker                                                                                                                                                                                                                                                    |

## 快速开始

<Steps>
  <Step title="克隆并下载 Go 依赖">
    ```bash theme={null}
    git clone git@github.com:Chaintable/blockx.git
    cd blockx
    go mod download        # 或 make deps（额外跑 go mod tidy）
    ```

    私有模块下载失败时先确认 `git` 能访问 `github.com/Chaintable/*`；CI 里通过 `GOPRIVATE=github.com/Chaintable/*` 和 token 注入解决。
  </Step>

  <Step title="安装 Python 依赖">
    ```bash theme={null}
    uv sync --project python
    ```

    这会在 `python/.venv/` 建 venv。`internal/testutil/paths.go` 的 `pythonBin()` 按顺序找 `/tmp/blockx-venv/bin/python`、`python/.venv/bin/python`，都没有才退回 `python3`。venv 缺失时所有 process E2E 会被跳过而不是失败。
  </Step>

  <Step title="编译与单元测试">
    ```bash theme={null}
    go build ./...
    go test ./...
    ```

    首次编译 `cmd/worker` 会链接 DuckDB 静态库，比其他包慢很多；之后走构建缓存。`go test ./...` 会连带跑 process / contract / system E2E（它们不检查 `-short`），需要上一步的 venv；`e2e/perf` 默认被 `TestMain` 跳过。只想跑纯单测用 `go test ./internal/... ./api/...`。
  </Step>

  <Step title="跑 Python 单测">
    ```bash theme={null}
    PYTHONPATH=python uv run --project python python -m unittest discover -s python/tests
    ```
  </Step>

  <Step title="在本机拉起一个 worker">
    下面这组变量来自 `internal/testutil/worker.go` 的 `StartWorkerAt`，是 process E2E 启动真实 worker 用的最小集合：

    ```bash theme={null}
    WORKER_LISTEN=127.0.0.1:9221 \
    WORKER_ADDR=127.0.0.1:9221 \
    EXECUTOR_COUNT=1 \
    PYTHON_BIN=$PWD/python/.venv/bin/python \
    PYTHONPATH=$PWD/python \
    ETCD_ENDPOINTS=none \
    ICEBERG_NAMESPACE=chaintable_test \
    BLOCKX_USAGE_DISABLED=true \
    BLOCKDB_BATCH_WRITE_ADDR=127.0.0.1:1 \
    STUB_BLOCKDB_DELAY_MS=0 \
    go run ./cmd/worker
    ```

    日志出现 `worker listening` 和 `executor started` 即成功。各项含义：

    * `ETCD_ENDPOINTS=none`：不注册到 etcd，也就没有 Coordinator 参与；客户端直接对 worker 调 `RequestTaskSlot` / `SubmitTask`。
    * `ICEBERG_NAMESPACE`：`WorkerFullConfig.Validate` 要求非空（`internal/worker/app/config.go`）。
    * `BLOCKDB_BATCH_WRITE_ADDR`：`cmd/worker` 的 block profile 注册了 `BundleWrite` plugin，`validateProfileRuntimeConfig` 要求该地址非空；本地填一个不可达端口即可。
    * `STUB_BLOCKDB_DELAY_MS=0`：一旦设置了任何 `BLOCKDB_*_ADDR`，worker 会认为 BlockDB 是真实的；这个变量把读路径切回 devstub。

    要提交一个 task 验证链路，按 `examples/local_test_service/README.md` 起 `local_proxy.py` 再跑 `submit.py`。也可以直接跑一条 process E2E 看真实调用：

    ```bash theme={null}
    go test -v -timeout 300s ./cmd/worker/ -run TestWorkerProcess_RequestSlotAndSubmit
    ```
  </Step>
</Steps>

<Warning>
  worker 必须按上面这组变量启动。少了 `ICEBERG_NAMESPACE` 或 `BLOCKDB_BATCH_WRITE_ADDR`，进程会在配置校验阶段直接退出。
</Warning>

## Makefile 目标

| 目标                                                             | 作用                                                                                                                                                                                                                               |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `make` / `make all`                                            | 等同 `make test`                                                                                                                                                                                                                   |
| `make fmt`                                                     | 检查格式：`go fmt ./...` 有输出即失败并列出 `gofmt -s -l .`                                                                                                                                                                                    |
| `make vet`                                                     | `go vet ./...`                                                                                                                                                                                                                   |
| `make test`                                                    | 先 `fmt`、`vet`，再对除 `cmd/syncinvoker` 外的所有含测试包跑 `go test -coverprofile=coverage.out -short -timeout $(TEST_TIMEOUT)`（默认 `600s`），最后把 `cmd/syncinvoker/*_test.go` 逐文件跑（`SYNCINVOKER_TEST_TIMEOUT` 默认 `3m`），合并覆盖率并打印 total 与 top 10 包 |
| `make test-cover`                                              | `go test ./... -v -cover -short`                                                                                                                                                                                                 |
| `make test-bench`                                              | `go test ./... -bench=. -benchmem -short`                                                                                                                                                                                        |
| `make proto`                                                   | 依次跑 `proto-blockdb`、`proto-meta`、`proto-localtestservice`，再生成 `api/grpc/{worker,coordinator,bundlecoordinator,syncinvoker}` 的 pb                                                                                                 |
| `make proto-blockdb` / `proto-meta` / `proto-localtestservice` | 分别生成 `internal/sdk/{blockdb,meta,localtestservice}/gen/`                                                                                                                                                                         |
| `make deps`                                                    | `go mod download` + `go mod tidy`                                                                                                                                                                                                |
| `make clean`                                                   | 删除 `coverage.out`、`coverage.html`，并 `go clean -cache -testcache`                                                                                                                                                                 |
| `make build`                                                   | 执行 `go build ./bin/...`。构建二进制用 `go build ./cmd/...`，或按 `Dockerfile` 逐个 `go build -o`                                                                                                                                             |

## 环境变量与配置文件

worker 的加载顺序在 `internal/worker/app/app.go` 里：`DefaultWorkerFullConfig()` → profile 的 `TuneDefaults` → `WORKER_CONFIG` 指向的 JSON 文件（`LoadWorkerFullConfigFrom`）→ 环境变量覆盖。JSON 字段名与 `WorkerFullConfig` 的 `json` tag 一致，示例见 blockx 仓库 `docs/deploy.md` §9.3。coordinator 只读环境变量，没有配置文件（`internal/coordinator/app/config.go` 的 `LoadCoordinatorRuntimeConfigFromEnv`）。

本地开发最常碰到的变量：

| 变量                                                          | 默认值                        | 说明                                                                                                              |
| ----------------------------------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `WORKER_LISTEN` / `WORKER_ADDR`                             | `:8081` / 自动探测出口 IP        | 监听地址与注册到 etcd 的对外地址                                                                                             |
| `ETCD_ENDPOINTS`                                            | `localhost:2379`           | worker 与 coordinator 共用；worker 侧 `none` 表示不注册，coordinator 侧必须非空（`Validate` 报 `etcdEndpoints must not be empty`） |
| `EXECUTOR_COUNT`                                            | `8`                        | Python executor 子进程数                                                                                            |
| `PYTHON_BIN` / `PYTHONPATH`                                 | `python3` / —              | executor 解释器与模块搜索路径；本地指向 `python/.venv/bin/python` 与 `python/`                                                  |
| `TASK_SLOTS`                                                | `50`（`core.DefaultConfig`） | 单 worker 最大并发 task                                                                                              |
| `TASK_DEADLINE_MS`                                          | `300000`                   | 用户未指定时的 task 超时                                                                                                 |
| `EXECUTOR_SPAWN_MODE`                                       | `process`                  | `sandbox` 走 containerd 沙箱，本地开发保持 `process`                                                                      |
| `BLOCKX_USAGE_DISABLED`                                     | `false`                    | 本地必须设 `true`，否则启动时探测 Kafka                                                                                      |
| `STUB_BLOCKDB_DELAY_MS` / `META_ADDR` / `NODE_RPC_ENDPOINT` | —                          | 控制 BlockDB / Meta / NodeRPC 是走真实端点还是 stub                                                                       |
| `COORDINATOR_LISTEN`                                        | `:8080`                    | coordinator gRPC 监听地址                                                                                           |
| `COORDINATOR_REGISTRY_PREFIXES`                             | profile 默认                 | coordinator watch 的 etcd 前缀，逗号分隔                                                                                |
| `OTEL_EXPORTER_OTLP_ENDPOINT`                               | —                          | 不设则关闭 trace 导出                                                                                                  |

完整表（含 AIMD、gRPC keepalive、Iceberg cache 等几十项）见 blockx 仓库 `docs/deploy.md` §9；`deploy/env/worker.env.example` 是 EC2 部署用的 env 文件样例，包含大量宿主机脚本才读的键，不适合直接拿来本地跑。

## 代码生成

改了 `.proto` 后重新生成 Go 绑定：

```bash theme={null}
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
make proto          # 全部
make proto-blockdb  # 只生成 internal/sdk/blockdb/gen/
```

`Makefile` 会把 `$(go env GOBIN)`（或 `$(go env GOPATH)/bin`）加进 `PATH`，所以插件装在默认位置即可。proto 源文件位置：`api/grpc/*/` 与 `internal/sdk/{blockdb,meta,localtestservice}/proto/`。生成结果要一起提交。

## 常见问题

<AccordionGroup>
  <Accordion title="E2E 全部显示 SKIP，提示 Python dependencies not available">
    `internal/testutil/python.go` 的 `RequirePythonModules` 会用探测到的解释器试 `import`，失败就 `t.Skip`。`cmd/worker` 的 process E2E 要求 `greenlet, blockx_sdk, blockx_executor, blockdb, blockx, leafage, chaintable` 七个模块都能导入（`cmd/worker/worker_process_helpers_test.go`），后四个来自 `pyproject.toml` 里的私有 git 依赖。先 `uv sync --project python`，并确认 `python/.venv/bin/python` 存在。
  </Accordion>

  <Accordion title="go build ./cmd/worker 报 build constraints exclude all Go files in duckdb-go-bindings/lib">
    这是 `CGO_ENABLED=0` 的症状。worker 与 bundle\_worker 必须开 CGO，并且机器上要有 C 编译器。首次链接较慢属正常。
  </Accordion>

  <Accordion title="worker 启动直接退出：invalid worker config">
    看错误串：`icebergNamespace must not be empty` 补 `ICEBERG_NAMESPACE`；`plugin BlockBundleWriteResultHandler requires blockdb.batchWriteAddr` 补 `BLOCKDB_BATCH_WRITE_ADDR`；usage 相关错误补 `BLOCKX_USAGE_DISABLED=true`。
  </Accordion>

  <Accordion title="worker 起来了但 Coordinator 看不到它">
    检查 `ETCD_ENDPOINTS` 是否指向同一个 etcd，以及 `WORKER_ADDR` 是否是 Coordinator 可达的地址；容器网络隔离时必须显式设置（`docs/deploy.md` §7）。本地单机调试可以直接 `ETCD_ENDPOINTS=none` 绕过 Coordinator。
  </Accordion>

  <Accordion title="Python executor 子进程没起来">
    确认 `PYTHON_BIN` 指向的解释器能 `import blockx_executor`（`PYTHONPATH` 要包含 `python/`）。默认值 `python3` 通常是系统解释器，缺 greenlet 等依赖。
  </Accordion>

  <Accordion title="go mod download 拉不到 github.com/Chaintable/*">
    这些是私有模块。配置 git 凭据（例如 `git config --global url."git@github.com:".insteadOf "https://github.com/"`），并设 `GOPRIVATE=github.com/Chaintable/*`。Python 侧的 `blockx-py` 等也是私有 git 依赖，同样需要凭据。
  </Accordion>
</AccordionGroup>

## 相关文档

* [测试](/development/testing)：各测试层的命令、耗时与前置条件。
* [贡献流程](/development/contributing)：提交与 PR 约定。
* [仓库结构](/architecture/repository-layout)：`cmd/`、`internal/`、`api/`、`python/` 各放什么。
* [部署概览](/development/deployment)：镜像构建与 EC2 运行形态。
* blockx 仓库 `docs/deploy.md`：完整环境变量表、Docker 启动示例与常见问题。
* blockx 仓库 `AGENTS.md`：构建、测试与协作约定的权威清单。
