
Mastra ClickHouse v-next 可观测性发现机制设计discovery_values / discovery_pairs 辅助表与刷新式物化视图实战解析【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra导读本文以 Mastra 仓库中 ClickHousev-next可观测性设计文档为主体系统讲解“发现Discovery”子系统的完整设计两张辅助表discovery_values、discovery_pairs的物理形态、维度语义、源表映射、刷新节奏与刷新 SQL 形态以及 8 个发现类端点的查询映射。通过本文读者可以掌握如何在只追加append-only信号表之上构建一个最终一致、best-effort 的发现能力并理解为何在 v0 阶段用“刷新式物化视图 辅助表”替代“扫描基表”是更优解。设计定位v0 的发现模型在 ClickHousev-next可观测性设计中发现Discovery是一个独立的辅助子系统其核心原则是不要用扫描基表的方式服务 UI 下拉框、自动补全等发现类查询而是把发现读请求全部收敛到两张专用辅助表上。v0 模型要点见 discovery.md发现只从两张辅助表读取discovery_values与discovery_pairs两张辅助表是普通 ClickHouse 表由**可刷新的物化视图refreshable materialized views**维护v0 阶段不通过写入时insert-time物化视图增量喂数据发现是**刻意设计为最终一致eventually consistent**的v0 阶段不对 JSON payload如metadata、scope、costMetadata、logdata、spanmetadataRaw做发现v0 阶段不为了对称性把 scores / feedback 强行并入跨信号发现。选择“可刷新辅助表”的关键理由是刷新会基于当前数据整体重算集合因此源表中的轻量删除lightweight delete与 TTL 过期都能在下一次刷新后被自然反映出来而写入时增量物化视图则难以低成本处理删除与过期。Best-effort 原则不阻塞核心可观测性发现被明确定义为best-effort 辅助功能它不是核心可观测性的启动依赖发现表不需要在适配器adapter启动时就存在发现不 gate 核心可观测性的读写辅助表与刷新式物化视图可以在启动之后再创建一旦启用发现bootstrap 与定时刷新应自动运行在发现完成初始化并成功刷新之前发现类方法应返回空结果而不是抛出未初始化错误禁止在辅助表不可用时静默回退到基表扫描。这套约定在 shared.md 中也有交叉印证如果刷新式物化视图能力不可用v-next应把发现标记为 unavailable而不是让整个可观测性适配器失败核心信号spans、metrics、logs、scores、feedback的读写不受发现故障影响。前置假设依赖的 ClickHouse 功能集v0 发现设计假设目标 Cloud ClickHouse 环境具备以下能力支持可刷新物化视图refreshable materialized views这是 v0 发现设计的首选依赖支持设计中其他部分已经在使用的ARRAY JOIN、mapKeys()、Map键直接查找以及LowCardinality(...)、Map(...)、Array(...)类型若目标环境不支持可刷新物化视图v0 应把发现标记为 unavailable而不是强行切换到另一条实现路径v-next应把“可刷新物化视图支持”视为发现能力discovery capability而不是整个可观测性适配器的必需运行时能力。也就是说发现能力与核心存储能力在能力边界上是解耦的——核心适配器可以在任何支持基本 MergeTree 语义的环境运行只有发现功能才强依赖刷新式物化视图。辅助表设计物理形态与维度语义物理方向两张辅助表在 v0 的物理方向见 discovery.md 与 physical-types.md属性discovery_valuesdiscovery_pairs用途去重后的单值维度entityType、serviceName、environment、tag、metricName、metric labelKey去重后的键值对式查找entityType → entityName、metricName labelKey → labelValue引擎MergeTreev0 设计方向MergeTreev0 设计方向分区v0 不使用分区v0 不使用分区排序键ORDER BY (kind, key1, value)ORDER BY (kind, key1, key2, value)列kind/key1/valuekind/key1/key2/value类型LowCardinality(String)/String/StringLowCardinality(String)/String/String/String关键约束key1在discovery_values中必须始终是非空Stringkey2在discovery_pairs中必须始终是非空String当某个发现族discovery family没有父键或二级键维度时v0 使用空字符串哨兵值不要依赖可空排序键列或allow_nullable_key来支撑辅助表辅助表是完全派生结构一致性机制是“刷新”本身因此 v0 中辅助表不需要自己的 TTL。discovery_values维度语义kindkey1value说明entityTypeentityType全局去重后的实体类型集合serviceNameserviceName全局服务名集合environmentenvironment全局环境集合tagentityTypetagtag 按所属实体类型组织metricNamemetric name指标名集合metricLabelKeymetric namelabel key指标标签键按指标名组织discovery_pairs维度语义kindkey1key2valueentityTypeNameentityTypeentityNamemetricLabelValuemetric namelabel keylabel value源表映射只读三个信号表v0 只允许从以下三个源表读取发现数据禁止从trace_roots、score_events、feedback_events读取span_events追踪事件见 span-events.mdmetric_events指标事件见 metric-events.mdlog_events日志事件见 log-events.mdkind 与源的映射关系kind数据来源entityTypespan_events、metric_events、log_events中非空entityType的并集serviceName三个源表非空serviceName的并集environment三个源表非空environment的并集tag三个源表tags数组展开行级entityType带入key1metricNamemetric_events中去重的指标namemetricLabelKeymetric_events.labels的键展开指标name带入key1entityTypeName三个源表去重的(entityType, entityName)对metricLabelValuemetric_events.labels展开的(name, labelKey, labelValue)三元组不参与发现的字段Non-Goals包括metadata、scope、costMetadata、logdata、spanmetadataRaw以及 scores / feedback 数据。刷新节奏默认值及其理由v0 起始默认刷新节奏辅助表刷新间隔理由discovery_values每 1 分钟支撑最常见的轻量 UI 选择器需要更新更频繁discovery_pairs每 5 分钟预期更大且对延迟不敏感设计文档明确说明这是产品默认值不是硬性的架构要求后续可以按部署规模调整。刷新查询形态UNION ALL 外层 DISTINCT刷新 SQL 的统一形态是把每个源归一化为公共投影再用UNION ALL合并最外层套SELECT DISTINCT。discovery_values刷新形态每个源子查询投影kind、key1、value各发现族子查询之间用UNION ALL连接外层使用SELECT DISTINCT发现族没有父键维度时key1归一化为在进入外层DISTINCT之前丢弃值为NULL或空字符串的valuetag 发现使用ARRAY JOIN tags AS tag指标标签键发现使用ARRAY JOIN mapKeys(labels) AS labelKey。discovery_pairs刷新形态每个源子查询投影kind、key1、key2、value用UNION ALL合并各 pair 发现族子查询外层SELECT DISTINCT没有二级键维度时key2归一化为外层DISTINCT前丢弃NULL/ 空字符串的value指标标签值发现使用ARRAY JOIN mapKeys(labels) AS labelKey并取值labels[labelKey] AS labelValue。归一化规则源表的 tags / labels 视为已由基表写入路径归一化刷新查询不做额外的模糊归一化未使用的发现键槽位归一化为当发现族确实需要真实父键或二级键时丢弃该键为 null 或空的整行而不是伪造合成值丢弃发现值为 null 与空字符串的行端点查询负责排序ORDER BY与分页LIMIT辅助表本身不存储规范化排序顺序。端点映射8 个发现端点当前发现端点共有 8 个映射关系如下端点读取表过滤条件getEntityTypesdiscovery_valueskind entityTypegetEntityNamesdiscovery_pairskind entityTypeName若提供entityType先按存储的实体类型键过滤再做排序与 limitgetServiceNamesdiscovery_valueskind serviceNamegetEnvironmentsdiscovery_valueskind environmentgetTagsdiscovery_valueskind tag若提供entityType按存储的实体类型维度过滤后再排序与 limitgetMetricNamesdiscovery_valueskind metricName先应用prefix再做排序limit在排序之后getMetricLabelKeysdiscovery_valueskind metricLabelKey且指标名匹配getMetricLabelValuesdiscovery_pairskind metricLabelValue指标名匹配且标签键匹配prefix应用于指标标签值排序后limit维度使用的精确条件getTags(entityType)→ 对kind tag过滤discovery_values.key1 entityTypegetMetricLabelKeys(metricName)→ 对kind metricLabelKey过滤discovery_values.key1 metricNamegetMetricLabelValues(metricName, labelKey)→ 对kind metricLabelValue过滤discovery_pairs.key1 metricName且discovery_pairs.key2 labelKey。运维注意无时间范围、bootstrap 与陈旧数据无时间范围过滤的取舍当前发现 API不暴露时间范围过滤器。刷新查询仍然可能扫描较宽的源数据范围但查询时query-time的端点开销不再依赖直接扫描可观测性基表——这是 v0 刻意接受的权衡把“扫描成本”从高频的查询路径转移到低频的刷新路径。Bootstrap 与陈旧行为发现 bootstrap 是可选操作可以在适配器启动之后进行创建完辅助表与刷新式物化视图后bootstrap 应在可能时立即触发两张发现表的刷新只有两张表的首次刷新都成功发现才被视为已填充populated在首次刷新完成之前发现表可能保持为空发现方法在此期间应继续返回空结果bootstrap 失败不应导致基础可观测性适配器失败发现方法应持续返回空结果直到后续某次刷新成功bootstrap 之后发现保持最终一致读取方应持续看到最后一次成功刷新的快照若定时刷新缓慢或失败发现数据可能超出名义刷新间隔而变陈旧一旦至少有一次 bootstrap 刷新成功后续定时刷新失败时应保留最后一次成功快照而不是清空发现。删除与 TTL源表的轻量删除与 TTL 过期会在下一次成功刷新时反映到发现中v0 发现辅助表不需要增量删除传播删除 / TTL 过期后的发现新鲜度受刷新节奏约束而非“读后即删”的即时保证因为辅助表完全派生自源表v0 中它们不需要自己的 TTL。源码实现印证从设计到 DDL 与查询设计文档对应的实现已存在于stores/clickhouse/src/storage/domains/observability/v-next/目录注意该文档集在 README.md 中注明是初始实现的设计指引实现落地后以代码与测试为权威来源。DDL表名、常量与刷新式物化视图在 ddl.ts 中定义了实际表名与视图名常量TABLE_DISCOVERY_VALUES mastra_discovery_valuesTABLE_DISCOVERY_PAIRS mastra_discovery_pairsMV_DISCOVERY_VALUES mastra_mv_discovery_valuesMV_DISCOVERY_PAIRS mastra_mv_discovery_pairs一个值得注意的实现细节见 ddl.ts设计文档 v0 模型写明辅助表用MergeTree而当前实现 DDL 实际采用ReplacingMergeTree排序键覆盖全部列。原因在源码注释中解释得很清楚刷新式物化视图以REFRESH EVERY ... APPEND TO pre-created table的方式写入每次刷新都会追加一份结果集的完整拷贝用ReplacingMergeTree让后台 merge 折叠完全相同的行使磁盘占用随真实基数增长而不是随刷新次数线性膨胀。刷新式物化视图定义在 ddl.tsDISCOVERY_VALUES_MV_DDLREFRESH EVERY 1 MINUTE APPEND与设计默认节奏一致DISCOVERY_PAIRS_MV_DDLREFRESH EVERY 5 MINUTE APPEND与设计默认节奏一致使用APPEND模式普通 INSERT而非原子表切换这是目标表在非 Replicated 数据库中为 Replicated 表时的必要选择重复行由ReplacingMergeTree目标表与带DISTINCT的读取路径共同折叠源表固定为span_events、metric_events、log_events不包含 scores / feedback与设计文档的源表映射完全一致查询体中逐个实现设计文档的 kind 族entityType/serviceName/environment/tag/metricName/metricLabelKey/entityTypeName/metricLabelValue并用ARRAY JOIN tags、ARRAY JOIN mapKeys(labels)、labels[labelKey]展开数组与 Map。查询实现空结果优先绝不回退基表在 discovery.ts 的文件头注释中实现明确复述了设计约束发现方法在辅助表初始化并成功刷新之前返回空结果不回退到基表扫描。各端点的查询实现印证了设计文档的端点映射getEntityTypesdiscovery.tsSELECT DISTINCT value FROM mastra_discovery_values WHERE kind entityType ORDER BY value并在返回前用mastra/core/storage导出的EntityType枚举过滤非法值getEntityNamesdiscovery.tskind entityTypeName可选key1 {entityType:String}参数化过滤ORDER BY valuegetServiceNamesdiscovery.ts与getEnvironmentsdiscovery.ts分别按kind serviceName、kind environment读取getTagsdiscovery.tskind tag可选key1 {entityType:String}过滤。实现统一使用SELECT DISTINCT尽管辅助表是ReplacingMergeTree去重发生在后台 merge 阶段两次刷新之间同一行可能短暂出现多次因此在ORDER BY列上做DISTINCT可以保证无论 merge 时机如何结果始终唯一且在此基数下开销几乎可忽略。与其他设计文档的关系发现设计是 ClickHousev-next设计集的一部分设计集入口为 README.md。与本主题最相关的配套文档shared.md跨表共享决策包含发现的 best-effort 定位、discovery_values/discovery_pairs两张辅助结构、以及“刷新式辅助表优于行级反规范化”的方向physical-types.md两张辅助表的精确列类型与排序键定义metric-events.md指标发现getMetricNames/getMetricLabelKeys/getMetricLabelValues读取辅助表而非直接扫描metric_events的约定span-events.md 与 log-events.mdspan_events、log_events的形状及其 tags / metadata / scope 在发现上的边界。总结ClickHousev-next的发现设计给出了一个清晰的模式把高频率、低成本的 UI 发现查询从大表扫描迁移到由刷新式物化视图维护的紧凑辅助表上。两张辅助表用kind统一承载 8 种发现族用key1/key2表达父子维度关系用空字符串哨兵统一“无父键 / 无二级键”的情况刷新采用UNION ALL 外层 DISTINCT的标准化形态配合 1 分钟 / 5 分钟的默认节奏实现最终一致删除与 TTL 的反映被明确约束为“受刷新节奏而非即时性”保证。这套设计在实现中落地为mastra_discovery_values/mastra_discovery_pairs及其刷新式物化视图并坚持以“返回空结果”而非“回退基表扫描”作为未初始化时的行为契约——这既保护了查询路径的成本边界也让发现能力可以安全地作为可选子系统渐进启用。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考