前情提要
TiddlyRAG Dev Log - MWS API | 工程屍 FlyPie 的異想世界
在前一篇文章中,我提到了 MWS (MultiWikiServer) 時做了一個名為 Recipe-Bag 多對多模型,用來展現類似於 OverlayFS 的行為。
但是我依然不能百分之百確定這個模型的設計哲學以及正確的行為是什麼,舉例來說:
- 對 Recipe 名下的某個 Tiddler 寫入時,是永遠寫到 Bag 的最上層嗎?這是不是代表每個 Recipe 都至少包含一個 Bag 作為 Overlay?
- Recipe-Bag 模型是從哪裡來的?是如我直覺的啟發自 OverlayFS 還是其實長得很像但是細節不太一樣?
翻譯 ETL
於是我計畫建構一個 ETL (Extract, Transform, and Load) 來處理這個問題:透過 GitHub API 拉出 Issue 和 Comment (討論紀錄)、進行翻譯、最後輸出成方便我閱讀的 Markdown。
除了表面的目的以外還有其他考量:最近我在 104 看職缺的時候,發現一些關於 ETL 的職位會提到 Perfect 這類工具,於是我想這是一個嘗試新東西的機會。
雖然我也可以嘗試透過最近完成的 EC-BT (Entity Component Behavior Tree) 進行資料探勘,但是我依然需要對標準的 ETL Pipeline 有基本的認識才能避免重複發明輪子。
一開始我是試著搭建 Prefect 的環境,不過過程中我看到了這個:

這讓我的開源雷達亮紅燈。加上它的文件不是很清楚,我沒辦法一下子搞清楚誰是 Client?誰是 Server?誰是 Workder?
另外一點,這是 Luigi 的官方範例1:
class AggregateArtists(luigi.Task):
date_interval = luigi.DateIntervalParameter()
def output(self):
return luigi.LocalTarget("data/artist_streams_%s.tsv" % self.date_interval)
def requires(self):
return [Streams(date) for date in self.date_interval]
def run(self):
artist_count = defaultdict(int)
for input in self.input():
with input.open('r') as in_file:
for line in in_file:
timestamp, artist, track = line.strip().split()
artist_count[artist] += 1
with self.output().open('w') as out_file:
for artist, count in artist_count.iteritems():
print(artist, count, file=out_file)
它顯式的表明了一個 Task 的輸入與輸出,比較符合我對 ETL 的想像,畢竟是 ETL (Extract, Transform, and Load) 對吧?
反之 Prefect 的官方範例則是長這樣:
from prefect import flow, task
import httpx
@task(log_prints=True)
def get_stars(repo: str):
url = f"https://api.github.com/repos/{repo}"
count = httpx.get(url).json()["stargazers_count"]
print(f"{repo} has {count} stars!")
@flow(name="GitHub Stars")
def github_stars(repos: list[str]):
for repo in repos:
get_stars(repo)
# run the flow!
if __name__ == "__main__":
github_stars(["PrefectHQ/prefect"])
好,你定義了一個像是 Pipeline 的東西,但是你的 Load 勒?
另外,最近學習 NestJS 和 FastAPI 的經驗讓我對於 Prefect 的語法有一點防禦心理。FastAPI 高度仰賴 Dep 注入函式的方式建構,副作用就是專案的組織充滿可能性,造成程式碼很雜亂,反而是 NestJS 基於物件的結構直接隱式的決定了程式碼的位置以及如何在專案中組織。
看起來好像有點用... 但老實說,我覺得用一個簡單的 Makefile 就能搞定所有這些東西了。用這種工具的好處是什麼啊?
路易吉的確是受到 GNU make 的啟發(文章裡不同地方有提到),而且文章裡的微型範例並沒有展現它所有的功能。它跟 Spark 和 Hadoop 這樣的框架配合得很好,它支援開箱即用的不同資料儲存目標,你有錯誤報告、依賴關係圖視覺化等等。 2
這個算是我嘗試 Luigi 的原因之一,CMake 或 Bazel 這類本地建置工具長什麼樣子我心裡有底,所以我需要的是一個光譜在 CI/CD 或本地自動化工具以外的 ETL/Data Orchestration 工具來了解領域模型。
貼文有 11 年老,但是 Luigi 看起來還有在維護,UI 以現代眼光來看也不會太差。初試 Prefect 無果之後我便嘗試 Luigi,

然後沒遇到什麼問題就接著實作,最後把我的需求解決掉了。