Rand Stats

App::DuckETL

zef:skyter10086

App::DuckETL

SQL-first ETL on DuckDB. Raku only plans SQL and renders results; rows stay inside DuckDB.

当前进度:M0 引擎 + M1 只读 TUI + M2 编辑闭环 + M3 生产化(ducketl run/tui/worker、增量水位、Parquet 分区写、--isolated worker、DuckDB 扩展装载、Pipeline 表单 / $EDITOR / 保存 JSON、history、失败回滚、原子落盘、运行取消)。设计依据见 TUI-ETL-设计分析.md。

快速开始

raku -Ilib bin/ducketl validate examples/pipelines/nightly_orders.json
raku -Ilib bin/ducketl run examples/pipelines/nightly_orders.json
# => examples/out/orders.parquet

raku -Ilib bin/ducketl run examples/pipelines/orders_table.json
# => examples/out/ducketl.duckdb, mart.orders(upsert)

raku -Ilib bin/ducketl tui examples/pipelines/nightly_orders.json
# => 全屏 TUI:Dashboard / Sources / Pipeline / Run

raku -Ilib bin/ducketl run examples/pipelines/orders_incremental.json
raku -Ilib bin/ducketl run examples/pipelines/orders_incremental.json
# => 第二次按 created_at 水位追加 0 行

raku -Ilib bin/ducketl run examples/pipelines/partitioned_orders.json
# => examples/out/orders_by_country/country=*/data_0.parquet

ducketl extensions                       # 列出 DuckDB 扩展
ducketl tui --isolated pipeline.json     # worker 进程独占数据库

用 zef 安装:

zef install .
ducketl run pipeline.json
ducketl tui pipeline.json

依赖:Rakudo 2026.09、DB::DuckDB 0.8.3、Alien::Libduckdb 1.1.0(DuckDB 2.x)、JSON::Fast、Terminal::UI 0.1.14(仅 TUI)。

命令

ducketl run <pipeline.json> [--db PATH] [--history PATH]
                            [--threads N] [--memory-limit SIZE]
                            [--keep-temp] [--json] [--quiet]
ducketl tui [pipeline.json] [--db PATH] [--history PATH]
                            [--threads N] [--memory-limit SIZE] [--isolated]
ducketl worker <pipeline.json> [--db PATH] [--history PATH]
                            [--threads N] [--memory-limit SIZE] [--keep-temp]
ducketl extensions [--json]
ducketl validate <pipeline.json>
ducketl list [--history PATH] [--limit N] [--json]
ducketl show <run-id> [--history PATH] [--json]
ducketl version

worker 是 tui --isolated 使用的子进程入口:stdout 每行一个 JSON 事件,stdin 收到 cancel 时取消当前运行。

Pipeline 格式

canonical 格式是 JSON。路径相对 pipeline 文件所在目录解析。

{
  "name": "nightly_orders",
  "database": "../out/ducketl.duckdb",
  "defaults": { "threads": 4, "memory_limit": "1GB" },
  "steps": [
    {
      "id": "extract",
      "type": "source",
      "format": "csv",
      "path": "../data/orders.csv",
      "options": { "header": true, "delim": ",", "sample_size": 100000 }
    },
    {
      "id": "clean",
      "type": "sql",
      "sql": "select id, cast(amount as decimal(18,2)) as amount from extract where amount is not null"
    },
    {
      "id": "check",
      "type": "assert",
      "sql": "select count(*) from clean where amount < 0",
      "expect": 0
    },
    {
      "id": "load",
      "type": "sink",
      "target": "mart.orders",
      "mode": "upsert",
      "key": ["id"]
    }
  ]
}

步骤类型

类型作用关键字段
sourceCSV / Parquet / JSON / 表 / SQLformat、path、table、sql、options
sql任意 SELECTsql、materialize
assert数据质量断言sql、expect、op(eq/ne/lt/lte/gt/gte)
sink表 / Parquet / CSV / 分区 Parquet 目录target、mode(replace/append/upsert)、key、watermark、partition_by

增量水位、分区写与扩展

{
  "name": "incremental",
  "extensions": ["parquet"],
  "steps": [
    { "id": "extract", "type": "source", "format": "csv", "path": "raw/events.csv" },
    { "id": "load", "type": "sink", "target": "mart.events",
      "mode": "append", "watermark": "created_at" }
  ]
}

TUI(M1 + M2 + M3)

ducketl tui 打开工作台。最小 50×10;宽度 ≥110 时显示左侧导航,≥80 时显示右侧运行面板,窗口缩放会重建布局。M1 为只读浏览与运行,M2 增加 Pipeline 屏的编辑闭环,M3 增加 --isolated worker 模式。

屏内容
Dashboard最近 20 次运行(来自 history);Enter 查看步骤、行数与错误
Sources当前数据库 catalog,加上当前 pipeline 的 source 步骤;Enter/p 预览(LIMIT 100)
Pipeline可编辑草稿:步骤表 + 字段表单;n 新建、Enter 打开文件、a 加步骤、s 保存
Run步骤状态、行数、耗时、日志;Enter 查看单步 SQL/错误,Esc 返回
键作用
1 2 3 4Dashboard / Sources / Run / Pipeline
Tab / Shift-Tab在导航、内容、详情面板间切换
j k / ↑ ↓移动选择;表单中移动字段
Enter激活选中项;Pipeline 中编辑当前字段(枚举/布尔值直接切换)
a / d追加步骤 / 删除选中步骤(二次确认)
e用 $VISUAL / $EDITOR 编辑选中步骤的 SQL,返回后恢复界面
s保存草稿;首次保存会提示路径(默认 <cwd>/pipelines/<name>.json)
o / n打开选中 pipeline 文件 / 新建草稿(有未保存改动时确认)
r / F2运行当前 pipeline;在 Pipeline 屏运行草稿(无需先保存)
c / F3取消当前运行
p预览 Sources 中选中的表或 source
[ ]切换 pipeline(同时刷新 catalog)
R重新加载 pipeline、catalog、history
q退出;有运行中的任务时先取消并等待回滚,有未保存草稿时确认

编辑草稿:

线程与连接:

隔离模式(ducketl tui --isolated):

运行语义

当前边界

目录

bin/ducketl                  CLI 入口
lib/App/DuckETL/Pipeline     模型、校验、步骤引用检查
lib/App/DuckETL/SQL          quote/literal、步骤引用扫描与重写
lib/App/DuckETL/Planner      步骤 -> SQL(纯函数)
lib/App/DuckETL/Duck         连接、catalog、schema、预览
lib/App/DuckETL/Executor     事务、事件、取消点、水位、分区安装、失败清理
lib/App/DuckETL/Run          后台运行、事件队列、取消
lib/App/DuckETL/IsolatedRun  子进程 worker、JSONL 事件、取消/崩溃处理
lib/App/DuckETL/Store        history / watermarks(线程安全)
lib/App/DuckETL/CLI          run / tui / validate / list / show
lib/App/DuckETL/TUI          布局、四屏状态机、键位、表单与草稿
lib/App/DuckETL/TUI/Draft    草稿字段/枚举/序列化/校验辅助
lib/App/DuckETL/TUI/Render   纯渲染:宽度感知表格、状态行
examples/                    可直接运行的 pipeline 与样例数据
t/                           单元 + 集成测试

测试

prove6 -l t

覆盖:SQL 引用扫描(函数/字符串/注释/限定名)、pipeline 校验、Planner golden SQL、CSV→Parquet、upsert 幂等、失败回滚、文件原子性、CLI、TUI 纯渲染(含 CJK 列宽)、后台运行事件与取消、草稿字段/枚举/序列化与保存后往返、增量水位幂等、分区目录替换、扩展装载、隔离 worker 事件/取消/崩溃。

本机验收: