入口约定
Executor 按下面的优先级在源码的顶层def 里选入口(python/blockx_audit/tables.py 的 resolve_entry_name):
functionId的最后一段。例如注册函数token.trace_to_token_transfer的入口是def trace_to_token_transfer。- 名为
_的顶层def,或者一行_ = <函数名>别名。blockx-py 把 Python callable 打包成源码时会自动补这行别名。 - 源码顺序里第一个不以
_开头的顶层def。
_:
import。
参数与返回值
参数按位置传入,来自 call 的args:
行 dict 的键是源表的列名,值是 JSON 解码后的值。blockx-py 里用
SOURCE_ROW 常量代替手写 "${表名}"。
返回值必须能被 JSON 序列化:
函数从哪里来
注册函数的源码由 Worker 的 Function Code View 维护:订阅 BlockDB 的
system.function 表,或按周期从 Redis hash 全量刷新(FUNCTION_CODE_REDIS_URL),每次变更发布一份完整的新快照。本地开发时用内存函数库(devstub)。
版本固定
- task 激活时固定一个快照
epoch;这个 task 的所有 call 和子调用都按同一份快照解析函数,热更新只影响之后提交的 task。 - 派发给 Executor 的只有源码 digest。Executor 按 digest 缓存编译产物,未命中时按 digest 向 Worker 回拉源码。
- 同一 task 内,把同一个
functionId绑到不同源码,或 inline 函数与快照函数撞名,都会让这个 ID 确定性失败。
函数里能做什么
函数不能做的事:
- 直接写 BlockDB。所有写入走 task 声明的 Writer Plugin。
- 绕过 SDK 访问外部服务。审计开启时
import只能拿到白名单模块。 - 跨 call 共享状态。模块命名空间按 task 隔离,call 之间没有顺序。
执行预算
- 单个 call 的执行预算是
CallDeadlineMs = 5000,只计 CPU 与 IO 后端时间,不含排队;超出即该 call 失败。 - 纯 CPU 循环由 Executor 的
SIGALRM定时器抢占,IO 等待期间 greenlet 让出。 - call 预算与 task 超时相互独立;task 超时以
TIMED_OUT收敛整个 task。
静态审计
Worker 的FUNCTION_CODE_AUDIT_MODE 决定源码在派发前是否经过静态审计:
审计只做
ast.parse,不执行代码。它检查语法节点、import、属性访问是否在白名单内,并对返回值和能力边界做类型追踪;受限命名空间只提供 SAFE_BUILTINS 和白名单模块的 facade。Sync Invoker 的 Precheck 用同一个审计器,只审计不执行,适合在提交前给作者看违规项。完整白名单见 blockx 仓库 docs/specs/function-code-python-whitelist.md。
小结
- 入口优先按
functionId最后一段找,其次是_,最后是第一个公开def。 - 参数按位置传入,扫描类 Builder 用
"${表名}"(SOURCE_ROW)占位放入行 dict;返回dict/list[dict]/None。 - 函数只读;IO 全部经 SDK 拦截回 Worker;写入由 Writer Plugin 完成。
- 每个 task 固定一份代码快照;单 call 预算 5 秒;审计
enforce模式 fail-closed。
- Function Code View:快照、epoch、syncer 与审计链路的实现。
- Python Executor:用户函数编程模型、greenlet 挂起与 SDK 拦截点。
- Call Builder 与 Writer Plugin:
params模板与返回值如何落表。 - 提交 task 示例:完整可运行的函数与 task。