iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
佛心分享-SideProject30

打造 APR Engineer 的生產力平台,從 Flow Tracer 到 SignOff DashBoard 的落地實戰系列 第 10

【Day 10】 Flow 可視化(Visualization):畫出連菜鳥都能一眼看懂的流程圖

  • 分享至 

  • xImage
  •  

前言

昨天把時間軸講完了,今天換一件更俗、也更關鍵的事——要有圖要讓人看得懂 R

通常資深的 APR 工程師心裡會有一張 flow 的流程圖,但對生為菜鳥而言的我眼前卻只有一坨 script

WinFlow 把 Generator 跟 Runner 都做成「同一張圖的兩種讀法」:一邊拿來改依賴,一邊拿來看狀態。畫布不是裝飾,是 flow.json 的投影。

好的流程圖要讓沒寫過這條 flow 的人,十秒內知道:誰等誰、誰正在跑、點進去能看到什麼。

本篇焦點放在 DAG 怎麼從 parents / children 長到 Tkinter canvas 上。GUI 的按鈕列會帶到,但不展開成操作手冊。


兩個分頁,同一顆畫圖引擎

winflow_gui 的視窗裡有兩個分頁:

+------------------+     Sync from Generator      +------------------+
| Generator        |  ------------------------->  | Runner           |
| 編輯 DAG         |         flow.json            | 監看 DAG         |
| 問:誰該等誰?    |                              | 問:現在跑到哪?  |
+------------------+                              +------------------+
              \                                      /
               \                                    /
                +----------------------------------+
                |  winflow/graph.py                |
                |  build_relation_edges            |
                |  compute_layers                  |
                +----------------------------------+

Generator 的 FlowEditorCanvas 與 Runner 的 FlowGraphCanvas 都吃同一套作法。差在投影:

Generator Runner
問題 依賴對不對、能不能改 現在哪個 job 在 PEND / RUN / DONE
節點顏色 選取關係(自己、父、子) LSF 狀態
正交折線,中間可標檔名 直線箭頭,不標檔名
位置 可拖、可 Auto Layout,寫進 document 每次 redraw 依層重排,不能拖
點一下 Highlight 父與子;連線模式用來 Link / Unlink job 開 Job 詳情:I/O、時間、單跑 / 停 / 驗檔

讓「菜鳥一眼看懂」不是靠兩套不同的圖,而是同一張 DAG,用顏色回答當下那個分頁在問的問題。

線從哪來:畫布不發明依賴

共享模組 winflow/graph.py 裡,真正拿來畫的 functionbuild_relation_edges()

  1. 每個 job 讀自己的 parents(存的永遠是 stage/task/job 這種 slash key)
  2. 為每一對 parent → child 畫一條邊
  3. 邊的標籤另作處理:若 child 的某個 input 剛好等於 parent 的某個 output,標籤用那個檔名;否則標成 (parent),表示這是純關係邊、沒有共享檔

JSON 裡的 children 是互逆欄位,用來給 Runner 解鎖下游,不另外再長一套邊。畫的時候認 parents 就夠,避免同一對節點被畫兩次。

舊檔若缺 parents / childrenensure_job_relations() 會在載入進畫布之前用 task 順序 + 檔案路徑補一次。補完之後,畫布就只認屬性了。Generator 上 Unlink 掉的關係,不會因為「看起來同一 task 前後相鄰」又自己長回來

Generator 內部的 job key 用 NUL 字元隔開(stage\0task\0job),避免 job 名稱裡剛好也有 /。寫進 flow.json 時再轉回 slash。我們不必管這個;只要記得:畫面上的一條箭頭,對應 JSON 裡的一組 parents/children,不是對應目錄排列。

層怎麼算:左邊是入口,右邊是越等越深的人

有依賴關係之後要決定節點站在哪一列。兩邊共用 compute_layers(),做法是拓樸分層:

1. 每個節點 indegree = 有幾條邊指進來
2. indegree = 0 的放進 queue,layer = 0(畫面上寫 inputs)
3. 每處理完一個點,把它的下游 indegree 減一
4. 下游的層號 = max(自己目前的層, 上游層 + 1)
5. 減到 0 的人進入下一輪

結果很直觀:

layer 0          layer 1           layer 2
(inputs)         (step 1)          (step 2)

IT_1 ----+-----> IT_3 ----+-----> IT_4 -----> IT_5
         |                |
IT_2_0 --+                |
IT_2_1 --+----------------+
IT_2_2 --+

同一層的人沒有互相等待,理論上可以並行。這就是為什麼 QC 會跟下一主站並排,而不是畫在主站正下方當「下一個 stage」。層是依賴深度,不是公司組織圖

若圖裡不小心有連成 circle 的話,就會造成某些點的 indegree 永遠減不完。Generator 的 Link 因此會先跑 would_create_cycle():從「即將當 child 的那顆」沿 children 往下走,若走得到「即將當 parent 的那顆」,這條連線就會直接被拒絕。可視化建立在「這張圖是 DAG」這個前提上;環一出現,層與排程都會一起壞。

Auto Layout 的功能只是把層號變成座標。Generator 的 layout_by_graph()

  • x = 100 + 層號 × 240
  • 同一層裡,y 依「目前畫布上的上下順序」排,間距 100
  • 節點中心點寫進 document.positions

Runner 更乾脆:每次 redraw 都重算,x 間距 80、y 間距 28,使用者不能拖這些 job node
Generator 才會提供你這個功能,好讓你思索怎麼串接自己的 flow


Generator:把「依賴」畫成可編輯的產物

https://ithelp.ithome.com.tw/upload/images/20260814/20127932MO69A0a5Qi.jpg

打開 Generator,右邊是 Flow canvas,左邊是 Flow settings 與 job 清單。工具列上跟圖最有關的是:

  • Load / Open:範本或現成 flow.json 進畫布
  • Add Job / Delete:增減節點
  • Link -> / Unlink:改依賴關係
  • Auto Layout:依層重排
  • Export flow.json:把結果寫出去,給 Runner 用

提示列寫著:Drag to rearrange | Double-click to edit | Esc cancels link。拖動只改座標,不會改 job 在 JSON 陣列裡的順序,也不會改依賴。這是刻意的:排版是給人看的。

Generator 上的 job 節點有什麼

固定會三行:

+------------------------------+
| |  FLOOR_PLAN                |   ← job name(太長會截成 ...)
| |  FLOOR_PLAN | normal       |   ← task | queue | 可選 -m host
| |  1 CPU  |  ^0  v2          |   ← CPU,以及幾個父、幾個子
+------------------------------+

^0 v2 讀作:沒有父親、兩個孩子。沒有任何邊的孤立點則顯示 no deps。滑鼠移上去會出 tooltip,補上完整的 stage / task、machine、以及 command——畫布不把指令印在卡片上,否則十個 job 就會變成十張便利貼小說。

顏色不表示跑到哪(Generator 還沒送 LSF),而表示你現在在看誰的親戚

角色 填色 左邊色帶 意思
普通 沒被選到
選中 淡藍 藍、邊框加粗 你點的這顆
它的 parent 淡綠 它在等的人
它的 child 淡黃 琥珀 等它的人
Link 起點 淡橘 連線模式的第一下

PLACE,FloorPlan 變綠、Q_PLACECTS 變黃——菜鳥不必去數 JSON 裡的 key,一眼就知道這顆的上下游。沒被選到的邊會淡掉,選中相關的邊變藍、加粗

邊怎麼畫、標籤寫什麼

邊是正交折線,不是斜線:

[ IT_1 ]——┐
          │
          └──────►[ IT_3 ]
              IT_1.done

從父節點右緣中點出發,水平走到兩點中線,垂直對齊子節點,再水平進子節點左緣,箭頭在終點。折線中間會寫標籤,超過 22 字截斷。標籤通常是共享的檔名(example_flow/out/IT_1.done);若只是 Link 出來、沒有檔案依賴,就會看到 (parent)

這讓「圖上的邊」跟「磁碟上的檔」可以同時存在、又不必混成一件事。QC 吃的 .done,邊上會寫那個路徑;你若只想說「這個 Summary 要等四個 QC」,但沒有檔按依賴,這個關係仍然在,只是標籤就是 parent

背景還有兩層給人定位、不參與排程的東西:

  • Stage band:依同一 stage 裡節點的 x 範圍,鋪一條直的淺底,頂上寫 stage 名。它是分組色帶。IT_1 跟 IT_2 若層號相同,可能站在同一直欄,但屬於不同 band。
  • Layer 標籤:層 0 寫 inputs,其後寫 step 1step 2。告訴你「這一列是入口還是已經等過一輪」。

Link / Unlink 就是在改這張圖

Link ->,游標變十字。第一下選父(橘),第二下選子。link_jobs() 會:

  1. 拒絕產生 circle
  2. 寫互逆的 parents / children
  3. 若父沒有任何 output,補一個 dummy 路徑,並盡量加進子的 inputs,讓「檔案視角」跟「關係視角」暫時一致

Unlink 反向清邊,並盡量拿掉不再被引用的 dummy。畫布立刻 redraw()。你在 GUI 上掰開的那條線,就是之後 Export 進 Runner 的那條線。

Runner:把「狀態」畫成會呼吸的樣子

https://ithelp.ithome.com.tw/upload/images/20260814/20127932747Gek1Ct0.jpg

來看一下我們的 ithome flow

切到 Runner,或按 Sync from Generator(僅在沒有 job 在跑、也沒有正在 kill 時可按)。畫布換成監看模式:節點變小(130×58),不能拖,每次重畫都依層站好。

左邊控制列是 Run Flow / Rerun / Stop / Reset;中間偏上是 DAG,偏下是 log。DAG 回答「現在誰在動」,log 回答「那個人嘴裡在喊什麼」。本篇只盯 DAG。

一顆節點上有什麼

+------------------+
|      IT_1        |   ← 名稱;過長會在 _ 處折行,再不行就縮小字再截斷
|      PEND        |   ← 狀態
+------------------+

卡片底色就是狀態。這是 Runner 可視化的全部賭注:顏色 = 生命週期

狀態 顏色 菜鳥該讀成
waiting / pending 還沒輪到;父還沒全 DONE
PEND 淺天藍,深色字 bsub,在 LSF 排隊
RUN 藍,白字 機器上正在跑
DONE LSF 說完,且 outputs 都在
EXIT / failed 掛了,或缺檔被判失敗
KILLING Stop 已送 bkill,還在確認
UNKNOWN 灰字 bjobs 回了認不得的字 (SSUSP 這類的狀態碼)

PEND 特別用淺底深字,避免一整排排隊 job 變成刺眼色塊。其餘有顏色的狀態用白字,讓 DONE 的綠、EXIT 的紅在十公尺外也分得出來。

活著的節點會有心跳

PENDRUNKILLING 會被放進 active_keys。畫布每 600ms 在節點外再畫一圈橘紅(#fd8c73),像是心跳那樣。並行時,同一層可以同時有好幾顆在閃——那不是動畫炫技,是在說:這些 job 已經通過依賴檢查,正在跟 LSF 說話。

DONE / EXIT 會從 active 集合拿掉,心跳停。Reset Flow 把所有節點打回 waiting,並清掉 lsf_name、job_id、起迄時間;它不 bkill。Stop 才是殺 cluster 上的作業,對應的節點會先變紫再變紅。

邊在 Runner 上較克制:父右緣到子左緣的直線箭頭,灰色,沒有檔名。監看時你要的是「誰解鎖誰」,不是再讀一次路徑。檔名改去點節點。

頂部仍有 stage 斜體小字,對齊該 stage 第一個出現的層。它只是名牌。QC 若跟下一主站同層,stage 標籤可能重疊在同一直欄——這時候請相信節點顏色與邊,不要相信標籤的左右順序。

點進去:內容不擠在方塊上

點節點會開 JobDetailDialog,這才是「內容」的主場。畫布只留名字與狀態,詳情視窗分三塊:

  • Job Input / Output:路徑清單。空的寫 (none)
  • Timing:Status、LSF Job 名稱、Start / End、Run Time
  • 動作:Run Job、Stop Job、Validate Job

時間從 Runner 收到的 callback 記入。job_start / job_submitted 寫 start;job_done / job_failed 寫 end。畫布上的綠與紅,背後是這段時間戳記。

Validate 不送 LSF,只問 outputs 是否已在磁碟;都在就把這顆標成 DONE。這是在「檔案已經生出來、但 GUI 還停在 wait」的作弊法 (實務上很常會遇到),不是另一套排程。Run Job 則是單點重送,整條 flow 在跑或正在 kill 時會禁用,避免跟 DAG 排程搶人。

狀態怎麼從 LSF 流到 GUI 上的呢 ?

FlowRunner.run_job
    → job_callback(job_start / submitted / status / done / failed)
        → GUI 執行緒 _handle_job_event
            → node.status / lsf_name / job_id / 時間
            → graph_canvas.update_job(...)
                → 改色、更新 active_keys、redraw

job_status 帶的是 bjobs 的 PEND / RUN / DONE / EXIT。GUI 不自己猜。輪詢時只問 LSF,畫布只負責翻譯成顏色。

再複習一下怎麼看這兩張圖

在 Generator

  1. 先看最左一列 inputs:沒有箭頭指進來的人,是可以一起開工的 Root
  2. 點一顆有疑問的節點:綠的是它在等的,黃的是等它的
  3. 看邊上的字:有檔名表示還對得上產物;只有 (parent) 表示純順序
  4. ^n vm:數字跟你心裡的上下游人數不一樣,就去查 Link 是不是多點或少點
  5. 不要被 stage 色帶騙:色帶是分組,層才是時間深度

在 Runner

  1. 灰的大片 = 還在等前面的 job
  2. 淺藍 PEND = 已進 queue;深藍 RUN 且外圈在閃 = 真的在跑
  3. 同一層同時閃兩顆以上 = 並行中
  4. 綠了才會解鎖右邊;紅了右邊會一直 wait,因為父結點沒有 DONE
  5. 不確定為何是紅,點進去看 outputs 與 log,不要猜

APR + QC 那張經典圖,用這套讀法會變成:

FLOOR_PLAN (layer 0)
    ├─► Q_FLOOR_PLAN (layer 1)     ← 旁掛,不擋住 Place 右邊的人
    └─► PLACE      (layer 1)
            ├─► Q_PLACE (layer 2)
            └─► CTS     (layer 2)

QC 跟下一主站站在同一層,所以顏色會一起從灰變藍。 若你公司規定 QC 必須擋住主鏈,Generator 裡就該把 QC 接到下一主站的 parents;層數會加一,Runner 上也會變成「主 flow 先一直灰,直到 QC 變綠」。

兩張圖為什麼不能合成一張

有人會問:為什麼不在 Generator 上也畫狀態,或在 Runner 上也能拖節點、改邊?

因為它們回答的時間點不同。編輯時改邊,執行中的狀態就作廢;執行中拖節點,只會讓「誰在跑」跟「誰看起來站在誰旁邊」脫鉤。WinFlow 選擇:

  • Generator 的圖是可寫的依賴
  • Runner 的圖是唯讀的生命週期
  • 中間用 Sync / Export 的 flow.json 換手,而且換手當下 Runner 必須閒置

小結

  • Winflow 兩邊 GUI 共用 build_relation_edgescompute_layers:邊來自 parents,層來自 flow 深度
  • Generator 用選取色(藍/綠/黃)講親戚,卡片上有 CPU 與 ^父 v子,邊上可標檔名
  • 拖動只改排版;Auto Layout 把層變成座標
  • Runner 用狀態色講生命週期,活著的節點會有心跳;內容放進點擊後的詳情窗
  • 同一層 = 可並行;Fail 的父節點會讓其子節點一直 wait

上一篇
【Day 09 】 Flow Tracer 架構哲學:什麼時間點該做什麼事,自己要清楚
下一篇
【Day 11 】 智慧 Log 追蹤與管理:一個一個開 Log 來看也未免太累
系列文
打造 APR Engineer 的生產力平台,從 Flow Tracer 到 SignOff DashBoard 的落地實戰12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言