Monday, October 31, 2016

[open-falcon] Graph 裡的資料會被週期性的異常清除

公司的 open-falcon 監控系統,最近發生了一個神奇的 bug 。公司的 4000 多台機器裡,只有一台,它在 graph 資料庫裡的資料,會被週期性清空。所以無論何時去看它的資料,總是只看到最後的,例如 15 分鐘左右。而且神奇的事是: judge 又是正常的,也就是該台機器其實是有正常的送資料到 judge 和 graph 。但是,每隔一段時間, graph 就會發生異常地資料清除。

後來,我們索性用肉眼來檢查 graph 用來畫圖的資料。才發現,原來是 timestamp 的問題。出問題的機器,可能是在某個時間點,機器的時間被調到 2024 年,於是,它上報的資料,記錄的 timestamp 就是 2024 年的時間。而 open-falcon graph module 內部是使用 RRD 資料庫。於是,當 RRD 每次看到該筆錯誤資料時,它會看到一個來自未來的時間,而且會判斷之前的正確資料都太舊,可以拋棄了。這就是導致資料被異常清除的理由。

補救之道,就得針對該筆錯誤的資料做移除了。

Friday, October 28, 2016

解析 json API 輸出 - Fold knowledge into data so program logic can be stupid and robust.

最近重構了一段程式碼,用來解析 json API 的輸出。寫完之後,發覺這個重構,它為什麼可以讓程式碼變得比較容易看懂、比較好維護,因為它符合了一條 Unix 設計哲學。

Fold knowledge into data so program logic can be stupid and robust.

Data is more tractable than program logic. It follows that where you see a choice between complexity in data structures and complexity in code, choose the former. More: in evolving a design, you should actively seek ways to shift complexity from code to data.

中譯:
將知識塞進資料,好讓程式的邏輯變得單純卻強固。

資料比起程式邏輯更容易被理解。因此,當你發現可以選擇資料複雜性或是程式碼複雜性時,選擇前者。更進一步來說,在系統設計演進的過程,你應該積極地尋找方式,好讓複雜性從程式碼進入資料。


Friday, October 21, 2016

[open-falcon] 在 open-falcon 監控系統中看到 lambda-architecture 的影子


這 

個人的工作,是基於 open-falcon 發展適合公司內部使用的監控軟體,每次因為工作的需求,要在 open-falcon 上增加功能時,我就不由得開始思考,這樣子加功能好嗎?會不會不小心破壞了所謂的「概念一致性」(conceptual integrity)?那什麼是最重要的「概念」呢?哪些是可以妥協?哪些是不該妥協的?

網路上介紹 open-falcon 的文章裡,有一篇是講,編寫 open-falcon 的構思過程。我也提了,從抽象的層次看待 open-falcon ,它像是什麼。目的是希望提出抽象層次的觀點,講述其概念一致性。日後在思考增加新功能時,優先考慮不傷害概念一致性的解法。
左側兩張圖,是大數據計算常使用的 lambda-architecture 計算模型。概念是,大數據分析系統收集資料之後,將資料分成兩份,一條路是走 batch view ,另一條路是走 real time view。

其中,batch view 因為要用完整的資料做批次處理的關系,資料的處理會略有延遲。這段延遲的時間,如果還是有提供運算結果的需求,就由 real time view 的模組來提供資料。

這樣子處理大數據的模型,我彷彿也在 open-falcon 裡看到了「似曾相似的東西」。參考右圖。就是 graph 和 judge 。其中,graph 類似於 batch view 。一方面,graph 某種程度地可以保存一年的資料,也算是 immutable data。graph 的 batch view 是用 RRD tool 做的。對資料做了某些程度的篩選,特別適合用來畫圖。所以圖就是 open-falcon lambda architecture 的 batch view 或稱 precompute view 。

judge 處理 stream data ,而且會做「實時告警」,也就是一查到符合告警條件,就會立刻觸發。 judge 做的工作,是對不斷送進來的資料流做是否觸發告警條件的檢查。 judge 產生的告警,則可以視為是一種 real time view 或 incremental view 。

略有不同的地方,是大數據的 lambda architecture 其實暗示了 batch view 和 real time view 都通過 serving layer 來呈現,所以使用者不知道裡頭存在了這兩種運算模組,甚至感覺這是同樣的模組做出的運算結果。另一方面,操作 open-falcon 的使用者,其實也未必分得清楚,繪圖功能和告警功能是在兩個不同的模組獨立計算的。

Monday, October 17, 2016

網路品質量測問題 --- mean opinion score

公司的 NQM (network quality measurement) 小組,做的系統是網路品質量測系統。該系統的目標是要評估兩地的連線品質,例如:中國的某兩個相鄰省分的連線品質。測量的方式,就是在公司的主機上,送出大量 ping 封包,透過取得 ping 封包的往返時間 round trip time 和 掉包率 packet loss rate,以及往返時間的變化量 jitter 來做品質的客觀評估。

同事很快就把量測系統、資料庫、前端的介面都做好了。最後一個關卡,變成是使用者與介面的問題。於是問題出現了:「我們的軟體可以呈現這些透過 ping 量測出來的數據,但是,怎樣子的數值叫做好?怎樣子的數值叫做不好?」

一開始我的想法很單純,就做統計吧。有一整天的資料,很容易就可以算出,此時此刻的網路品質相對於一整天來講,是偏好、還是偏不好,是在一個標準差之外、還是兩個標準差之外?但是,後來想一想這個邏輯完全不通。因為使用者,也就是運維人員,他們需要的資料,並不是此時此刻網路品質相對於該兩點的一天的平均品質而言有多好。而是對於客戶來講,客戶真實的感受。比方說:客戶傳輸音訊、傳輸 http ,傳輸影片,是否會覺得不順暢?

後來,才發現這個「如何定義傳輸品質好壞」問題已經有人研究過了。有一個詞彙叫「平均意見分數」mean opinion value。也就是說,有做過實驗,讓許多不同的人在透過網路傳輸語音,讓一群人對不同的品質打出分數,於是這個 mean opinion value 就定義成了人對於聲音傳輸品質的主觀感受平均分數。MOS 的數值是從 5 分到 1 分, 5 分是完美。 1 分是無法通訊。

有了 mean opinion value (MOS) 之後,研究人員後來又發展了換算方式,可以先把量測出來的數值:round trip time, packet loss rate, jitter,透過公式算出 R factor ,再算出 mean opinion value 。我查到的計算公式如下:

' Take the average latency, add jitter, but double the impact to latency
' then add 10 for protocol latencies
EffectiveLatency = ( AverageLatency + Jitter * 2 + 10 )

' Implement a basic curve - deduct 4 for the R value at 160ms of latency
' (round trip).  Anything over that gets a much more aggressive deduction
if EffectiveLatency < 160 then
   R = 93.2 - (EffectiveLatency / 40)
else
   R = 93.2 - (EffectiveLatency - 120) / 10

' Now, let's deduct 2.5 R values per percentage of packet loss
R = R - (PacketLoss * 2.5)

' Convert the R into an MOS value.(this is a known formula)
MOS = 1 + (0.035) * R + (.000007) * R * (R-60) * (100-R)

Sunday, October 16, 2016

物件關係阻抗不匹配(object-relation impedance) vs 快速開發的選項 ORM 或是 mongodb

物件關系阻抗不匹配是指:記憶體中的資料結構,往往是多維度的、巢狀的,和關聯式資料庫中的二維表格,其實是不同的。程式設計人員總是要花費不少時間,才能將記憶體中的資料結構轉換成資料庫中的資料結構。

現代許多流行的程式語言( ruby, python 等)都有提供「物件關系對應」 ORM( object-relational mapping ),主要是用來簡化程式開發人員處理「物件關系阻抗不匹配」問題。然而, ORM 麻煩的地方在於,批評者指出,它是一個 反面模式( anti-pattern )。是反面模式的理由主要是因為 ORM 違反了物件導向程式設計的重要原則:「封裝」。它沒有沒有完整地將 SQL 的細節隱藏在物件中,導致了程式變得難以測試。 批評者則是認為,應該要用 SQL-speaking object 來取代 ORM。

這個用 SQL-speaking object 來取代 ORM 的作法,固然是相當成熟、穩健的作法,因為有完整地封裝 SQL ,妥善地處理 object-relation impedance 問題。問題是,其實勢必還是有許多使用者之所以想用 ORM 最根本的理由,是懶得寫這麼多程式碼! 太麻煩了,因為要快速開發的話,根本一開始連需求都還沒有完全想好。

既然 ORM 有難以測試的問題的話,那還是使用 mongodb 吧。當然,等程式發展到一段時間之後,還是有可能會需要對資料庫做重新設計,但想要快速開發、想要跳過處理 object-relation impedance 的苦工,也只有 ORM 或是乾脆不要使用 RDBMS 這兩大類的解法了。

Friday, October 14, 2016

[open-falcon] micro services 與 message queue

公司做的監控系統是基於 open-falcon 去開發的。open-falcon 裡用來讓 agent 這個 micro services 可以同時對數個資料庫送出「訊息」的模組叫 transfer 。而由於一些討論 micro services 的文章建議,如果 micro service 之間是做一對一的溝通適合用 RPC(remote procedure call) ,如果是 micro services 之間做一對多的溝通,則建議使用 message queue 。

於是,當我上回安裝過 rabbitMQ 之後,我就不自覺地去想,能不能用 rabbitMQ 來取代 transfer ? 如果用 rabbitMQ 來取代 transfer 之後,可以有什麼好處? 我在除錯 agent 的時候,常常需要做的一件事是,用 tcpdump 去看 RPC 裡傳輸的資料,透過這個動作才容易把錯誤的點加以定位。如果用了 rabbitMQ 的話,就可以有一個 web UI 介面,可以輕易地看到正在傳遞的「訊息」,而不需要 tcpdump 了。

常常嗆主管是資淺工程師的M大大聽完我的想法之後,終於按捺不住地來指導我了。他的指導主要有兩件事:
(1) 上述的需求是除錯(diagnostics)的問題。

既然如此,應該考慮從 log 的改進、或是自行設計更合用的 run time debug tool。引入一個有複雜功能 rabbitMQ 並不是用來解決「除錯」問題的好解法,更不是一種透過「改進系統的架構」來解決問題的解法。好的系統設計,確實可以減少錯誤。但是那個原因是在於「概念完整性conceptual integrity

註:Conceptual integrity is the quality of a system where all the concepts and their relationships with each other are applied in a consistent way throughout the system.

(2) 抽象化可以解決問題,例如修改的彈性、或是程式的簡潔,但是抽象化也要付出代價。

M大大說著就提到了他找工作時,被考官質問的許多問題。「我在找求職時,有時候會被一些考官問一些莫名其妙的問題。例如,『你的前公司為什麼不用XX技術?XX技術不是OO大公司在用嗎?』這是什麼鳥問題?問這類問題的面試官一整個很像外行人。」

流行的技術或是工具背後,總是有力推的廠商或是社群,這些推廣技術的人,永遠只會講技術有多好,功能有多強。然而,從系統設計者的角度,要考慮的問題則是 trade off 。工具的功能再強不是重點,重點是適不適合解決手上的問題,還有解決問題需要付出什麼代價。

Saturday, October 8, 2016

[open-falcon] 利用 influxdb 來做 rrdtool 繪圖相關問題的除錯

最近公司基於 open-falcon 布署的監控系統,發現了一個難以理解的問題:「速度上限 10Gbps 的網卡,被監控系統量測出了 5Tbps 的超高速。」這樣子的問題該怎麼處理呢?


首先想到的解決方案是先從原始資料找起,但是,open-falcon 記錄這些數值的原始資料,是利用 RRDtool 來記錄的。所以一旦事發超過 12 小時,可以拿到的資料的精準度就大幅下降了。

所幸,當初公司也有把 open-falcon 的 transfer 模組,與 influxdb 加以串接。於是,我就可以在  influxdb 裡,找到由機器送過去的原始資料。

由於這一回要除錯的是網卡速度,我們就必須對資料的類別加以著墨。由 transfer 送往 Graph 和 influxdb 的資料,型態有 gauge 和 counter 。其中, gauge 比較單純,就是量測的數值本身,直接就可以被使用者所理解,例如:現在有幾個 processes 。而 counter 數值則是「累計數值」。為什麼要使用 counter 呢? 比方說,網卡速度就會需要了。因為像 linux 系統,其實直接透過 /proc/net/dev 只能取得某一張網卡瞬間累計接收/送出的 bytes 數目,而無法直接取得網卡的瞬時速度。換言之,監控系統之所以可以顯示網卡的速度,那是監控系統已經透過 counter 的資料型態,將速度從累計數值推算出來了。

 很快地,我就在 influxdb 裡找到了原始的資料。由於是 counter 型別的累計數值,原始資料必然是穩定上昇的直線。

而計算出 5T 速度的時間區段,也正好是區線呈現斷斷續續、不是穩定上昇的時刻。透過 grafana 的功能將時間區間定得更細之後,就看得更清楚了。
後記:

處理這個問題之後,我也比較了一下 influxdb 和 RRDtool 的。

RRDtool 是比較早的時代就發展的系統,它的特性就是很善於捨棄比較不重要的資料。所以像是 counter 型別的資料,RRDtool 是先將資料加以做類似對時間「差分」的運算,再平均值後才儲存。缺點就是要除錯時,因為不容易取得原始資料而難以下手。

而 influxdb 則是直接儲存原始的資料。也因此,如果要用 grafana 來對 influxdb 的 counter 類型資料做繪圖時,差分的運算就要自己來做了。所幸 influxdb 本身的 query language 也提供了這部分的支援,像下方的 derivative 指令,就是做這個差分運算。

SELECT 8 * derivative(mean("value"),1s)

從 unit test 到 functional programming

在最近的這分工作,才漸漸有在寫「單元測試」 unit test ,在和其它同事討論寫 unit test 的時候,我們討論到了寫 unit test 之前,就必須先把程式寫成「可以被測試」testable。

怎麼寫程式,程式可以是 testable 呢?
例如,函數儘量就只有透過 input argument 和 output argument 來和外界溝通,不要透過全域變數或是在內部呼叫會有副作用的函數。如此才能確保這個函數只要是相同的輸入,就可以總是產生一樣的輸出。

上述的種種要求,在 functional programming 的領域,就叫做要寫沒有副作用(side effect)的函數。或是叫做純函數 (pure function)。我查了網路之後,發現這個「單元測試」與「函數式編程」的關連性,也早就有許多人在談了。讓我來引用一下他們用的句子:

Writing unit tests is reinventing functional programming in non-functional languages.

Tuesday, October 4, 2016

vim, tmux - 256 colors support

現在做的工作,我都是用 vim 開發程式。網路上似乎很多人都是用 vim 搭配 tmux 。我也決定開始用 tmux 。畢竟每次開 terminal 都要用滑鼠點開一個新的 bash shell 好像也滿蠢的。

由於我用的 vim 是有設定 256 色支援的。在我的 ~/.vimrc 裡有下列兩行

colorscheme molokai
set t_Co=256

所以我用 tmux 的時候,自然也要做 256 色的處理。於是,我在 ~/.bashrc 裡加上了一行

alias tmux='tmux -2'

tmux 的 256 色出乎我意料之外的容易設定。

Monday, October 3, 2016

API 文件撰寫考慮的重點

公司在開會時,討論到我們該如何統一公司 API 文件的規範。本來大家都各說各話,主管則是表達:「請大家設法形成共識,不要讓我去推動一個 80% 的人都無法遵守的規範。」

終於,資深工程師M大大,發表了他對於 API 文件的先知卓見:

1 在我以前任職的公司,並沒有這種「先把程式寫完才寫 API 文件的這種開發方式。」一種可以做出穩定軟體產品的作法,是要先構思 API ,構思清楚之後才開始開發。 API 文件的重要性,可以類比資料庫的 schema 。

2 我個人比較重視下列三件事:
   (a) 編寫文件的表現自由度
   (b) 文件的格式必須是公開的格式,可以被改變的。因為外在的環境總是不停地在改變。
   (c) 是否可以快速地切換「檢視模式」和「編輯模式」

首先,apiary 這個平台,雖然有提供一些 sugar ,可以自動生成一些測試、說明,但是,它的平台功能並不是非常完整,同時也限制了編寫文件的自由度。如果需要撰寫的 API 是 stateful 的,是特別複雜的,其實用這種平台反而不會特別好用。這種平台只適合 API 的數目不多, API 相對單純的情況。於是,這個就導出了一個議題,是要選擇「半自動生成的 API 文件」還是選擇「純手刻但是 100% 自由的編寫方式」。如果考慮要做出高品質的文件,我還是傾向於後者。

關於文件格式的部分,我本來寫的文件一開始是用 mark down 寫在 gitbook 上,後來移到 trac 的 wiki 上。雖然 mark down 和 wiki 的語法不同,但是這些格式只要取代之後即可。所以使用公開的格式是很重要的。因為承載文件的平台很有可能會一直變換。然而,如果一開始是用 word 來寫,噢,那就糟了。

最後,像是 apiary 這個平台,它要快速切換「檢視模式」和「編輯模式」,並不是很順。然而,寫 API 文件如果要寫得清楚,就難免要用到比較進階的「格式化選項」。這時候就勢必要用肉眼來檢查,格式指令是否正確。所以能否快速切換,這個也是考量的重要條件。

javascript async/await promise ---- 驗証方式

在 javascript 裡,因為 I/O 都是非同步呼叫,如果需要寫出有相依性的業務邏輯(同步呼叫),因為要寫大量的 callback,可讀性上就會遇到 callback hell 的問題。新版的 javascript 已經有提供了 async/await  語法,搭配 promise 來取代 callback 的寫法。

然而,要如何驗証使用 async/await 之後,函數的呼叫真的是「同步」的呢? 這個問題該如何驗証呢?其實可以利用 chrome debugger 來驗証。
 圖一:這個是實作 async/await 之前的狀態。可以看出兩條紅線的右端,兩個呼叫後端的函數是在同一個時間點開始。
圖二:經過 async/await 的修改後,兩條紅線的右端同樣兩個函數,開始的時間產生了明確的差距,恰好就是第一個函數完成之後,第二個函數才開始。