首页
/ Ghost 的 Tinybird Mock 数据生成实践:从 `numbers(ROWS)` 查询到 `fixtures/` 落地的完整工作流

Ghost 的 Tinybird Mock 数据生成实践:从 `numbers(ROWS)` 查询到 `fixtures/` 落地的完整工作流

2026-09-07 17:20:01作者:何举烈Damon

Ghost 的 Web 分析链路(文章阅读量、活跃访客、KPI 等)由 Tinybird 数据管道承载,开发阶段经常需要向本地 Data Source 灌入可复现的样例数据来验证 Pipes 与 Endpoints。本文以 mock-data.md 这份 Agent 规则文档为主体,讲清 CLI 4.0 下替代已移除的 tb mock 的完整 Mock 数据工作流:如何用 SQL 生成确定行数的 mock 行、如何以 fixtures/<datasource_name>.ndjson 的形式固化样例数据、如何通过 tb datasource append 写入 Tinybird Local,以及隔离区(quarantine)与 mode=create 报错等典型故障的排查方法。读完后你可以独立地为任意一个 Ghost Tinybird 数据源构造可重复的本地测试数据。

背景:CLI 4.0 移除了 tb mock,fixtures 目录成为官方替代方案

Tinybird CLI 4.0 移除了 tb mock 命令。cli-commands.md 中明确给出了新的标准做法:

tb mock was removed in CLI 4.0 Use the fixtures/ folder and agent skills to generate sample data, then append with tb datasource append

即:用 fixtures/ 目录承载样例数据文件,配合仓库中的 Agent 技能(skill)规则生成 mock 行,最后用 tb datasource append 写入数据源。mock-data.md 正是这一流程在 tinybird-cli-guidelines 技能中的具体规则文件,它与 SKILL.md 中"Generating mock data"的适用场景相互对应,定义了 Agent(以及人工开发者)执行 mock 数据生成时的固定步骤、SQL 约束和错误处理策略。

七步 Mock 数据生成流程

mock-data.md 给出的标准流程共 7 步,以下完整保留原文步骤并补充每步的操作细节:

  1. 构建一条返回 mock 行的 SQL 查询。查询的字段必须与目标数据源的 SCHEMA 完全对应(类型、字段名一致),否则 append 会被 Tinybird Local 拒绝。
  2. 本地执行查询并指定格式与行数:使用 tb --output=json|csv '<sql>' --rows-limit <rows> 命令,--output 决定输出为 JSON 还是 CSV,--rows-limit 限制实际取回的行数。
  3. 预览生成的输出,确认字段结构与随机值分布符合预期(例如时间戳是否落在合理的分区范围内)。
  4. 确认要创建的 fixture 文件,路径为 fixtures/<datasource_name>.ndjsonfixtures/<datasource_name>.csv,文件名与数据源名一一对应。
  5. 写入 fixture 文件
  6. 确认追加(append)操作即将执行的命令与目标数据源。
  7. 把 fixture 数据 append 到 Tinybird Local 中的数据源,命令形式见 append-data.md
tb datasource append [datasource_name] --file /path/to/local/file

流程中刻意保留了"预览→确认→写入→确认→追加"的节奏:mock 数据一旦 append 进入 MergeTree 数据源,回滚成本远高于生成成本,因此每一步都要求显式核对后再执行。

编写 Mock 查询:完整示例与两条 SQL 约束

规则文档给出了一条可直接运行的示例查询(来自 mock-data.md):

SELECT
    rand() % 1000 AS experience_gained,
    1 + rand() % 100 AS level,
    rand() % 500 AS monster_kills,
    concat('player_', toString(rand() % 10000)) AS player_id,
    rand() % 50 AS pvp_kills,
    rand() % 200 AS quest_completions,
    now() - rand() % 86400 AS timestamp
FROM numbers(ROWS)

这条查询体现了 mock 查询的几个惯用写法:

  • rand() % N 生成 [0, N) 区间内的伪随机整数,用一行表达式模拟出一个数值字段的分布(示例中的 experience_gainedlevel 等);1 + rand() % 100 则是把取值范围平移到 [1, 100]
  • concat('player_', toString(rand() % 10000)) 生成带前缀的字符串主键(如 player_8412),toString 负责整型到字符串的类型转换。
  • now() - rand() % 86400 生成"最近 24 小时内"的时间戳(86400 秒为一天的秒数),保证 mock 数据的时间分布落在当前活跃分区里,而不是集中在极早的历史分区。

文档同时给出两条硬性约束(mock-data.md Notes):

  1. 查询必须通过 FROM numbers(ROWS) 恰好返回 ROWSnumbers(N) 是 ClickHouse/Tinybird 的标准系统表,返回 0..N-1 共 N 行,配合上面的 SELECT 表达式逐行物化出随机值;把行数写成字面量 ROWS 占位,执行时再替换为 --rows-limit 指定的值,可保证"请求多少行就生成多少行"。
  2. mock 查询本体中不要追加 FORMAT 子句,也不要写结尾分号。格式化与截断的职责全部交给 CLI 的 --output=json|csv--rows-limit 参数处理,SQL 文本保持纯查询语句,才能同时用于预览和落盘。

在 Ghost 仓库中落地:fixtures/ 目录与真实数据源对照

Ghost 仓库中这套约定有真实的落点:Tinybird 项目根目录位于 ghost/core/core/server/data/tinybird/,其 fixtures/ 子目录中已经存在一个符合命名规则的数据集:

对照两者的结构可以清楚看到"mock 行必须贴合 SCHEMA"这条约束的实际含义。数据源声明了 7 个字段:

SCHEMA >
    `timestamp` DateTime `json:$.timestamp`,
    `session_id` String `json:$.session_id`,
    `action` LowCardinality(String) `json:$.action`,
    `version` LowCardinality(String) `json:$.version`,
    `payload` String `json:$.payload`,
    `site_uuid` LowCardinality(String) `json:$.payload.site_uuid`,
    `inserted_at` DateTime64(3) DEFAULT now64() `json:$.inserted_at`

而 fixture 中的每一行都是一条与之对应的 JSON 记录(NDJSON 格式,每行一个对象),例如首行的字段为 timestampsession_idaction: "page_hit"version: "1"、嵌套的 payload(内含 site_uuidpost_uuiduser-agentpathnameutm_* 等字段,与 json: 提取路径一一对应)以及 inserted_at。可以推断该 fixture 的作用正是为本地分析与 Admin 后台的 stats 视图提供可复现的事件行。

另外两个仓库内的旁证值得留意:

  • 分区键影响 mock 数据的时间分布analytics_events.datasource 声明了 ENGINE_PARTITION_KEY "toYYYYMM(timestamp)"ENGINE_SORTING_KEY "site_uuid, timestamp"。这意味着 mock 行的 timestamp 如果全部落在一个月前,查询走到的分区与真实流量分布会不同——示例查询中"最近 24 小时"的写法正是对这类约束的正面回应。
  • 开发容器以 tb --local build 构建项目docker/tb-cli/entrypoint.sh 在启动时执行 tb --local build 部署 Tinybird 文件并提取 workspace token 供 Ghost 与分析服务连接,说明本地迭代以 Tinybird Local 为默认目标(与 cli-commands.md 中"--local 是 append 的默认目标,--cloud 才指向 Cloud"的说明一致)。
  • 前端测试侧的 mock 是另一条独立路径。Admin 后台的验收测试并不依赖真实 Tinybird,而是用 MSW 直接伪造 pipe 的 HTTP 响应与 GET /tinybird/token/ 令牌,见 apps/admin/test-utils/acceptance/tinybird.ts。它演示了"声明的行原样返回、断言查询参数"的测试契约,与本文的 fixtures 工作流互补:前者面向前端请求层,后者面向数据层。

生成后的写入:append 到 Tinybird Local

预览与 fixture 文件确认无误后,按第 7 步把数据 append 进数据源。append-data.md 给出三种入口,按数据来源选择:

# 本地文件(mock 数据的主路径)
tb datasource append [datasource_name] --file /path/to/local/file

# 远程 URL
tb datasource append [datasource_name] --url https://example.com/data.csv

# 直接传 JSON 事件
tb datasource append [datasource_name] --events '{"a":"b", "c":"d"}'

要点:

  • 该命令追加到已存在的数据源,不会清空既有数据;
  • 默认目标是 Tinybird Local,需要操作 Cloud 时显式加 --cloud 前缀;
  • 若后续要做数据替换或删除,注意 data-operations.md 的告警:replace 不应作用于正在持续写入的分区,delete --sql-condition 异步执行且不会级联到下游物化视图。

错误处理:隔离区与 mode=create 报错

mock-data.md 的 Error Handling Notes 定义了两类典型故障的标准处置方式:

数据源进入隔离区(quarantine)

当 append 的行与 schema 不符等校验失败时,Tinybird Local 会把数据源置入 quarantine 状态。处置方法是:查询 <datasource_name>_quarantine 这个隔离数据源,并向上呈现前 5 行。隔离行保留了"被拒数据原样 + 失败原因"的元信息,前 5 行足够定位是字段类型不匹配、缺失必填字段还是时间格式问题,修正 mock 查询后重新生成 fixture 即可。

append 报错 must be created first with 'mode=create'

该错误表示 CLI 侧认为数据源尚未经 mode=create 流程创建。规则给出的处置是:重新构建项目后重试。对照仓库内开发容器的启动脚本,"重建项目"对应的正是 docker/tb-cli/entrypoint.sh 中的 tb --local build——重新校验并按文件构建整个 Tinybird 项目后再执行 append。

小结与延伸阅读

本文以 mock-data.md 为主线,覆盖了三块可独立复用的能力:

  1. 可复现的 mock 查询写法FROM numbers(ROWS) 控制精确行数,rand() % N / concat / now() - rand() % 86400 控制字段分布,SQL 本体不带 FORMAT 与分号;
  2. fixtures 落地规范fixtures/<datasource_name>.ndjson|.csv 命名,字段严格对齐数据源 SCHEMA,可对照 analytics_events 数据源与 fixture 的真实样例;
  3. 写入与排障tb datasource append --file 追加、quarantine 前 5 行定位、mode=create 报错重建重试。

如需继续深入,可参考仓库内以下资料:

登录后查看全文
热门项目推荐
相关项目推荐