GreptimeDB 分布式元数据服务 meta-srv 架构深度解析:心跳、选举、分布式过程与配置实战
【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb
GreptimeDB 采用存算分离的分布式架构,而meta-srv正是这套集群的"元数据大脑":它持久化集群/表/Region 元数据、负责 Leader 选举、驱动心跳循环追踪集群拓扑,并编排 DDL、Region 迁移、Repartition 等分布式过程。本文以 src/meta-srv/AGENTS.md 为核心脉络,结合meta-srvcrate 源码与 config/metasrv.example.toml 真实配置,系统讲解其模块划分、核心流程、配置项语义、公开接口与测试方法,帮助你理解并维护 GreptimeDB 的元数据服务层。
meta-srv 在整个项目中的定位
在分布式模式下,GreptimeDB 由 Frontend、Datanode、Flownode 与 MetaSrv 共同组成。meta-srv承担元数据与协调职能,具体包括:
- 持久化集群/表/Region 元数据:所有元数据以 KV 形式写入后端存储(默认 etcd),跨节点共享;
- Leader 选举:在多个 MetaSrv 副本之间选出唯一 Leader,保证写操作一致性;
- 心跳循环与集群拓扑追踪:通过 Frontend/Datanode/Flownode 上报的心跳维护节点存活状态与集群拓扑;
- 分布式过程编排:DDL(建表/删表等)、Region 迁移、Repartition 等操作以可恢复的过程(Procedure)形式执行。
从 crate 依赖来看,数据模型、KV 后端抽象、选举、Key 编码与 DDL 管理器都实现在common-metacrate 中;meta-srv在其之上实现服务端、状态机与控制逻辑。分层遵循仓库级架构约束(见 .agents/architecture-invariants.md 中"common-* 不能反向依赖 storage engine、frontend、datanode、meta-srv"的约定),meta-srv通过common-meta、common-procedure等基础 crate 协作,而不是重复实现元数据模型。
模块地图:meta-srv 源码导航
meta-srv的源码全部位于 src/meta-srv/src,AGENTS.md 给出了清晰的模块导航表。下面按模块逐一说明其职责与关键文件:
| 模块 | 路径 | 职责 |
|---|---|---|
bootstrap | src/meta-srv/src/bootstrap.rs | 组装 KV 后端、选举、gRPC 服务与过程管理器,完成进程启动 |
metasrv | src/meta-srv/src/metasrv.rs、src/meta-srv/src/metasrv/builder.rs | Metasrv结构体、MetasrvOptions配置、MetasrvBuilder构建器与启动逻辑 |
state | src/meta-srv/src/state.rs | Leader/Follower 状态机与状态迁移 |
handler | src/meta-srv/src/handler/ | 心跳处理器链(HeartbeatHandler):Leader 检查、Region 租约、统计、信箱 |
service | src/meta-srv/src/service/ | gRPC 服务(heartbeat、procedure、cluster、store)与 HTTP admin 接口 |
procedure | src/meta-srv/src/procedure/ | 分布式过程:region_migration/、repartition.rs、wal_prune/ |
region | src/meta-srv/src/region/ | Region 监督器、租约维护、故障转移触发 |
discovery | src/meta-srv/src/discovery/ | 节点发现与基于租约的节点信息 |
pubsub | src/meta-srv/src/pubsub/ | 心跳主题的发布/订阅支持 |
gc | src/meta-srv/src/gc/ | 元数据驱动的垃圾回收过程与调度 |
selector | src/meta-srv/src/selector/ | Region 放置策略(round-robin / load-based / lease-based) |
peer | src/meta-srv/src/peer.rs | 通过 selector 分配 Peer |
cluster | src/meta-srv/src/cluster.rs | MetaPeerClient:带 Leader 回退的内部 RPC |
cache_invalidator | src/meta-srv/src/cache_invalidator.rs | 向 Frontend/Datanode 推送缓存失效通知 |
key | src/meta-srv/src/key/ | MetaSrv 侧的 KV Key 编码 |
bootstrap:启动装配与 gRPC 路由
bootstrap.rs中的MetasrvInstance::start()是进程启动的核心入口:先启动 HTTP 服务,再通过router()函数装配 gRPC 服务。从 src/meta-srv/src/bootstrap.rs 可以看出,gRPC 路由依次注册了五个服务,且全部开启 Gzip/Zstd 压缩与消息大小限制:
HeartbeatServer:心跳服务,接收 Frontend/Datanode/Flownode 的心跳流;StoreServer:元数据 KV 读写服务;ClusterServer:集群管理服务;ProcedureServiceServer:过程执行服务;ConfigServer:配置下发服务;admin::make_admin_service:兼容旧版的 admin 服务。
后端选择逻辑见metasrv_builder()(src/meta-srv/src/bootstrap.rs):根据opts.backend决定使用MemoryKvBackend(测试用)、EtcdStore(默认,并同时构造EtcdElection)或 PostgreSQL/MySQL(需要对应 feature)。
state:Leader/Follower 状态机
src/meta-srv/src/state.rs 中有一段 ASCII 状态图,完整描述了状态迁移路径:
+------------------------------+ | | v | +-------------------v--------------------+ | | LeaderState{enable_leader_cache:false} | | +-------------------+--------------------+ | | | +---------v---------+ | | Init Leader Cache | | +---------+---------+ | | | +-------------------v-------------------+ | | LeaderState{enable_leader_cache:true} | | +-------------------+-------------------+ | | | +-------v-------+ | | FollowerState | | +-------+-------+ | | | +------------------------------+关键语义:新 Leader 上任时先进入enable_leader_cache: false状态(此时禁用 Leader 缓存),完成缓存初始化后切换为enable_leader_cache: true;一旦发生 Leader 变更则回退为FollowerState。become_leader()与become_follower()两个函数(src/meta-srv/src/state.rs)正是上述迁移的具体实现。这正是 AGENTS.md 中"Leader 使用在 Leader 变更时重置的缓存 KV 后端(LeaderCachedKvBackend)"这一注意事项的状态机体现。
核心流程:四条主线
AGENTS.md 归纳了四条核心流程,下面结合源码逐一展开。
心跳流程(Heartbeat)
心跳是集群拓扑感知的基础。service/heartbeat.rs中的HeartbeatSession维护一条 gRPC 双向流:
- 收到首个心跳请求后校验
RequestHeader,并向HeartbeatHandlerGroup注册 pusher(src/meta-srv/src/service/heartbeat.rs); - 循环
select两条事件:流上新的心跳消息、或 Leader 下台事件(wait_leader_step_down)。一旦通过选举订阅收到LeaderChangeMessage::StepDown,立即向客户端返回 "not leader" 错误并终止会话(src/meta-srv/src/service/heartbeat.rs); - 响应中携带信箱(mailbox)消息,用于下发 DDL 结果、缓存失效等指令。
每个心跳请求会顺序穿过handler/下的处理器链。从 src/meta-srv/src/handler.rs 的add_default_handlers()可以看到默认链的完整组成(顺序即执行顺序):
ResponseHeaderHandler— 写入响应头;DatanodeKeepLeaseHandler/FlownodeKeepLeaseHandler— 续租节点租约;CheckLeaderHandler— 仅 Leader 处理,非 Leader 返回 "not leader";OnLeaderStartHandler— Leader 上任初始化;ExtractStatHandler— 提取节点统计;CollectDatanodeClusterInfoHandler/CollectFrontendClusterInfoHandler/CollectFlownodeClusterInfoHandler— 采集各角色集群信息;MailboxHandler— 处理信箱消息;RegionLeaseHandler— Region 租约续期(条件挂载);FilterInactiveRegionStatsHandler— 过滤非活跃 Region 统计;RegionFailureHandler— Region 故障处理(开启 failover 时挂载);PublishHeartbeatHandler— 向 pubsub 发布心跳(开启时挂载);CollectLeaderRegionHandler— 采集 Leader Region 信息;CollectTopicStatsHandler— 采集 WAL 主题统计;PersistStatsHandler— 持久化统计(开启时挂载);CollectStatsHandler— 汇总统计(基于flush_stats_factor触发刷写);RemapFlowPeerHandler— 流(Flow)Peer 重映射;FlowStateHandler— 流状态处理(条件挂载)。
该构建器还提供add_handler_after/add_handler_before/add_handler_last三个扩展点,并支持通过HeartbeatHandlerGroupBuilderCustomizer插件化定制链,测试与扩展都围绕这套机制展开。
DDL 流程
DDL 由 src/meta-srv/src/service/procedure.rs 暴露,是Leader-only的:非 Leader 节点会返回 "not leader"。Leader 收到 DDL 请求后交给common-meta的DdlManager处理,并经由common-procedure以可恢复过程的形式执行。这意味着 DDL 状态会被持久化到 KV 后端,节点崩溃后可恢复继续执行。
Region 迁移流程
Region 迁移由 src/meta-srv/src/procedure/region_migration/manager.rs 以及region_migration/目录下的分步文件实现。从目录结构看,迁移被拆分为多个步骤:
migration_start.rs/migration_end.rs/migration_abort.rs— 迁移的启停与中止;downgrade_leader_region.rs/flush_leader_region.rs/close_downgraded_region.rs— 旧 Leader Region 降级与刷盘关闭;open_candidate_region.rs/upgrade_candidate_region.rs— 候选 Region 打开与升级;update_metadata.rs及其子步骤(upgrade_candidate_region、downgrade_leader_region、rollback_downgraded_region)— 元数据更新与回滚。
迁移可以由故障转移触发,也可以通过显式命令发起;其状态机定义与执行步骤可进一步参考 src/meta-srv/src/procedure/region_migration.rs。
Leader 选举流程
选举能力来自common-meta的 election 抽象(etcd 后端对应EtcdElection),meta-srv侧的状态迁移集中在 src/meta-srv/src/state.rs。当选时become_leader()触发 Leader 缓存初始化;失选时become_follower()回退状态并重置 Leader 缓存,从而保证新的 Leader 从 KV 后端重建内存视图。
MetasrvOptions 配置详解
配置结构体MetasrvOptions定义在 src/meta-srv/src/metasrv.rs,完整示例见 config/metasrv.example.toml。下面按主题分组解读核心配置项。
元数据后端(backend / store_addrs)
backend:元数据存储实现,对应BackendImpl枚举(src/meta-srv/src/metasrv.rs),取值包括etcd_store(默认)、memory_store(测试用)、postgres_store与mysql_store(需编译对应 featurepg_kvbackend/mysql_kvbackend);store_addrs:后端地址列表。etcd 为"host:port"列表;PostgreSQL 为 libpq 连接串或 URI;MySQL 为连接 URL;store_key_prefix:非空时所有 KV key 加此前缀,用于多个 meta-srv 集群共享同一存储;max_txn_ops:单事务最大操作数,默认 128,应小于 etcd 的--max-txn-ops;backend_client:后端客户端保活与连接超时(keep_alive_timeout=3s、keep_alive_interval=10s、connect_timeout=3s);backend_tls:KV 后端的 TLS 配置,支持disable/prefer/require/verify_ca/verify_full五种模式。
心跳与故障转移(heartbeat / failover)
heartbeat_interval:基础心跳间隔,用于推导分布式时间常量,例如 Region 租约时长约为heartbeat_interval * 3 + 1s。Frontend 的心跳间隔是基础间隔的 6 倍,Datanode/Flownode 为 1 倍;心跳间隔在握手阶段由 MetaSrv 协商下发,节点本地配置不会覆盖它;enable_region_failover:是否启用 Region 故障转移,仅在使用 Remote WAL + 共享存储(如 S3)的集群模式下可用;region_failure_detector_initialization_delay:Region 故障检测启动前的延迟(默认 10 分钟),避免 Datanode 尚未全部就绪时误触发 failover,对未使用 GreptimeDB Operator 且未开启维护模式的部署尤为重要;allow_region_failover_on_local_wal:是否允许本地 WAL 场景下的 Region failover,官方不建议开启,可能造成数据丢失;node_max_idle_time:节点信息从 MetaSrv 内存移除前的最大空闲时间(默认 24h)。
状态机与过程(state / procedure)
[procedure]:过程执行配置。max_retry_times=12(最大重试次数)、retry_delay=500ms(初始重试延迟,指数递增)、max_metadata_value_size=1500KiB(etcd 单请求上限 1.5 MiB 减去 36KiB key 保留空间)、max_running_procedures=128(并发过程上限,超出即拒绝)。
故障检测(failure_detector)
MetaSrv 使用Phi Accrual Failure Detector算法检测 Datanode 故障:
threshold(默认 8.0):判定 Peer 故障的 φ 阈值,越小反应越快但误报越多;min_std_deviation(默认 100ms):心跳间隔的最小标准差,防止间隔几乎不变时 φ 爆炸;acceptable_heartbeat_pause(默认 10000ms):心跳间可接受的暂停时长,为学习到的平均间隔追加额外宽限期,吸收网络抖动与 GC 暂停。
Region 放置(selector)
selector决定 Region 放置在哪个 Peer 上,支持三种策略:
round_robin(默认):轮询分配;lease_based:基于租约;load_based:基于负载。
对应实现见 src/meta-srv/src/selector/:round_robin.rs、load_based.rs、lease_based.rs,以及weight_compute.rs/weighted_choose.rs提供的加权计算。修改 selector 时需注意与RegionStatAwareSelector消费的统计字段保持一致(见下文"修改关联")。
统计持久化与 GC(stats / gc)
[stats_persistence]:ttl默认0s(关闭持久化,大于 0 时开启,建议小值如3h);interval默认10m,低于10m会被强制覆盖为10m;[gc]:enable=false默认关闭,且必须与 Datanode 的mito.gc.enable保持一致;gc_cooldown_period=5m为同一 Region 两次 GC 之间的冷却期。
事件记录与遥测(event_recorder / telemetry)
[event_recorder]:事件表 TTL 默认90d;event_types为空数组时禁用记录,省略时记录全部当前与未来事件类型(如region_migration、create_table、repartition、wal_prune、batch_gc等);enable_telemetry:默认开启 GreptimeDB 遥测。
gRPC / HTTP 监听
[grpc]:bind_addr(默认127.0.0.1:3002)、server_addr(向 Frontend/Datanode 通告的地址,留空则自动取主机第一网卡 IP 加同端口)、runtime_size、max_recv/send_message_size、HTTP/2 keep-alive 参数;[http]:addr(默认127.0.0.1:4000)、timeout、body_limit(默认64MB)。
WAL 配置(wal)
MetaSrv 在 Kafka 作为远端 WAL 时需要同步配置[wal]:
provider:raft_engine(默认)或kafka;- Kafka 相关:
broker_endpoints、auto_create_topics、num_topics=64、topic_name_prefix、replication_factor、selector_type、SASL/TLS; auto_prune_interval=30m:自动清理远端 WAL 的间隔,0s关闭;auto_prune_logical_delete用于不支持 DeleteRecords 的 Kafka 部署;flush_trigger_size=512MB/checkpoint_trigger_size=128MB:基于(latest_entry_id - flushed_entry_id) * avg_record_size估算的刷盘/检查点触发阈值,0表示由系统自行决定。
公开接口:客户端与 Admin API
AGENTS.md 明确指出对外接口分两侧:
- 客户端侧:统一使用
meta-clientcrate 的MetaClient,封装了对 MetaSrv 各 gRPC 服务的调用; - 服务端侧:入口是 src/meta-srv/src/service/ 下的 gRPC 服务(heartbeat、procedure、cluster、store),以及 src/meta-srv/src/service/admin/ 下的 HTTP admin API。
从 admin 目录可看到一组实用的运维接口:health(健康检查)、leader(查看当前 Leader)、procedure(过程管理)、maintenance(维护模式)、recovery(恢复)、node_lease(节点租约)、sequencer(序列号)、heartbeat(心跳调试)等。
修改代码时的联动关系
AGENTS.md 的"When you change X, also touch Y"部分给出了高度可操作的联动清单,这既是维护契约,也是理解模块耦合的捷径:
- 心跳间隔 / 租约:时间常量定义在
common-meta的distributed_time_constants(源码中可见BASE_HEARTBEAT_INTERVAL、frontend_heartbeat_interval等引用,见 src/meta-srv/src/metasrv.rs);修改它会同时影响handler/region_lease_handler.rs的租约续期与region/supervisor.rs的故障判定。 - 过程(Procedure):状态必须持久化在 KV 后端,且
execute()必须幂等,才能保证崩溃恢复正确(这与 .agents/architecture-invariants.md 的持久化格式兼容性约束一致)。 - 缓存失效:新增元数据类型时,需要在
cache_invalidator.rs与心跳发布处理器(PublishHeartbeatHandler)中同步补充失效逻辑。 - Selector 统计字段:
selector/的实现必须与其消费的统计字段保持同步,否则 Region 放置会读到错误数据。
测试方法
AGENTS.md 给出了标准的测试命令:
# 运行 meta-srv 的全部测试 cargo nextest run -p meta-srv # 使用 mock 选举 / KV 后端(更快的单元测试) cargo nextest run -p meta-srv --features mockmockfeature 在 src/meta-srv/Cargo.toml 中声明,可避免依赖真实 etcd。测试基础设施还包括三个辅助文件:
- src/meta-srv/src/mocks.rs — 各类 mock 组件;
- src/meta-srv/src/test_util.rs — 通用测试工具;
- src/meta-srv/src/procedure/test_util.rs — 过程测试工具。
此外,handler.rs的测试模块中大量使用HeartbeatHandlerGroupBuilder::new(...).add_default_handlers()组装真实处理器链,并通过add_handler_after/add_handler_before注入自定义处理器,验证链的顺序与扩展点行为(src/meta-srv/src/handler.rs)。
常见陷阱(Gotchas)
AGENTS.md 总结了三个最容易踩坑的点,也是 review 与排障的检查清单:
- 写操作必须 Leader-only:绝大多数变更操作只允许 Leader 执行,
handler/与service/procedure.rs中都有对应的 Leader 检查;非 Leader 节点统一返回 "not leader"。心跳会话中若监听到 Leader 下台也会主动断开(见 src/meta-srv/src/service/heartbeat.rs)。 - 内存状态不可持久:Leader 变更后需要保留的任何状态都必须写入 KV 后端;Leader 使用
LeaderCachedKvBackend,该缓存会在 Leader 变更时重置。 - 租约时序必须协调:Region 租约续期节奏与 supervisor 的检查间隔必须协调,否则同一个 Region 可能同时出现在两个 Datanode 上(双活脑裂)。
维护契约:何时更新 AGENTS.md
AGENTS.md 本身也是一份需要随代码演进的活文档,其维护契约要求:新增过程(procedure)、修改心跳处理器链、变更 Leader-only 边界、或调整 metasrv 与 common-meta 的分工时,必须同步更新本文件。对贡献者而言,遵循该契约可以确保这份导航文档始终与实际代码结构一致,这也是本仓库将 crate 级 AGENTS.md 作为协作规范的一部分(其余仓库级规范见 .agents/README.md 与 .agents/architecture-invariants.md)。
小结
meta-srv是 GreptimeDB 分布式模式的控制面核心,其设计呈现出清晰的"分层 + 状态机 + 过程化"特征:元数据模型下沉到common-meta,服务与状态机在meta-srv,分布式操作全部以可恢复过程执行,心跳链则通过可定制的处理器组完成拓扑感知与指令下发。理解模块地图、四条核心流程、MetasrvOptions配置语义以及联动修改清单,是深入参与 meta-srv 开发与运维的第一步;配合cargo nextest run -p meta-srv与 mock feature,你可以快速验证改动而不必依赖真实集群。
【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考