Day 18 提到的 BI 動態看板,是讓客戶查看自家門市營運數據的地方:今天來了幾台車、平均等多久、哪個時段最壅塞。
一開始這件事完全由我們自己開發。後端提供一組指標 API,以 Python 計算數字,前端拿到數字再自行繪製圖表。這個做法單純,也完全可控:需要新的指標就加欄位,圖表要怎麼呈現就怎麼實作。
每家餐飲品牌的營運重點不同,在意的指標與閱讀數據的習慣也就不同。每談成一個新客戶,往往就伴隨一批只屬於這家客戶的需求。
這些需求本身都合理。但在原本的做法下,每一項都是一輪開發:修改 API、撰寫新的計算、前端加上圖表、發布一次新版本。
結果是,業務端每多一個新客戶,工程排程就多一份壓力。客戶數量乘上客製需求,開發人力無法跟上。
我們的目標是讓「要看什麼」這件事,不再需要工程師介入。
我們同時做了兩件事。第一,把資料處理移到 dbt (data build tool,以 SQL 定義資料轉換的工具),先把原始資料整理成一組結構固定、可以直接查詢的資料表。第二,停止自行繪製圖表,改用現成的 BI 工具呈現與查詢資料。
這兩件事互為條件。資料沒有事先整理,BI 工具接上資料庫也只能看到大量原始欄位;如果不打算換掉自行開發的圖表,也就沒有必要投入人力整理資料。dbt 的部分留到 Day 21 說明。
我們評估過幾套商用 BI,授權費用都超出當時的預算。
我們的使用情境在商用 BI 的計價方式下成本特別高。看板要嵌入 (embed) 我們的產品,提供給每一個客戶的每一位使用者,人數遠多於公司內部看報表的情境。商用 BI 多半按使用者席次計價,客戶越多、授權費越高,而增加客戶正是公司的成長方向,兩者直接衝突。
Apache Superset 是 Apache 軟體基金會的開源專案,採用 Apache License 2.0 授權,自架就沒有授權費。它也符合我們需要的三項條件:資料留在自己的環境裡、原生支援把看板嵌入其他網站、可以依使用者身分過濾查詢結果。
省下的是授權費用,代價是權限管理、部署與升級都要自行負責,後面會再回到這筆帳。
看板嵌在我們自己的產品裡。客戶不會拿到 Superset 的網址,也不需要知道背後是 Superset,登入產品後就能直接查看自己的數據,不必另外記一組帳號密碼。
Superset 為這種用法提供了 guest token,也就是一個短期有效、只能開啟指定看板的憑證。官方 Embedded SDK 的流程如下:
EMBEDDED_SUPERSET 這個 feature flag,並在看板的設定中開啟嵌入,取得這張看板的嵌入 ID。can_grant_guest_token 權限的帳號呼叫 Superset 的 POST /api/v1/security/guest_token/,在請求中指定使用者身分與可以開啟的看板。embedDashboard(),SDK 以 iframe 把看板嵌入頁面。token 預設 5 分鐘到期 (GUEST_TOKEN_JWT_EXP_SECONDS = 300),SDK 會在到期前重新呼叫前端提供的取得函式。嵌入的流程照官方 SDK 實作即可,需要自行設計的是權限。
資料隔離是整件事裡唯一不能妥協的條件:A 客戶絕對不能看到 B 客戶的資料。同一個品牌內也一樣,總部看得到全品牌的門市,加盟商只能看到自己的店。
為每個客戶各做一張看板、各準備一張資料表,無法隨客戶數量擴展。因此所有人共用同一張看板與同一組資料表,開啟看板時只查詢出這位使用者有權限看的資料列。這類需求稱為列層級安全性 (Row-Level Security, RLS)。
我們最後的做法是把權限判斷完全交給 Superset:後端在申請 guest token 時只帶上使用者身分,過濾條件預先定義在 Superset 裡,由 Superset 決定這位使用者能查到哪些資料。過濾條件可以使用 Superset 的 SQL 模板功能 (SQL templating),以 Jinja 語法的 {{ current_username() }} 在查詢中帶入目前的使用者,同一條規則就能套用到每一位使用者。
好處是權限規則從產品的程式碼移到了 Superset。以前調整誰能看到什麼需要發布一次新版本,現在修改設定即可生效。
代價是除錯變得困難。使用者回報「我的圖是空的」,原因可能落在好幾層。看板能開啟,只代表 guest token 有效;要看得到資料,還需要過濾條件正確對應到這位使用者,而且底層資料表本身有資料。
回到前面的成本問題。省下授權費之後,以下三項工作都要自行負責。
權限管理。規則移進 Superset 之後,修改規則確實不必發布新版本,但這些規則從此需要有人維護。新客戶上線要建立規則,人員異動要修改規則,出問題時要有人能排查。Superset 提供了彈性,維護的責任仍在我們身上。
部署與升級。自架就要自行追蹤版本。BI 工具的升級通常伴隨相容性問題,嵌入與 guest token 這類非核心功能尤其容易受到影響。
查詢速度。這是影響最明顯的一項。以前由後端自行計算,查詢只有固定的幾種,可以針對它們調整索引、預先計算並快取結果。改用 BI 工具之後,查詢變成動態的:使用者選擇什麼條件、切分什麼維度、查詢多長的時間範圍,都會產生不同的查詢,我們無法事先預期。同一張看板,換一組條件就可能慢上好幾倍,使用者看到的只是畫面持續載入。
查詢無法預期,正是讓使用者自行決定要看什麼的直接結果。查詢的彈性與事先最佳化的空間,兩者此消彼長。
我們把資料處理移到 dbt,同時以 Superset 取代自行繪製的圖表。看板透過 guest token 嵌入產品,客戶之間的資料隔離以 RLS 處理,交由 Superset 依使用者身分過濾。導入後,客戶想從不同角度查看自己的數字,不必再等我們排一輪開發,「要看什麼」這件事從此離開了工程排程。
如果你也在評估開源 BI,要先算清楚兩件事:省下的授權費是以自己的維護人力換來的,查詢速度也會變得較難掌握。對當時的我們來說這筆帳划算,但權限規則的維護與查詢效能,是導入之後需要持續投入的工作。
Superset 能查詢的,是 dbt 事先整理好的資料表。明天的 Day 21 回到資料這一端,說明營運指標的計算如何從 6,000 行 Python 搬進 dbt。
本系列由 Berry AI 工程團隊出品。更多工程實戰紀錄都在 Berry AI 技術部落格。