[請益] 如何有效益的維護data loader
如題
目前做的project架構長這樣
Loader1 Loader2 Loader3 ........... Loader30
Area 1
Area 2
Area 3
....
....
Area 10
就是同一個Loader 會算出結果給不同Area的人
有分成前端跟後端,前後端都是我維護 (前端 .Net Framework java script MVC,會
用到一點 jquery,其他大多套套件用現成的物件再加工,後端就純C#
query DB用的工具是公版的不用自己寫,就負責input 要打的SQL跟DB,然
後output datatable或是對DB做insert/delete/update....)
Loader每支老闆認知都不算很大所以也是一人包辦XD
Loader每支的邏輯也類似:
Start--->會記錄log表示loader開始
Initial setting----> query一些接下來SQL會用到的config
QueryRawData---->把上一個initial function的config帶下來在這裡組成SQL string
並且query進data table
ProcessData--->對上一步的data 做運算然後轉換成可能只有一個kpi值的結果
或是pre-sum過後的結果 (舉例來說query hourly的資料轉換成daily的資料)
End---->只是記錄log表示上面的function都執行完了
看似很穩定
但loader卻需要不斷的更版
原因是因為SQL全部都是寫死的,接水其他系統的資料
以這個圖為例,USER只看的到最終的TABLE,沒有太多空間可以彈性修改query的條件
也沒有像左上角有彈性調整query資料的方法
(有config但是很少)
所以需求常常長得像這樣:
"tinasfishs 啊你幫我改一下,我發現今天的KPI有錯,因為
今天開始要加採計'AA' 這種類別,麻煩再幫我改"
原始SQL通常長這樣
SELECT COUNT(1) CNT FROM STORE_SALES
WHERE 1=1
SELECT * FROM STOR_SALES
WHERE 1=1
(
AND TYPE IN ('A','AB','BC',....'DZ','AA')
AND SALE_DATE BETWEEN SYSDATE-3/24 AND SYSDATE-1/24
AND MT_CODE IN ('2','5')
AND......
CASE WHEN ....
)A,
(
SELECT EMPLOYEE_DEG,EMPLOYEE_NAME FROM EMPLOYE_L
WHERE AREA='AREA1'
AND RES_PRODUCT NOT LIKE '%AAZc'
....
....
...
)B,
(
SELECT EMPLOYE_ID,EMPLOYEE_SHIFT FROM SHIFT_ARRANGE_LIST
WHERE RECTIME>SYSDATE-5 AND AREA='AREA1'
if (AREA=='AREA1' or AREA=='AREA2')
{
RECTIME<SYSDATE-1
}
)C
WHERE A.SALE_EMPLOYEE=B.EMPLOYEE_NAME
AND B.EMPLOYEE_ID=C. EMPLOYEE_ID
SQL通常都很長而且用sub-query,會把商業邏輯埋在hard-code的SQL裡面
不能用SQL處理的邏輯則是在ProcessData那步處理
假設綠色的地方是config,黃色的地方則是這次修改的部分
重點是改後端的SQL都需要重新上code,雖然有提供公版的工具把SQL另外儲存
只要呼叫那個外部工具就可以把SQL取得
但前人為了開發方便
當然全部都把SQL直接擺在程式裡
然後複雜的是,同樣算這個KPI,各個AREA 的條件是不盡相同的
用藍色字的地方就是舉例,沒有使用config而是用C# 的code 直接hard code 這兩個
area 需要加此條件
所以也會衍生像是
"我們是AREA3,AREA1 跟 AREA2 看的那個條件我們從下禮拜開始也想跟著採計
,你們可以重新上code嗎"
有爬文像是Stored procedure 之類的做法,歹勢對於這個架構完全不適用
理論上像這樣各個area的條件有不少差異的情況下,應該是個別維護各area的loader
但這整套是寫在一起的...,每次上code為了維護版控也會在各area同步上同一套
也就會發生改A壞B,假設area1跟area2 共用同一段SQL條件,答應area1刪掉
其中一句之後,area2 就跑來說他們的KPI異常
這個專案負責接水30幾支上游的TABLE,就有30幾支LOADER..而且還在逐漸增加中
所以談需求跟開發的時候,根本也不是從
"為什麼要新增AA這個條件,除了改SQL有什麼更好的方式" 去切入
因為那要去詢問上游以及user為什麼要新增這個條件太花時間
而是 "幫我改SQL吧比較快"---> 所以從來都只在做改SQL這種很末端的工作
一個禮拜大概有4-5個這樣的"假需求"---->我定義為只是改SQL,請user驗驗看是否
符合他們期望
user有時候也不確定改了條件是否就會對,改了也不能上code
總是改了再測測了再改
這樣的需求都做完了,才有時間去處理可能真的是loader本身的問題
----->效能的問題啊、要新增一支新loader 與user談規格之類的....orz
談規格也是我一人,頂多再加0.5個PM
PM也跟我想像會把規格談到不會讓日後充滿這些維護 剛好相反
最後規格談一談還是常常訴諸短解
能夠hard code SQL就hard code,理想性的應該要跟上游負責系統的人看資料
怎麼接軌的溝通過程完全沒有= =
我有堅持過想先看清資料流再做新需求也因為求時效性被打槍
有被訓練得比較有警覺性,但一個SQL 至少20行,下面又有條件讓user高興改就改
實在很難用很大的框架去處理這樣的事,SQL有記得拔出來放在另外存的地方
不用重新上code就很不錯了
BTW SQL邏輯跟要接水什麼報表通常都是由USER提供
-----------
有跟老闆溝通過像這樣接水別人資料的匯總型AP 應該要重構,或者是一支一支
拿出來看有什麼可以enhance的
但user常常會認知KPI錯誤就來說是系統問題,久而久之老闆也好像覺得是我的問題
而沒有深究之所以KPI會錯誤,就是因為SQL在上code流程的不彈性,還有讓user在
不能彈性重上CODE的情況下還是一再想修改SQL
(不能彈性的原因是要重上code老闆都會看,跟他解釋這個是要改SQL就會被
question,但老闆也不看CODE也不會負責建議有建設性的方法,只會打高空問
說可不可以不要改SQL--->問我的格局只能做到這樣...整個黑人問號)
私以為想修改SQL或是想改變KPI的算法,都應該是大家坐下來看data flow是什麼
是不是只是加了一句SQL就可以改變
但文化上大家也不太有耐心這樣梳理問題
不太清楚老闆是否了解目前系統的設計是長這樣,總之在溝通上也覺得不太有救
想問問有沒有維護或是開發過這種DATA LOADER的前輩 可以分享一下心得的QQ
--
我看到1=1了耶...
也不要用*
把條件弄成dynamic 不要hardcoded 設定好權限 讓個別
user可以選要加甚麼條件在filter裡 我做過這個 很難寫
難在一堆無聊人士搞audit 設定權限他們也不懂
辛苦啦,我是建議你讓sql參數化單純一點,把條件的東西搬去
c#用策略模式做
然後多使用虛擬類別,多的kpi用介面去加
這樣你sql就相對單純
現在這樣筆數多撈起來會很慢吧...
然後你sql要板控的話可以加一個sql,專案在方案裡
最終還是sp化接參數再透過策略選擇呼叫那一種就好
好維護,改起來也快,而且會好閱讀很多吧
1=1是為了 後面串條件方便吧 應該是後面可能不會出現
條件
不太懂分那麼多支loader 的原因為何?
先把上游資料轉成同樣格式,query 的部分再跟著 area 版控
現在這樣維護成本就是 loader 與 area 數量乘開
把東西拆到讓維護成本變成加法是唯一的活路
Game Live patch:做多種MOD讓使用者自行訂閱
Loader1.GetSQLText(Area1), Loader2.GetSQLText(Area1)
query可以獨立分開放在檔案裡讀吧?
JavaScript 為什麼你要分開寫
你做好一版 馬上把所有可能被影響的loader/area重跑 比
對實際被影響的資料是否符合你預期 先建立這樣的系統避免
改A壞B沒發現 再來談重構
會發生改a壞b表示你有共用的code卻允許共用的其中一個需
求端任意更改 這當然不正常, 如果一種東西會被多個需求
端各自提需求修改 那這種東西不論有多相似 都應該建立單
獨的class
你的整個重構會很複雜不是你一人一兩個月就能完成 一定要
先建立確保不會改A壞B的機制
你這正常的架構就是要先定義原始資料類別有哪些, 聚合資
料的filter有哪些
你的loader程式應該要給每個最終報表有適當的命名 同時
有文件紀錄每個報表會有哪些單位查看
每一支報表由哪些資料源以及哪些filter算出中間的結果以
及最終報表 要有文件
當有人跟你說 他要增加採計AA類別 你就去查程式 採計AA
類別這個行為是哪一支filter在處理, 有其他單位用到嗎?
如果沒有 那大概就是直接修改這支filter 最終報表就會是
他要的結果 也不影響別的單位. 如果有其他單位用到 你可
能需要拆分成兩支filter給兩個單位使用 或是你的這個filt
er可以吃參數 那就是兩個area吃不同config即可. 最重要是
不管你的架構如何 改完就是要測試確保沒有動到不該動的
東西 這就是測試的覆蓋率的重要性
最後建議 直接換工作
不太懂 老闆聽你這樣解釋之後還是認為是你的問題嗎
通常這樣看你敘述下來就知道是架構問題了吧?
SQL WHERE 用 python 的 f-string 把 list 傳進去,這樣
只要更新這個list就好
像是水流一樣,資料一站一站往下流,就很好追蹤!可能會
變動的地方可以另開TABLE存放參考物件AA ,BB...後續就
算新增參考,也只需更動參考TABLE內容,MAIN SQL部分只
要寫好關聯,後續就不用一直改
已經有參數化,loader唯一有設計的就是把同一支loade
r的可能10幾、20幾支SQL擺在同一個class,然後用retu
rn string的方式去return SQL,只是除了用class的殼
包住&部分用參數之外就沒做其他的了。參數有的帶了6-
7個結果還是沒辦法把SQL拆到自由度完全釋放
我的確很想by Area去把SQL list拆出來,但是可能和其
他公司不同,SQL除了擺在公司共用的SQL池以外就是只
有hard code一法,SQL池只是儲存string跟參數位置,
假設要把本來是hard code的條件改成帶參數,就是原本
C#裡面call SQL池的地方要改。例如string SQL=sql po
ol(type,gender)要改成SQL=sqlpool(type,gender,age)
就要重新上code,不能自行維護一份txt檔就好
感覺前人在這樣的infra環境下,選擇hard code或是擺S
QL pool需要的effort是差不多的,為了快速完成,也不
會去選擇把TABLE裡每個欄位都拆出來寫成class…table
跟table我覺得關聯性很差,常常出現10幾個column的ta
ble,我資料庫不強不知道這算不算是架構不好的象徵QQ
但大家的建議都很棒,我會多尋找機會點的!謝謝
sql = "" +
(age != null ? "" +
"\n and age = @age" +
"" : "") +
"";
藍色的if可以寫成上面哪樣, 字串一直串
不想整個Code重上, 可以拆成不同project (dll)
若你將所有C# Code都寫在一個cs檔,就可以實現維護一份text
參數太多可以考慮用一個物件來放
(type, class or Dictionary<string, object> args)
c# call sqlpool interface 改成傳 object 就可以解決改
來改去的問題?
樓上是講我嗎? "實現維護一份text"那一行只是玩笑話
不太懂樓上的意思
另外花點時間寫一支自己理解後的模組?
然後兩邊比較結果,久了沒問題就能把舊的取代
如果你會很常異動邏輯,通常會把核心做成view有需要再篩選
吧,這樣就從資料庫改就好,子查詢也可以做成function放在
資料庫
這個架構也太神奇了吧?