mcpField
C
clickhouse-migrations

clickhouse-migrations

github.com/posthog数据与数据库40.5k258 次安装今天更新
代码仓库

关于 clickhouse-migrations

用于编写 PostHog ClickHouse 迁移的仓库技能,指导开发者选择集群、保持 Schema 一致,并遵循安全规则与测试流程。

主要功能

  1. 01按集群选择节点角色
  2. 02ClickHouse 引擎与迁移模式
  3. 03Schema 变更安全规则
  4. 04本地 Schema 与 HCL 一致性
  5. 05迁移测试指引

适用场景

  1. 01向 ClickHouse 添加表或视图
  2. 02为新数据对象选择集群
  3. 03准备仅包含迁移的 Pull Request

MCP 相关

该指南要求使用 ClickHouse MCP 检查 dev、prod-us 和 prod-eu 的实时集群,再确定对象的部署位置。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/posthog/posthog --skill clickhouse-migrations

安装后在 Claude Code 中输入/clickhouse-migrations确认已加载。

详细介绍

ClickHouse 迁移

阅读 posthog/clickhouse/migrations/AGENTS.md,了解全面的模式、集群设置、示例以及摄入层细节。

快速参考

迁移结构
operations = [
    run_sql_with_exceptions(
        SQL_FUNCTION(),
        node_roles=[...],
        sharded=False,  # True for sharded tables
        is_alter_on_replicated_table=False  # True for ALTER on replicated tables
    ),
]
节点角色(根据拥有该对象的集群来选择)

只有 posthog/clickhouse/client/connection.py 中 NodeRole 的成员是有效的:ALL、DATA、 INGESTION_EVENTS、INGESTION_SMALL、INGESTION_MEDIUM、ENDPOINTS、LOGS、APM、AI_EVENTS、 AUX、BATCH_EXPORTS、OPS、SESSIONS。不在该枚举中的名称会在导入时报错,从而中止 迁移发现,并连带拖垮所有运行迁移的任务。

根据哪个集群拥有该对象来选择角色,而不要仅依据其类型:

  • [NodeRole.DATA]:属于主集群的对象——分片表、位于分片表或卫星表之前的 Distributed 读 表、视图、字典。位于 DATA 上的非分片复制表会在每个主集群节点上保留一份完整副本,因此它不适合 承载产品汇总或预聚合(见下文)。
  • [NodeRole.INGESTION_SMALL]:可写表、Kafka 表、摄入层上的物化视图
  • [NodeRole.APM]:ingestion-apm 节点,即指标摄入层。Kafka 表、Null 输入 表、物化视图,以及写入 LOGS 上存储的可写 Distributed 表。 与其他摄入节点一样是无状态的,因此存储表绝不能放在这里。
  • [NodeRole.OPS]、[NodeRole.LOGS]、[NodeRole.AUX]、[NodeRole.AI_EVENTS]、[NodeRole.SESSIONS]、 [NodeRole.BATCH_EXPORTS]:位于卫星集群上的对象。把服务于上述某一角色的表放在 DATA 上会落到错误的节点,并使原本预期的集群缺少该表,因此在默认选择 DATA 之前,请先确认 该对象在哪里被读取和写入。Dev 运行与 US/EU 生产相同的卫星集群——绝不要根据 CLOUD_DEPLOYMENT 分支 来给 dev 一个不同的布局。
  • [NodeRole.ALL]:很少使用

预聚合和汇总存储在卫星集群上,并通过 DATA 上的 Distributed 表读取 (如需在存储卫星上临时读取,也可在那里读取),除非应用已经直接连接到该卫星。 对卫星自身数据的汇总保留在该 卫星上;通用的产品预聚合则放到 AUX。用下面的实时检查确认卫星, 并用 hclexp locate 查找当前示例的 HCL 层。

在选择角色之前检查实时集群

较旧的迁移、HCL 的 local 层以及共享辅助函数可能落后于 dev 和生产中实际运行的内容。在编写迁移之前,请对照实时集群确认放置位置:

  1. 使用 ClickHouse MCP 检查 dev、prod-us 和 prod-eu 中的每一个:可比较的表位于何处, 主集群通过哪些 Distributed 表读取哪些卫星表,以及新表 名称未被占用。有关设置和用法,请参阅 Housekeeper MCP runbook。
  2. 当先例相互矛盾时,遵循该表族最新的迁移,并确认它与 所有三个环境中运行的内容一致。只有部分环境仍在运行的布局属于迁移 中途状态,不是可以照搬的模式。
  3. 在 PR 描述中说明每个新对象落到哪个集群,并指出该选择所依据的参考迁移 或实时检查。
表引擎快速参考

MergeTree 引擎:

  • AggregatingMergeTree(table, replication_scheme=ReplicationScheme.SHARDED) 用于分片表
  • ReplacingMergeTree(table, replication_scheme=ReplicationScheme.REPLICATED) 用于非分片表
  • 其他变体:CollapsingMergeTree、ReplacingMergeTreeDeleted

Distributed 引擎:

  • 分片:Distributed(data_table="sharded_events", sharding_key="sipHash64(person_id)")
  • 非分片:Distributed(data_table="my_table", cluster=settings.CLICKHOUSE_SINGLE_SHARD_CLUSTER)

Kafka 引擎 —— kafka_engine() 仅返回 ENGINE 子句,因此每个表都要自行追加 SETTINGS。从这个基线开始:

SETTINGS kafka_skip_broken_messages = 100,
         kafka_num_consumers = {kafka_num_consumers(1)},
         kafka_thread_per_consumer = 1,
         kafka_poll_timeout_ms = 10000,
         kafka_max_block_size = 100000
  • kafka_skip_broken_messages 默认为 0,此时一条格式错误的消息就会永久停止消费者。
  • kafka_poll_timeout_ms 对于 WarpStream 为 10000,因为 WarpStream 不支持 fetch.min.bytes。
  • kafka_max_block_size 默认约为一百万行,因此 100000 是缩减;对于宽行应调低, 但不要低于约 1000。
  • kafka_num_consumers 是每个节点的,因此该角色的节点数会将其倍增;总数不得 超过主题的分区数。
  • 列和设置在创建时即固定:删除并连同其 MV 一起重建表,不要用 SYNC。 ALTER TABLE <mv> MODIFY QUERY 可覆盖仅修改 MV-SELECT 的情况。切勿用于 kafka_events_json_ws / events_json_ws_mv —— 参见关键规则。

posthog/clickhouse/migrations/AGENTS.md 中有容量规划规则及其余内容。

关键规则
  • 绝不在 SQL 语句中使用 ON CLUSTER 子句
  • 始终使用 IF EXISTS / IF NOT EXISTS 子句
  • 在同一迁移中删除并重建复制表时,使用 DROP TABLE IF EXISTS ... SYNC
  • 如果生成 SQL 的函数有 on_cluster 参数,始终设置 on_cluster=False
  • 修改分片表时使用 sharded=True
  • 修改非分片复制表时使用 is_alter_on_replicated_table=True
  • 绝不在列上写 CODEC(ZSTD(1)) —— 服务端已用 ZSTD 压缩每一列,因此这毫无收益。只有在能胜过该默认值的地方才声明 CODEC,并先检查 ORDER BY:Delta/DoubleDelta 需要该列在存储顺序中接近有序(排序键的前缀),而在仅被键分桶或未包含在键中的列上则会失效。T64/Gorilla 与排序无关。只将它放在存储表上 —— 在 Distributed 或 Kafka 表上的 CODEC 是无效元数据,会与其前置的分片表产生偏差。
  • 绝不要自行编写 DROP COLUMN 迁移 —— DROP COLUMN 可能会在 ClickHouse 中卡住并阻塞发布。删除列是一个两步流程:(1) ClickHouse 团队直接在集群上删除该列,然后 (2) 你编写带有匹配 DROP COLUMN 的迁移,使代码库 schema 保持同步。在 ClickHouse 团队未先完成第 1 步的情况下,绝不要从迁移中发起删除。
  • 绝不要删除或重建 kafka_events_json_ws 或 events_json_ws_mv —— 这些表是禁区。MV 定义在美国生产、欧洲生产和开发环境之间差异很大(数十个特定于环境的 mat_* 列),而这些差异并未反映在仓库中。根据仓库 SQL 删除并重建会破坏特定于环境的 schema 并中断事件摄取。任何更改都必须经过 ClickHouse 团队。
PR 范围

包含 ClickHouse 迁移的 PR 必须仅包含迁移。不要在同一 PR 中将迁移文件与功能代码、API 变更、模型变更或前端变更混在一起。与迁移相关的文件包括:

  • 迁移文件本身(posthog/clickhouse/migrations/0NNN_*.py)
  • 迁移所依赖的 SQL 定义文件(例如 posthog/clickhouse/sql/*.py、表引擎辅助文件)
  • 直接测试该迁移或其所涉及的 SQL 定义的测试

如果你同时需要 schema 变更和使用新 schema 的应用代码,请先以单独的 PR 发布迁移,并在应用代码 PR 之前合并它。

本地设置一致性

不应存在只存在于云端的表。 通过迁移创建的每一张表,也都必须存在于本地开发环境中。

有些迁移受云端保护,会在本地/hobby 开发环境中被跳过。判定条件应基于 posthog.run_mode,而不要直接基于 settings.CLOUD_DEPLOYMENT,因为 clickhouse-migrations-use-run-mode semgrep 规则会阻止原始比较:

from posthog.run_mode import RunMode, run_mode

operations = (
    []
    if not run_mode().is_deployed_cloud  # US/EU/DEV
    else [...]
)
判定条件为 true 的情况
run_mode().is_deployed_cloudUS、EU、DEV(通常的迁移判定条件)
run_mode().is_prod_cloudUS、EU(不包括 staging)
run_mode() is RunMode.CLOUD_US单个区域(CLOUD_EU、CLOUD_DEV 同理)

run_mode().is_cloud 也会把 E2E 计算在内,因此不适用于迁移判定。该项匹配的是 posthog.cloud_utils.is_cloud。

在需要时调用 run_mode(),而不要将其赋值给模块级常量。posthog/clickhouse/test/test_migrations.py 会在打补丁后的 posthog.settings.CLOUD_DEPLOYMENT 下重新导入每个迁移,以检查每个部署分支中是否存在误加的 ON CLUSTER,而缓存的值会静默跳过这一覆盖检查。

如果你在这样的保护分支内创建新表,还必须将其 SQL 函数添加到 posthog/clickhouse/schema.py 中相应的元组,以便该表在本地被创建:

表类型schema.py 中的元组
MergeTree / 基础表CREATE_MERGETREE_TABLE_QUERIES
Distributed / 可写入CREATE_DISTRIBUTED_TABLE_QUERIES
Kafka 消费者CREATE_KAFKA_TABLE_QUERIES
物化视图CREATE_MV_TABLE_QUERIES
非物化视图CREATE_VIEW_QUERIES
字典CREATE_DICTIONARY_QUERIES

唯一的例外是那些定义在不同环境中有意不同、且未在仓库中跟踪的表(例如禁区中的 events_json_ws_mv 表)。

字典凭据: 当字典使用 SOURCE(CLICKHOUSE(...)) 时,应通过 get_clickhouse_creds(ClickHouseUser.DICT_READER) 解析源用户名/密码,并将其插入到 USER/PASSWORD 子句中——不要硬编码 default/CLICKHOUSE_USER,也不要省略凭据。这样可以保持字典认证使用专用的低权限 dict_reader 用户,与 default 解耦;当环境变量未设置时,它会回退到 default 凭据。该模式可参见 posthog/models/exchange_rate/sql.py。

声明式 schema(HCL)必须一致

有些角色也在 posthog/clickhouse/hcl/ 中声明,而多节点迁移冒烟测试会重新内省 已迁移的集群,并在实时 schema 与已提交的 golden 之间存在任何漂移时失败。因此,在受管 角色上由迁移添加的表,需要在同一个 PR 中更新 HCL,否则 CI 会在 check_live_hcl 处失败,并给出一个 DRIFT: 块,列出你的新对象。

manifest.hcl 中带有 local-multi 块的每个角色都是受管的,这覆盖主集群的 data 角色以及大多数卫星和摄取角色。请在该文件中检查你目标角色;在受管角色上新增表也计入。 编写迁移之后:

HCL=posthog/clickhouse/hcl
# add the table to the layer that composes it (hclexp locate a sibling table to find that layer)
bash $HCL/gen-golden.sh && bash $HCL/gen-sql.sh   # refresh generated artifacts
bash $HCL/check.sh                                # must exit 0

在编辑某一层之前,请先阅读 posthog/clickhouse/hcl/README.md——其中介绍了应选择哪一层、抽象 列列表,以及为你编写迁移的代码生成路径。

测试

从 infi_clickhouse_orm_migrations 表中删除对应条目即可重新运行迁移。

评论

支持 Markdown