开发指南:模块结构、构建测试与 traces 拓扑感知迁移实战)
Opik 后端模块opik-backend开发指南模块结构、构建测试与 traces 拓扑感知迁移实战【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llmapps/opik-backend是 Opik 全栈仓库中的 Java 后端服务模块负责 LLM 可观测性的核心数据面trace/span 摄取、数据集与实验、评估与告警等业务能力。本文以该模块的开发者指南apps/opik-backend/AGENTS.md为主体系统讲解其目录组织、构建与测试命令、分层编码规范、测试约定与提交流程并深度展开其中最具工程分量的部分——tracesClickHouse 表在切流窗口内的拓扑感知 DDL 迁移模式辅以源码与测试佐证帮助你快速上手并为该模块提交高质量变更。模块定位与文档继承关系apps/opik-backend/AGENTS.md是一份模块专属的工程指南它遵循 monorepo 的文档分层约定共享的工作流、PR 与安全策略统一收口在仓库根目录的 AGENTS.md模块级指南只保留与本模块强相关的增量信息。因此任何贡献者都应先读根级指南再结合本文件落地到 Java 后端的上下文。仓库内与之协同的模块包括apps/opik-frontendReact/TypeScript 前端apps/opik-documentation文档站点与生成的 API 文档sdks/*Python、TypeScript、opik_optimizer 等 SDKtests_end_to_end、tests_load跨栈 E2E 与性能测试套件deployment、scripts部署编排与开发工具脚本项目结构与模块组织后端代码按“源码—测试—资源—迁移”四大区域组织源码apps/opik-backend/src/main/java测试apps/opik-backend/src/test/java资源apps/opik-backend/src/main/resources数据迁移与运行时脚本apps/opik-backend/data-migrations、apps/opik-backend/scripts以及根目录的scripts/从源码结构看业务代码根包为com.comet.opik内部按职责划分api资源层的数据模型与请求/响应对象Dataset、Trace、Alert、AnnotationQueue、PromptVersion等domain领域服务与 DAO如TraceDAOinfrastructure基础设施配置、Guice 模块、DB 接入等utils通用工具资源目录apps/opik-backend/src/main/resources下包含liquibase/db-app-analytics与db-app-state两套 changelog、llm-models-default.yaml、model_prices_and_context_window.json生成产物勿直接编辑等关键配置。一个特别值得注意的约束出现在文档的显眼位置任何修改tracesClickHouse 表的迁移都必须先阅读 docs/traces-schema-ddl.md 并遵循拓扑感知 DDL 模式。原因是traces正处于向分区化继任表的迁移过程中一条迁移必须同时正确作用于切流前后两种物理布局而两种布局下犯错的方式在迁移执行时都不会报错。这一点在“traces 拓扑感知迁移”章节详述。构建、测试与开发命令模块指南给出了完整的环境验证、本地开发与构建命令链可在仓库根目录执行命令用途./opik.sh --build以 Docker 启动完整技术栈全量验证环境./opik.sh --verify/./opik.sh --stop健康检查 / 关闭本地服务scripts/dev-runner.sh --be-only-restart本地 Java 后端进程模式含热更新工作流停止→构建→启动基础设施与后端进程scripts/dev-runner.sh --be-only-start不重新构建快速启动后端进程scripts/dev-runner.sh --build-be仅重建后端依赖与产物scripts/dev-runner.sh --lint-be运行后端 lint/格式检查scripts/dev-runner.sh --migrate执行数据库迁移MySQL ClickHousecd apps/opik-backend mvn test单元/集成测试含 Testcontainers 支撑的套件cd apps/opik-backend mvn spotless:apply应用 Java 格式化其中dev-runner.sh的各开关在 scripts/dev-runner.sh 中均有对应分支实现例如--migrate会在本地执行 MySQL 与 ClickHouse 两套 Liquibase 迁移--be-only-restart承担日常“改代码→重启→看效果”的循环。根级 AGENTS.md 还补充了全栈默认启动./opik.sh、前端构建npm run build、Python SDK 与 E2E 测试等命令跨栈改动时应按需使用。编码风格与分层架构后端遵循清晰的分层设计resource → service → DAO并配套以下规范依赖注入使用构造器注入Inject复用现有 Guice 模块保持层边界不被破坏。业务对象如TraceDAO通过Inject构造器接收配置与连接池等依赖。格式化Java 风格遵循 Spotless 默认规则配置见 spotless.xml只格式化改动过的文件避免全量重排以保持 diff 最小、评审成本最低。命名方法与变量使用 camelCase类使用 PascalCase。不可变优先偏好不可变集合List.of、Set.of、Map.of结构化日志中的值用引号包裹便于检索与排障。这些约定直接服务于可评审性构造器 DI 让依赖关系显式化不可变集合减少共享状态带来的并发隐患一致的命名降低跨文件理解成本。测试指南后端测试框架为JUnit Mockito Testcontainers运行于 Maven 测试生命周期mvn test命名规范测试类置于src/test/java文件名以*Test.java结尾集成边界类使用描述性名称如TracesSchemaParityPreCutoverTest。行为变更必须配套测试改动行为前先补充/调整测试再提 PR。跨栈改动验证本地启动后端并保证 MySQL、ClickHouse、Redis 等必需服务可用根级指南同样提示本地自托管测试需预先配置这些依赖。基础设施类测试大量依赖 Testcontainers 拉起真实数据库实例例如切流相关测试会在容器内执行真实 Liquibase changelog 并断言 schema 一致性。traces 拓扑感知迁移模块中最关键的工程约束这是模块指南中唯一指向专项文档的技术点也是后端维护中风险最高的区域值得重点展开。切流窗口内的两种物理拓扑traces的物理层正在迁移到一个分区化、面向分片的继任表。在全部安装完成切流之前同一条迁移文件必须对两种不同的物理布局都正确而犯错的方式是静默的——迁移时不报任何错误随后表现为读坏数据或丢数据。两种布局如下表切流前新安装、多数自托管切流后SaaS及完成迁移的自托管traces存活的ReplicatedReplacingMergeTreeDistributed包装表——不存数据traces_local不存在真正存数据的分片MergeTreetraces_local_v2切流将提升的空继任表“影子表”被切流重命名移除traces_pre_cutover_backup不存在冻结保留的切流前数据用于观测期回滚切流由运维 runbook 执行见>--changeset opik:000123_add_foo_to_traces_pre_cutover --comment: Pre-cutover branch — traces is the live MergeTree and traces_local_v2 is the shadow; apply to both --preconditions onFail:MARK_RAN onError:HALT --precondition-sql-check expectedResult:0 SELECT count() FROM system.tables WHERE database ${ANALYTICS_DB_DATABASE_NAME} AND name traces_local ALTER TABLE ${ANALYTICS_DB_DATABASE_NAME}.traces ON CLUSTER {cluster} ADD COLUMN IF NOT EXISTS foo String DEFAULT ; ALTER TABLE ${ANALYTICS_DB_DATABASE_NAME}.traces_local_v2 ON CLUSTER {cluster} ADD COLUMN IF NOT EXISTS foo String DEFAULT ; --changeset opik:000123_add_foo_to_traces_post_cutover --comment: Post-cutover branch — traces is the Distributed wrapper over traces_local --preconditions onFail:MARK_RAN onError:HALT --precondition-sql-check expectedResult:1 SELECT count() FROM system.tables WHERE database ${ANALYTICS_DB_DATABASE_NAME} AND name traces_local ALTER TABLE ${ANALYTICS_DB_DATABASE_NAME}.traces_local ON CLUSTER {cluster} ADD COLUMN IF NOT EXISTS foo String DEFAULT ; ALTER TABLE ${ANALYTICS_DB_DATABASE_NAME}.traces ON CLUSTER {cluster} ADD COLUMN IF NOT EXISTS foo String DEFAULT ;四个承重的细节用sqlCheck查system.tables而非tableExists守卫必须读取运行时拓扑Liquibase 自身的记账无法告诉你运维是否执行了切流。onFail:MARK_RAN被跳过的分支记录为“已应用”而不执行后续启动不会在错误拓扑上重试。liquibase-clickhouse0.7.2 支持该语义门禁断言它升级若破坏会先在 CI 失败而非生产环境。onError:HALT前置条件本身无法求值就停止不要猜测拓扑。每条语句都带ON CLUSTER {cluster}缺失时 DDL 只到达 Liquibase 连接的节点其余副本被记作已应用却缺少变更。且守卫基于本地system.tables读取非集群 DDL 会让节点之间对拓扑的判定分叉。处处IF [NOT] EXISTS保证重跑、部分应用、或从任一侧到达的安装都幂等。两种典型场景与结构性变更禁区场景 1——新增字段读面向变更。两个分支、每个分支改两张表若为保留列还需加入回填列清单。场景 2——新增索引仅存储层。切流前改两张表切流后只改分片——不要对无数据的包装表建索引。罕见的结构性变更ORDER BY、PRIMARY KEY、PARTITION BY在MergeTree上不可变根本无法ALTER改动只能重建表并拷贝数据。文档明确建议不要在混合集群窗口内尝试结构性变更——它应跟随继任表定义落地如000114中按周分区键的做法而非窗口内的ALTER。若确有必要那是一场设计讨论而不是迁移。不变式对结构性变更依然成立门禁仍会对比排序键与主键。已知局限与冻结规则守卫有一个已知局限Liquibase 在它持有的单条 JDBC 连接上、针对该服务器自己的system.tables求值sqlCheck然后才提交ALTER ... ON CLUSTER即分支是从单一主机的拓扑视角选出的。若副本短暂偏斜切流中途或副本追赶中某主机可能选定一个分支并把互补 changeset 记成MARK_RAN令其他主机在账本声称已应用的情况下永久缺失该变更。实践中有三件事约束风险切流EXCHANGE本身是集群级的、exchange_and_wrap.sh在推进前等待复制稳定、冻结规则把 schema DDL 挡在偏斜最可能发生的窗口外但没有一件能消除它。候选加固方案是用clusterAllReplicas而非本地system.tables求值前置条件并在部分回答时失败——尚未定案。在此之前不要对未确认稳定的集群发布 trace schema DDL。冻结规则安装处于EXCHANGE与观测期结束之间traces_pre_cutover_backup仍保留、回滚仍可能的窗口时不要发布任何 trace schema DDL。回滚会把冻结的切流前表重新提为traces观测期内只作用于继任表的 DDL 会被该回滚丢失而其 changeset 仍记作已应用——账本声称存在一个实际不存在的列后续迁移也不会补上。请在切流开始前或观测期结束后落地 trace schema 变更。CI 如何把关五个门禁与常见失败门禁断言内容TracesSchemaParityPreCutoverTest以新安装的方式应用真实 changelog断言三方一致traces≅traces_local_v2影子表 ≅ 回填列清单TracesSchemaParityPostCutoverTest在000114处停止 changelog拼接 runbook 的EXCHANGE 包装恢复后让你的迁移运行在切流后拓扑上再断言包装表恰好暴露分片的所有列TracesMigrationPreconditionLintTest免容器的快速检查严格在000114之后新增的traces变更迁移必须在变更 changeset 自身携带守卫并且两个互补分支都要有TraceMutationRoutingArchTest/TraceMutationSqlRoutingTest运行时 DAO 变更必须通过TraceDAOImpl#tracesMutationTable()解析目标表任何 SQL 都不得直接命名traces/traces_local每个门禁还携带注入偏差的负向测试保证没有任何断言会悄悄停止生效。常见失败信息对照“read-facing column parity”——只改了一张表补上缺失的ALTER。“cutover backfill parity”——新增保留列却没加入回填列清单。“wrapper column parity”/列不可读——切流后分支只改了分片没改包装表。“skip-index parity”——索引只加在traces没加影子表。lint 失败——新迁移改动traces却完全没带前置条件守卫从上面的模式模板起步即可。CI 不检查什么以上全部只比较 schema名字、类型及建立其上的选择/表达式定义不移动任何数据行因此无法证明转换无损。把列加进BASELINE_TYPE_DIFFERENCES可使其豁免类型一致性但此后没有任何东西再验证切流转换是否保值——值保真由TracesLocalV2CutoverTest及 QA 全量预演负责。因此白名单条目是一项决策而非形式它断言你已核查转换安全或丢失是有意的请在条目 reason 里写明是哪一种。运行时的迁移路由佐证从源码看DAO 层对两种拓扑的适配同样遵循“单一决策点”原则。TraceDAO.java 中tracesDistributedWrapEnabled()通过DatabaseAnalyticsDataModelConfig读取包装是否启用启用时traces是拒绝变更code 36/48的Distributed表因此所有 trace变更DELETE/ALTER/OPTIMIZE必须指向traces_local分片未启用时traces仍是MergeTree删除可直接进行。读与插入始终经traces路由两种拓扑下都正确。tracesMutationTable()是唯一决定变更目标表名的地方tracesDistributedWrapEnabled() ? TRACES_LOCAL_TABLE : TRACES_TABLE。历史上该路由是每个变更模板里重复的两分支if(distributed_wrap)traces_localelsetracesendif条件导致新变更的正确性取决于是否记得复制分支而错误写法在视觉上与正确写法无异。收敛为单一名字后变更模板变为拓扑无关的DELETE FROM traces_mutation_table只剩一行需要审计TraceMutationRoutingArchTest强制两条半边——任何其他代码单元不得读取包装开关任何变更 SQL 不得拼出任一表名。Liquibase 迁移则按种类拆分DELETE/MATERIALIZE COLUMN/ADD INDEX/MODIFY TTL只面向traces_localDistributed包装表拒绝它们而ADD/DROP/MODIFY COLUMN必须同时面向traces_local与traces——包装表以元数据方式接受它们跳过会让读侧看不到该列code 47。Append-only 原则与未决事项已发布的迁移永不编辑——既不为修 bug也不为给切流前的老迁移补前置条件。所有修改都是新增一条追加迁移。无守卫地改动traces的迁移000091、000113等早于切流对运行过它们的安装是正确的lint 从000114起才生效正是出于这个原因迁移目录当前已有 129 个 changelog 文件000114即重建traces_local_v2时间戳精度的那次。一个被推迟的开放决策新安装与开源安装最终是否收敛到切流后拓扑目前新安装从切流前起步并停留直到运维运行 runbook因此每条受守卫迁移的切流前分支无限期承重混合集群永不闭合。替代方案是让新安装直接创建终态traces_local 包装表代价是绿地方案与迁移路径不同。尚未定案——在此之前请假设两种拓扑都是永久的为每条 trace 迁移都写好两个分支。Agent 贡献工作流与提交规范作为 monorepo 的一部分后端模块遵循根级指南中共享的 Agent 工作流提交前先阅读 CONTRIBUTING.md运行本模块的格式与测试命令再请求评审推荐 draft PR 优先、多组件并行时使用 worktree。提交与 PR 层面后端专属约定是适用时在 commit/PR 标题中使用[OPIK-####] [BE]前缀根级规范为[OPIK-1234] [COMPONENT] feat|fix|refactor|docs: short summary语义风格组件前缀含[FE]、[SDK]、[DOCS]、[NA]、[INFRA]等PR 描述应包含变更摘要、已运行的测试覆盖与关联 issueResolves #...并遵循 PR 模板的 Details、checklist、Issues、Testing、Documentation 各节。安全与配置提示除根级安全策略密钥与 API key 不得入库使用本地.env或 shell 变量SDK 示例对本地部署使用opik configure --use_local外后端专属检查是主要后端变更后运行迁移并验证/healthcheck。跨栈本地验证还需保证 MySQL、ClickHouse、Redis 可用与测试指南中的要求一致。小结apps/opik-backend的工程实践可归纳为三条主线清晰的 resource → service → DAO 分层与构造器 DI、以 JUnit/Testcontainers 为底座的测试纪律、以及对tracesschema 变更的严格拓扑感知迁移纪律。其中最后一条是当前仓库中最值得注意的运维约束——它把“静默失败”的隐患转化为由sqlCheck守卫、ON CLUSTER落地、MARK_RAN记账、五个 CI 门禁背书的可执行模式并在 DAO 层以单一路由决策点配合架构测试双向收口。新贡献者按此文档与 docs/traces-schema-ddl.md 操作即可在混合拓扑集群中长期安全地演进 trace 数据面。【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考