Chainlink 仓库 CRE Settings Override 实战:Local CRE e2e 测试中的运行时设置热替换与自动回滚

发布时间:2026/9/16 10:40:34
Chainlink 仓库 CRE Settings Override 实战:Local CRE e2e 测试中的运行时设置热替换与自动回滚 Chainlink 仓库 CRE Settings Override 实战Local CRE e2e 测试中的运行时设置热替换与自动回滚【免费下载链接】chainlinknode of the decentralized oracle network, bridging on and off-chain computation项目地址: https://gitcode.com/GitHub_Trending/ch/chainlink本文聚焦 Chainlink 仓库中 Local CRE e2e 测试体系提供的CRE Settings Override 运行时覆盖机制它允许测试在不拆除、不重启整个拓扑的前提下于运行时修改 CREChainlink Runtime Environment设置并在测试结束时自动还原。读完本文你将掌握t_helpers.ApplyCRESettings的完整 API、四种作用域Global/Org/Owner/Workflow的隔离语义与合并规则、设置生效时机即时 / 轮询延迟 / 注册时、串行执行的守护约束以及它与生产环境 CLD 设置滚动的同源关系与底层实现原理。这是什么一个面向 Local CRE e2e 测试的运行时设置覆盖辅助工具CRE Settings Override 是一个test helper用于在 Local CRE e2e 测试运行期间覆盖 CRE 设置——例如某个速率限制、尺寸上限、特性开关或并发上限——而不需要为此专门搭建一套新的拓扑去把该值烘焙进CL_CRE_SETTINGS环境变量。它的三个核心卖点运行时生效通过向节点投递cresettings类型的 job 实现节点侧在收到 job 后调用loop.AtomicSettings.Store热替换内存中的设置读取器无需重启自动回滚ApplyCRESettings会在开始时捕获每个 DON 的启动基线boot baseline并注册t.Cleanup在测试结束时自动还原无论测试通过还是失败作用域可控支持Global/Org/Owner/Workflow四种作用域可把变更的爆炸半径收敛到自己的 workflow 或 owner避免污染共享环境中的其他测试。核心实现位于 system-tests/tests/test-helpers/cresettings_override.go配套的示例测试见 system-tests/tests/smoke/cre/cresettings_override_test.go该测试同时在四种作用域上跑了一遍完整的应用 → 断言 → 还原流程充当可执行的文档。TL;DR一条调用搞定整个 happy pathfunc Test_CRE_MyThing(t *testing.T) { testEnv : t_helpers.SetupTestEnvironmentWithPerTestKeys(t, t_helpers.GetDefaultTestConfig(t)) // 用 option 限定作用域——这里限定到本测试的 workflow不会影响其他测试。 // 测试结束时自动还原。myWorkflowID 是 hex 字符串不带 0x 前缀。 t_helpers.ApplyCRESettings(t, testEnv, t_helpers.Workflow(myWorkflowID, [PerWorkflow.HTTPAction]\nCallLimit 1), ) // ... 触发 workflow并断言其在覆盖设置下的行为 ... }一次调用清理自动完成。优先把变更限定在自己的 workflow或 owner这样隔离性最好只有当确实需要环境级变更时才使用Global见下文作用域选择与隔离。何时使用、何时不使用适合使用当测试需要一个与拓扑默认值不同的设置值——速率限制、尺寸上限、特性开关、并发上限——但你又不想为这一个值单独搭建一套拓扑、把它固化到CL_CRE_SETTINGS中时。不要用当要修改的东西不是 CRE 设置时节点 TOML、capability 配置、链配置等这些都不在 settings schema 里这个辅助工具也不会去碰它们。与 CLD 生产设置滚动的同构关系这套机制是生产环境设置滚动流程chainlink-deployments中的cre-limit-change流程在测试内的对应物同一套机制更小的爆炸半径。生产滚动CLD本测试辅助工具test 内编辑settings/*.toml重新编译生成settings.toml用作用域 optionGlobal/Org/Owner/Workflow或FromFS直接给文件树传入同样的设置由同一个CombineCRESettingsFiles完成合并持久化 pipeline 发射job_propose_arbitrary→jobName: CRESettingstemplate: cre-settingsdonName: all-nodes调用同一个ProposeJobSpec{Template: CRESettings}changeset投递到每个 DON 的每个节点签名并执行提案在每个节点上自动 approve 提案节点通过loop.AtomicSettings.Store实时应用无需重启同样——无需重启变更持续生效直到下一次滚动自动还原到测试前的基线cleanup 时相同的 job 类型、相同的 all-nodes 投递、相同的实时应用路径——只是限定在一个测试内并在结束后回滚。如果你理解 CLD 的设置滚动就理解了这个工具。API 一览// 将一个或多个作用域 option 应用到每个 DON注册自动回滚返回句柄。 func ApplyCRESettings(t *testing.T, env *TestEnvironment, opts ...Option) *CRESettingsHandle // 作用域 options——每个都接收该作用域的设置 TOML不含作用域前缀 func Global(toml string) Option // [global] —— 影响所有 org/owner/workflow func Org(id, toml string) Option // [org.id] —— id: org id func Owner(id, toml string) Option // [owner.id] —— id: workflow-owner hex不带 0x func Workflow(id, toml string) Option // [workflow.id] —— id: workflow id hex不带 0x func FromFS(fsys fs.FS) Option // 一整棵 prod 风格文件树os.DirFS / embed.FS func (h *CRESettingsHandle) Reset(t *testing.T) // 立即还原测试结束时也会自动执行 func (h *CRESettingsHandle) AppliedTOML(donName string) string // 实际应用上去的文档 func (h *CRESettingsHandle) BaselineTOML(donName string) string // 将要还原到的文档几个关键约定与源码逐条对应每个 option 的字符串就是该作用域自己的设置不带作用域前缀作用域和 id 由 option 决定与生产环境的settings/*.toml完全一致。相关设置放在同一个 table 下例如[PerWorkflow.HTTPAction]然后是CallLimit 9。设置值必须是带引号的字符串CallLimit 9、Enabled true。这一约束在 API 层面由Option func(fstest.MapFS) error直接以string类型保证非字符串值不可能出现在投递前还会经过 deployment/cre/jobs/settings.go 中VerifyCRESettings的ensureStrings递归校验任何非字符串值都会报X is not a string错误。所有作用域必须在同一次调用中传入。每次ApplyCRESettings都会从启动基线重新构建并替换之前的覆盖而不是叠加因此在上一次覆盖仍生效时再次调用会被拒绝并报错否则会静默丢掉第一次的覆盖。要在同一测试内切换设置先调用Reset再重新ApplyCRESettings。该守卫在源码中由包级变量creSettingsActiveMucreSettingsActiveOwner实现见claimCRESettingsOverride。底层上options 构建出一棵 prod 风格的文件树由 deployment 侧的CombineCRESettingsFilesdeployment/cre/jobs/orgsettings.go合并——与生产环境编译settings.toml用的是同一段代码。从源码看CombineCRESettingsFiles在调用通用的settings.CombineTOMLFiles之前会先把org/目录下的每个文件复制一份带org_前缀的副本用于桥接不同版本对 org id 归一化方式不同的过渡期。设置的完整清单与格式规范位于chainlink-common/pkg/settings/cresettings/defaults.toml。作用域选择与隔离最具体优先设置的解析遵循最具体优先most-specific-first规则。对某个给定 workflow节点按以下顺序查找workflow.id → owner.id → org.id → global → 编译默认值优先使用最窄的作用域。由于环境在测试套件之间是共享的你选择的作用域同时也是你的爆炸半径Workflow / Owner—— 覆盖只对你的workflow或 owner解析生效。即使出了问题也不会影响其他测试的 workflow。这是默认应优先选择的方式。Org—— 影响该 org 下的所有 workflow。Global—— 影响环境中的每一个 workflow。这是刻意的环境级逃生舱只在确实需要时才用。id 从哪来来自你的部署步骤workflow 运行所在的 org id以及注册/部署 workflow 时拿到的 workflow-owner / workflow-id hex不带0x前缀。Global不需要 id。示例五种典型用法每个 option 的字符串都是普通的设置 TOML——不含作用域前缀——与settings/*.toml文件完全一致前置缩进没问题TOML 会忽略。如果想用磁盘上的 fixture 而不是内联字符串用t_helpers.FromFS(os.DirFS(testdata/...))。Workflow —— 只影响一个 workflow推荐t_helpers.ApplyCRESettings(t, testEnv, t_helpers.Workflow(workflowID, [PerWorkflow.HTTPAction] CallLimit 9 [PerWorkflow.HTTPTrigger] RateLimit every5s:2)) // workflowID 不带 0x 前缀Owner —— 只影响一个 workflow ownert_helpers.ApplyCRESettings(t, testEnv, // ownerHex 不带 0x 前缀 t_helpers.Owner(ownerHex, [PerOwner]\nWorkflowExecutionConcurrencyLimit 5), )Org —— 只影响一个 orgt_helpers.ApplyCRESettings(t, testEnv, t_helpers.Org(orgID, [PerOrg] BaseTriggerRetransmitEnabled false WorkflowExecutionConcurrencyLimit 42))Global —— 整个环境逃生舱t_helpers.ApplyCRESettings(t, testEnv, t_helpers.Global( [PerWorkflow.HTTPAction] CallLimit 7 [PerWorkflow] ExecutionConcurrencyLimit 3))多作用域同时传入 —— 在同一次调用中组合 optionst_helpers.ApplyCRESettings(t, testEnv, t_helpers.Global([PerWorkflow.HTTPAction]\nCallLimit 11), t_helpers.Org(orgID, [PerOrg]\nBaseTriggerRetransmitEnabled false), t_helpers.Workflow(workflowID, [PerWorkflow]\nExecutionConcurrencyLimit 1), )所有作用域会合并进同一个文档分层叠在每个 DON 的基线之上并一起投递。想看最终产物用t.Log(h.AppliedTOML(workflow))打印。生效时机实时 vs 重启最重要的一点每个设置都通过同一个实时AtomicSettings读取但消费者何时读取它决定了生效时机——这是最容易踩坑的地方类别测试中途覆盖的生效效果示例即时生效每次操作读取下一次调用即可看到门控RemoteExecutableWorkflowDONBindingEnabled、ExecutionTimestampsEnabled、ChainAllowed、上限/限制HTTPAction.CallLimit、ExecutionConcurrencyLimit、ChainRead.CallLimit、超时约 5 秒延迟轮询器调整几秒内生效速率限制HTTPTrigger.RateLimit、gateway 速率、队列容量上限注册时生效只影响覆盖之后注册的 workflowtrigger 订阅/注册限制、WASM 尺寸检查、workflow 准入限制经验法则逐次执行的限制/门控 → 先ApplyCRESettings再触发 workflow。注册时生效的设置 →在部署/注册 workflow之前调用ApplyCRESettings。事后应用对已注册的 workflow 是静默无效的看起来就像什么都没发生。投递范围每个 DON 都会被应用不由你选择覆盖会被投递到每个 DON 的每一个节点镜像 CLD 的 all-nodes 滚动策略。这是刻意的一个设置可能由 workflow 节点、capabilities 节点或 gateway 节点执行部分投递会导致静默不生效。你只需声明改什么helper 负责确保它能落到任何可能读取它的地方。内部它仍会合并每个 DON 各自的启动基线因为不同 DON 可以用不同的CL_CRE_SETTINGS启动——这部分由工具代劳。从 cresettings_override.go 源码看投递目标过滤为拥有 workerplugin节点的 DON因为 CRE 设置由 worker 节点执行、且投递 changeset 只面向typeplugin节点所以纯 bootstrap DON如bootstrap-gateway会被跳过并打日志skipping DON ... (no worker nodes)。必须串行执行覆盖守卫override guard因为覆盖会修改共享环境上的设置同一时刻只允许一个覆盖处于激活状态设置覆盖类测试必须串行运行不要在这些测试里调用t.Parallel()也不要把它们加入CRE_TEST_PARALLEL_ENABLED集合。CRE 套件默认就对使用环境的场景串行执行所以这是常态而非特例。helper 会强制执行。如果另一个覆盖仍处于激活状态时又启动了一个覆盖会快速失败并给出可操作的提示a CRE settings override from Test_CRE_Other is still active on the shared environment. Settings-override tests mutate shared state and must run serially: remove t.Parallel() from this test (and do not add it to the CRE_TEST_PARALLEL_ENABLED set). ...如果看到这条消息让失败的测试串行化——去掉它的t.Parallel()不要加入 parallel 集合。要在同一个测试内再次改设置先调用h.Reset(t)再ApplyCRESettings覆盖仍激活时二次 apply 会被拒绝提示信息会告诉你先Reset因为它会静默替换第一次的覆盖。守卫协调的是覆盖 vs 覆盖。而防止无关的并发测试看到你的变更靠的是串行执行默认如此加上窄作用域——见作用域选择与隔离。清理机制自动回滚到底层基线的原理ApplyCRESettings会在一开始捕获每个 DON 的启动基线即它的CL_CRE_SETTINGS并注册t.Cleanup在结束时重新应用它。你什么都不用做——测试结束即还原无论通过还是失败。删除 settings job 并不会还原设置节点会保留最后一次存储的值所以清理通过重新应用基线而不是删除 job 来完成。helper 替你做了这件事。源码注释cresettings_override.go也明确记录了这一点Deleting the settings job does NOT revert the settings, so cleanup must re-apply the captured baseline explicitly.覆盖不是叠加式的每次投递都会完全替换 getter因此 helper 发送的始终是baseline ⊕ 你的覆盖。这带来两个推论省略某个 key 完全没问题——它会回落到编译默认值DON 的启动设置始终被保留因为它们是基线的一部分。从源码看覆盖文档与基线的合并逻辑在renderSettingsdeepMergeIntocresettings_override.go先读取 DON 的CL_CRE_SETTINGSJSON作为基底再把覆盖文档递归深合并进去重叠的子表是合并而非替换最后序列化为 TOML 并计算 sha256 哈希——节点应用后也会在日志里打印同一个哈希用于收敛核对。提前还原Handle.Reset(t)调用Reset可在测试体内提前还原——例如同时断言变更前和变更后的行为或在一个测试里做两种设置的 A/Bh : t_helpers.ApplyCRESettings(t, testEnv, t_helpers.Global([PerWorkflow.HTTPAction]\nCallLimit 1), ) // ... 断言收紧的限制确实被强制执行 ... h.Reset(t) // 现在回到基线 // ... 断言正常行为已恢复 ...Reset是幂等的并会把自动清理变成 no-op所以调用它是绝对安全的不调用也安全——清理照常运行。源码中restore以h.reverted标志保证只执行一次并通过defer releaseCRESettingsOverride(h.owner)把单覆盖槽位归还给守卫。高级用法检查精确文档h.AppliedTOML(donName)/h.BaselineTOML(donName)返回实际应用的 / 将要还原的 TOMLdonName例如workflow。很适合t.Logf和调试。同一测试内做 A/B应用 → 断言 →Reset→ 应用不同值 → 断言。因为每次 apply 都叠加在基线上而不是上一次覆盖上值永远不会累积。自由组合作用域一次调用中组合多个作用域它们合并成一个文档。常见错误与失败模式helper 对一切可检测的问题都是大声失败fail-loud并且在投递任何东西之前就会对照 schema 校验你的覆盖。错误会发生什么未知 / 拼错的设置 key在ApplyCRESettings处使测试失败unknown fields …。不会投递任何内容。值的格式不符合该设置int 限制传banana、畸形 rate 等在ApplyCRESettings处使测试失败invalid toml settings …——每个值都会按其设置类型解析。不会投递任何内容。非字符串值不可能——API 只接受string类型的值。带0x前缀的 org / owner / workflow id在ApplyCRESettings处使测试失败——id 必须不带0x前缀。环境未运行 / 节点不可达 / 提案被拒在ApplyCRESettings处使测试失败——propose/approve 错误会被暴露出来。清理时还原失败被暴露而非吞掉——自动清理会调t.Error显式Reset则直接硬失败。尽力而为的收敛日志读不到容器日志忽略——那只是可见性辅助权威的成功信号是每个节点都 approve 了 job。在 workflow 注册之后翻转注册时生效的设置对该 workflow静默无效见生效时机表。要在部署 workflow之前应用。覆盖了一个没有任何匹配的作用域例如某个没有运行中 workflow 使用的 org id正常投递只是永远不会被查询到。不算错误。两个覆盖测试在共享环境上并发运行在ApplyCRESettings处快速失败——第一个覆盖仍激活时第二个会被拒绝并给出可操作提示。覆盖测试要保持串行不要调用t.Parallel()。同一测试内重新应用前先Reset——激活期间二次 apply 会被拒绝。一个无关的并发测试落入Global覆盖的爆炸半径不受守卫保护——守卫只协调覆盖 vs 覆盖。通过串行执行CRE 默认和窄作用域Workflow/Owner来预防。简短版编写层面的错误坏 key / 坏值 / 坏 id和重叠的覆盖测试都会大声失败。仅存的静默风险是时序性的——翻转注册时生效的设置太晚或Global覆盖碰到无关的并发测试——而这两者都可通过窄作用域 串行执行默认来规避。运行要求与执行命令这些是 e2e 测试需要一个正在运行的 Local CRE 环境搭建流程见 docs/local-cre/system-tests/running-tests.md本地通常为cd core/scripts/cre/environment go run . env setup go run . env start。环境就绪后go test ./system-tests/tests/smoke/cre -run ^Test_CRE_CRESettings_ -timeout 20m -v配套的示例测试 system-tests/tests/smoke/cre/cresettings_override_test.go 是一个单测试、单环境启动其内部各段落串行执行依次跑 workflow 作用域、org 作用域、global 作用域、以及多作用域合并前三个显式Reset最后一个留给t.Cleanup自动还原从而把两条还原路径都覆盖到。测试里的exampleOrgID与exampleWorkflowID是示意值workflow id 是 62 字符的 hex 串、不带0x真实测试中应使用部署时拿到的实际 id投递与清理的机制与 id 取值无关只有隔离保证依赖真实 id。底层原理导读可选追踪路线使用这个 helper 不需要了解这些但想深入追踪时可以按图索骥节点侧实时应用core/services/cresettings/delegate.go →loop.AtomicSettings.Storechainlink-common/pkg/loop/settings.go。从源码看该 delegate 只允许一个cresettingsjob 同时运行activeJobID原子指针守卫收到 job 后按config_type分发默认settings类型调用d.atomicSettings.Store(...)并打印Updated settings日志含 hash——这正是 helper 的收敛日志扫描器creSettingsUpdateLogMarker Updated settings所匹配的标记。投递 changesetdeployment/cre/jobsProposeJobSpec{Template: CRESettings}校验逻辑在 deployment/cre/jobs/settings.go 的VerifyCRESettings——它用DisallowUnknownFields严格解码到cresettings.Schema未知字段会报unknown fields - if these are new fields, then chainlink-common must be updatedid 以0x或org_/owner_为前缀、非小写等都会在此被拦截投递前会先跑VerifyPreconditions再执行Apply。设置 schema、作用域与解析chainlink-common/pkg/settings/cresettings和chainlink-common/pkg/settings。一句话总结这条链路测试内调用ApplyCRESettings→ 作用域 options 组装成 prod 风格设置文件树 →CombineCRESettingsFiles合并 →ProposeJobSpec{Template: CRESettings}投递给每个有 worker 节点的 DON → 节点 delegate 调AtomicSettings.Store热替换 → 测试结束t.Cleanup重新应用基线。这正是生产环境 cre-limit-change 滚动的同一条链路只是被限定在测试的生存期内。【免费下载链接】chainlinknode of the decentralized oracle network, bridging on and off-chain computation项目地址: https://gitcode.com/GitHub_Trending/ch/chainlink创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考