2020年4月5日 星期日

redis 的 cache stampede

之前就大概有稍微看了一下 cache stampede 的文章,也大概知道了 xfetch 這個東西,
但是一直都沒有仔細下去看,因為覺得問題不大,

直到剛好看到這篇

https://kkc.github.io/2020/03/27/cache-note/


裡面大概講到幾種避免的方式:

  1. 弄個 cronjob/worker 定期去更新,這就犯了一個常見的問題。
    1. cache 到底是 cache 還是 storage?
    2. 如果是 cache,那他應該是 on demand 的才對吧?
    3. cache fail 了,程式應該要知道怎麼 fallback!
    4. 既然是兩個地方都要處理同樣的邏輯,程式的架構是寫死在一個地方,還是寫成 component?
  2. lock 機制
    1. 當大家同時遇到某個 cache expire 了,就用 lock 來搶吧!
    2. 所以只有搶到 lock 的那個 request 可以真的 query db,其他的就在那邊 sleep!
    3. 那要等多久?等到天荒地老?要不要設定一個 timeout?如果 timeout 到了,還是沒辦法從 redis 拿到資料該怎麼辦?一樣大家進去操 db?
  3. xfetch 機制
    1. 會在 query db 的時候把要花多久存下來(aka delta),之後在每次要 get cache 的時候,一次把 cache value/ttl & delta 都拿出來,先比對 delta 如果 >= ttl 的話,就預先做一次 query db,然後把資料存回來。
    2. 那如果同時好幾個人都 get cache,然後大家都要去 query db 呢?
    3. xfetch 用了一個 random 來判斷!
    4. 沒錯,用 random,所以最差就是大家一起死,不然就是等到大家都 cache expire 了,再一起死!
這些方法,聽起來都好像可以,但是真的實作起來,其實都還是怪怪的。

  1. cron job 如果程式架構可以,要處理的 hot spot 也夠明確,的確能立即解決問題!
  2. 但是在 cloud 的時代,每個 instance 都應該是沒有狀態在上面,也不應該在上面跑 cronjob 才對。不然大家都一起跑,還不是一樣大家都戳一次!
  3. lock 好像也很好,但是在很多 instance 很多 process 的狀況下,該怎麼 lock 呢?
  4. 有的人會說,redis 有 redlock,對,的確可以用,不過就算 lock/unlock 的時間點抓得夠好,還是會有其他的 request 在那邊等的狀況發生(不過原本 request 在等,有可能是 query db,所以無論如何都在等,無所謂)
  5. xfetch 看起來是比較聰明的,但是蠢就在,他用 random 去判斷誰該做事,random ㄟ,random ㄟ!沒有更聰明的?

經過一段沈思跟試驗實作之後,我覺得真正簡單又可以動的版本應該是這樣的:

xfetch + lock

要提前 query db,但是又要想辦法讓同時間只有一個 query db 的動作!

那要怎樣做到這些事情?
要感謝 redis 後來有 server side lua 了,可以讓你在 redis 裡面先做一些判斷,
client 只要一個 packet 就可以拿到他要的結果!

2019年6月26日 星期三

[FreeBSD] UEFI boot loader with http support

最近 FreeBSD 的 uefi boot loader 加上了 http protocol 的支援,

但是剛出來的 code,總是有點 bug,而不幸的是,我剛好踫上了!

make world; make kernel; reboot 之後,機器就卡在 boot loader 的過程,
因為是 load boot loader 階段,所以連進去 boot loader 去 bypass 的機會都沒有。

更難堪的是,跑在小機器上,M2 規格只有一個,不可能裝另外一個 SSD,然後把原本的掛上去偷天換日。

所以想了一下,大概就是用 bootable cd,開起來之後進去 shell 裡面去處理。

因為現在其實很少人會還有燒錄機這種東西,而我也討厭用 usb 去 boot,
所以 N 年前就在同事建議之下,買了拉麵外接盒 (https://24h.pchome.com.tw/prod/DRAB5S-A9005B1NK)
可以直接將 iso 丟進去,讓他模擬成 bootable cd,很環保。

下面是一些步驟,是為了以後遇到同樣的問題,我自己會忘記而記錄下來的。


  • 用 install cd 開機之後,dialog 選 shell,然後用 root login。
  • dmesg 看一看,可以查到自己的硬碟在哪個裝置上(我的機器上是 nvd0)
  • gpart show nvd0
    • 可以列出這個硬碟上面有哪些 partition。
    • 我是把系統都裝在 zfs 上。
    • 理論上第一個應該是 efi,第二個是 freebsd-boot,第三個是 freebsd-swap,第四個就是 freebsd-zfs 了。
  • 再來就是用 gpart 來安裝 boot loader
    • gpart bootcode -b /boot/pmbr -p /boot/loader.efi -i 2 nvd0
    • index 是 2 的原因是, 1 => efi, 2 => freebsd-boot
  • 重開機,boot loader 就是比較舊版本的那個了。


Ref: https://ashish.blog/2018/06/freebsd-uefi-boot/

2017年5月9日 星期二

[elixir] 打包

以前一直都是用 exrm + exrm_rpm 做 elixir project 的 release manager,

但是最近因為改用 aws,同事又偏好 ubuntu (aws 上面不是應該直接用 amazon linux 嘛?)
所以得用 exrm + exrm_deb 來打包,


無奈 exrm & exrm_deb 在 hex.pm 上面的版本都太舊,
搭配著 elixir 1.4 都有一堆 warning,
然後 exrm_deb 生出來的 deb 無法安裝,一直都會有 conffile 的問題,
自己試著解,還是沒辦法搞定。

觀察一下,exrm 的作者其實心力都不在 exrm 上了,他自己又搞了一套叫
distillery 的 package,功用也是作 release manager,
然後別人也幫他弄了一個 distillery_packager,宣稱可以產出 deb/rpm。
玩弄了一下,生出來的 deb 沒有問題,systemd & upstart script 都幫你處理好,
理論上裝上去就可以直接用。

PS: distillery_packager 宣稱 deb/rpm 都支援,但是實際上只有 deb,rpm 還在等待有緣人送 patch。

2016年10月24日 星期一

Apache Traffic Server & Varnish Cache 之我見. (Part 2)

上一篇忘了講,大部分的人弄 proxy,架構應該都是像下面這樣弄的:




















流量由 l4 load balancer 導入,可能用 round robin 或者是一般的 hash 將流量散到各台去,這樣會有幾個問題可能發生:

  1. 如果是 round robin,任意一個 url 都可能導到任意一台,假設我有 100 台 storage,如果很不幸的每次都是導到沒有 cache 的某台 storage,這樣對 Origin Server 就是一百次的 request,並沒有真的解決問題。
  2. 如果是用一般的 hash,全部的機器都健康的時候,hash 都維持得很好,cache hit 也可能都會不低,但是一但某台機器掛點,會重新 re hash,原本養的 cache 全都報銷了。
再講回 Apache Traffic Server,其實它源自 Inktomi 的 proxy server,設計精良,當年被 yahoo 併購之後,yahoo 只看到 inktomi 的搜尋部分業務,其他的東西都被冷凍起來,後來有人挖寶之後,發現了 inktomi proxy server,一用之後驚為天人,轉身變成 Yahoo Traffic Server,並且大量部署在 yahoo cdn 上。

不過後來可能因為政策改變,yahoo 內部自行研發了 uff 這套原本想要取代 yts 的 proxy server,所以將 Yahoo Traffic Server 在 2009 年捐給 Apache Project,原本一些在內部做 yts 研發的人員,大部分也同時成為 apache committer 來改善 Apache Traffic Server。

後來的後來,由於 uff 最主要的開發者離職,Apache Traffic Server 在 open source 社群中大量被改善效能,原本只能跑在 32bits 的環境,也改成對 64bits 相容。所以後來 Yahoo 又將 cdn 相關的 solution 改回來 Apache Traffic Server。


剛才講到 Apache Traffic Server 設計精良,內部對於 thread/event/network 都有各自的 processor,像是 carp 做 health check 的部分,就是丟給 event processor,然後會去呼叫 thread/network 相關的 processor 做更低階的處理,最後再透過 callback 之類的將最終狀態丟回原本的 caller。


讓我整個卡住的部分,其實就是在這邊,怎麼樣看,他都應該幫我開一個 tcp connection 出去,但是程式判斷的地方,他會先判斷 thread event type,如果不是他要的,就直接不處理,等到 event timeout 之後,就會去 callback 原本的 caller 來做後續的處理。可是我找不到任何的地方去決定 thread event type 呀!!!

因為我的 c++ skill 實在太差,在卡住三四天之後,覺得這樣下去不行,雖然手上有最終解決方案,就是 Apache Traffic Server cluster,但是終究是要 deprecated 的東西。所以我就開始找尋下一個替代方案,nginx 不行,因為 proxy mode 比較厲害的東西都被放在 nginx plus 裡面。那 varnish 呢?以前稍微試用過的經驗並不好。不過市面上可以用的 proxy 幾乎沒有了,每一套宣稱自己多厲害多厲害的 proxy,終究就只是很單純的 proxy,很多連自己對 client request/response, origin server request/response 都沒辦法額外處理。更不要講 performance 了。

在抱著試試看以及捧個人場的心情下,試用了一下 varnish cache,為什麼說捧個人場?因為 varnish 是 phk@freebsd.org 最近這十年來的力作,他幾乎都沒有再碰 kernel development,全心全力的做 varnish cache 的開發。

varnish cache 有下列幾個我覺得很棒的地方:
  1. bsd-2 clauses license,去他媽的 gpl。
  2. 沒有設定檔,一些參數都用 cli 方式傳進去,然後如果不會寫 vcl 也沒關係,backend server 相關資訊一樣可以用 cli 傳進去。
  3. vcl 很簡單,會寫簡單 perl 的人都會。
  4. vcl 不是直譯式處理,而是在 start-up 的時候,直接轉譯成 c code,馬上 compile 成 .so,varnishd load .so 起來處理,只要專心做好自己想要判斷的事情就好,memory leak/performance issue 等相關的事情完全不要理會。
  5. builtin vcl function 不夠用的話,可以自己寫 vmod 來擴充 function。
  6. log 什麼鬼的,varnish 全部都扔到 shared memory 去,另外有一隻 varnishlog 會從 shared memory 讀資料之後寫黨,所以什麼 log rotate 之類的事情,不用 kill -HUP varnishd,服務完全不會中斷。
  7. reload vcl 之類的也是可以 online 做,一樣服務不會中斷。
  8. 相關的 utility 做得非常好,varnishadm/varnishstat 之類的,幾乎都是商業等級的水準。
  9. 內建 health check,可以做到失敗幾次才會算真的失敗這類的情境。
  10. 支援各種 director,可以簡單應付各種場景。
    • fallback
    • hash
    • round robin
    • random
    • shard
不過 varnish 現在對我而言有一個瑕不掩瑜的缺點,雖然這個也可以用另外的程式做掉,但還是會覺得有點不舒服,那就是,他不支援 ssl,https 之類的要自己處理,這對於之後的 ios 可能會是個問題。

總之,varnish 算是簡單就達到我想要的功能,理論上會直接拿來使用。而 Apache Traffic Server 也不是完全沒有優點的,只是不會寫他的 plugin 的話,出廠預設的功能,其實是很難滿足需求。

2016年10月23日 星期日

Apache Traffic Server & Varnish Cache 之我見. (Part 1)

最近因為任務需要,需要自己弄一套 proxy/cache 的系統。

至於為什麼要自己弄?

  1. 當地國際頻寬貴,如果能在當地弄一份 static replica,可以省下很多錢。
  2. 但是在偏遠的地方自己弄 storage,光想營運成本就很高,尤其每樣都要做 HA 的時候,成本跟硬碟幾乎都是一路 * 2 * 3 上去。
  3. 既然不能搞 storage,那弄一份 cdn 總可以吧,基本上解決 20% 的 content,就省掉了 80% 的 cost。
  4. cdnetwork 之類的,幾乎都沒有在當地有 edge server。
  5. 所以即使用了 cdnetwork,還是要走國際頻寬出去,一樣貴。
原本想像中的架構如下:
  1. 最前面擋台 l4 load balancer,將流量 round robin 散到下面的 l7 load balancer 上。
  2. l7 load balancer 理論上會用 Apache Traffic Server 加上自己寫的 plugin,將前面進來的 request,透過 consistent hash 算過之後,平均導到後面的 storage 上面。
  3. consistent hash 只是個說法,重點是每台 l7 load balancer 自己要維護一份後面的 storage 的健康狀況表。
  4. storage 相對起來簡單,但是要能根據後面 Origin Server 的不同產生不同的 header 之類的,例如如果 Origin Server 是 S3,要能自動生出 s3 request header。
  5. 這樣做的好處在於,如果其中一台 l7 load balancer 壞掉,起碼有一台可以撐住。而後端的 storage,也會因為 l7 load balancer 已經用 consistent hash 算過,固定的 url 會送往固定某台,cache hit rate 會拉高,Origin Server 假設是 300T 好了,每台 storage 基本上可以只是部分就好,一旦任意一台 storage 壞掉,起碼可以先將 request 由隔壁來分擔,而不是全部養好的 cache 就全部掛光。





























剛才講到會用 Apache Traffic Server 來實做這些東西,為什麼呢?

  1. 畢竟我也是 ex-yahoo 呀。
  2. 畢竟我也摸了好一陣子的 yts & uff 呀。
  3. 畢竟我也是受到業界知名的嫩哥的薰陶。
Consistent Hash 就用 google 提出的 Jump Consistent Hash,而後端機器健康程度,要就是 plugin 自己開個 thread 定期去維護,不然就是透過 lmdb or mmap file 之類的,讓另外一支程式來幫你維護。

plugin 寫了一半,看到其實 Apache Traffic Server 有 cluster 的功能,看起來大方向其實跟我想做的雷同,只是方法不同。
ats cluster 的做法是,根本不分 l7 load balancer 還是 storage,全部都是 storage,只是一個 request 近來,會先查查自己肚子裡面有沒有 hit,沒有就發 udp multicast 出去問隔壁的有沒有人有 hit,有就直接抓來往 client 送。

諮詢嫩哥對於 ats cluster 的看法之後,他說 "cluster? 快要 deprecated 了呀" "現在你要用 carp 啦!"

carp 是什麼呢? 是 yahoo 針對這類的需求開發出來的一個 ats plugin,並且已經有送 pull request 給 ats 了,目標是在下一個版本的 ats 會包含這個 plugin。
想嚐鮮的可以先到 https://github.com/ericcarlschwartz/trafficserver/tree/TS-4723/plugins/experimental/carp 去查看。

既然 yahoo 都這麼有心,放出了方案給大家用,似乎不去試試看,好像還頗對不起 MM 的。

carp 光看設計,其實頗不錯的,health check 幫你做,也做 consistent hash,基本上我想要的他都做到了。很棒,立馬抓來 compile,load 進 ats 來試試看。

嗯嗯, 問題不大,最大的問題頂多是不會動而已!

對,他不會動,仔細查了一下相關的 code,寫法都是對的,但是他就是不會動!

好吧,一路追下去好了,TSContCreate,TSContSchedule,TSNetConnect 看起來每一樣都對呀!

不對,為什麼 TSNetConnect 裡面只要是 carp 要做 health check 的 connection,他就是直接不處理,然後最後等到 timeout?

喔,原來是 thread type 不對他就不處理呀!可是從頭到尾都沒有一個地方可以讓我自己決定要用哪種 thread 呀!

這個部分大概就卡了我三四天!!!

2016年3月23日 星期三

[Elixir] 安裝 erlang + elixir + phoenix framework

elixir 是架構在 erlang vm 上的語言,所以在開發程式之前,第一件事就是把 erlang 裝起。



  • Linux
    • 由於個人使用習慣,平常我個人都用 FreeBSD,linux 我大概只用 rhel/centos。
    • rpm 可以在 Erlang Solutions 下載,不過這個版本會順便將 wxGtk 也裝起來,個人並不喜歡。
    • 可以使用 rabbitmq 做好的 zero dependencies rpm
  • FreeBSD
    • 透過 ports,直接到 lang/erlang 下面 make install 就好了。
裝好了 erlang,下一步就是把 elixir 也裝好。
  • Linux
    • 我不確定有哪一個 distribution已經內建 elixir,但是同樣的  Erlang Solutions 有 package,但是只有給 debian/ubuntu 的而已。
    • 所以直接到 elixir 的 github 下載,解開之後打個 make install 就裝好了。
  • FreeBSD
    • 一樣很簡單,到 lang/elixir 下面 make install 就裝好了。
裝好 elixir 之後,基本上會有兩個東西會不停地在開發過程被用到:
  • mix - project management tool,任何你在開發過程會用到的工具或者流程,都會透過 mix 這一個 command 來執行,另外也可以自行編寫 mix task 擴充功能。
  • iex - interactive elixir shell,就是一個 elixir shell,一些簡單的想法可以直接在 iex 裡面實現及驗證。
再來就是安裝 phoenix framework,這東西有點像是蛋跟雞一樣,你要有 phoenix 的 mix task,才能夠建立一個 phoenix project,但是你也要先有相關的 package 安裝好了,才能有 phoenix mix task 能夠被使用。不過沒問題,elixir & phoenix team 都幫你想好了,透過 mix archive.install 就可以把 phoenix 裝好。

 mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez  

裝好 phoenix 之後,打 mix -h 就可以發現多了一個 mix phoenix.new 的 task 可以用。
也可以打一下 'mix phoenix.new -h' 來看看相關參數。

2016年3月10日 星期四

[Elxir] 為什麼要用 elixir 呢?

開始的開始,其實我是先知道 erlang 這個語言的。

大約在 2007/2008 左右,那時候應該還是在 erlang 13.x 的版本,我忘記我從哪邊知道這個語言,一直想好好學好它,也買了書來看。

但是大概是 perl/php 這類語言的餘毒,讓我在學習 functional language 的路上沒能好好走完。
雖然一直都沒學好,不過我都還是持續在關心著 erlang,尤其這中間很多公司,都用了 erlang 做一堆神經病等級的產品,例如 whatsapp,例如 cowboy 等等。

而 elixir 這個架構在 erlang vm 上的語言,我大概是兩三年前在 erlang 的社群略有耳聞,但是由於之前對於 erlang 一直學不好的心理因素,就一直忽視他。

那到底是什麼樣的機緣,又讓我再度擁抱 erlang 甚至開始學習 elixir 呢?應該是因為工作需要吧!因為一些先天上的限制,軟體的 dependencies 其實是困擾的所有的開發人員,尤其是開發電信機房裡面的服務。

一開始我們選擇的 golang,寫起來簡單,相關的 unit test 都具備了,但是又似乎少了點什麼,如果程式因為任意原因死掉了,你必須自己監控,必須自己想辦法。即使 golang 有了 static link 這個降低 dependencies 的好處,但那還是不夠呀。

所以大約在去年七月,經過一些評估之後,我重回了 erlang/elixir 的懷抱:

  • ecosystem 完善
    • 有 hex.pm 這一個 package management site,任何人寫的東西都可經由這個網站發布,也可以輕易的跟 project 結合。
    • elixir 內建了 mix 這一個 project management tool,任何開發期需要的東西,都可以透過這個工具去呼叫,也可以自己寫 mix task 來把複雜的事情變簡單。
  • otp
    • Open Telecomm Platform
    • erlang 內建 otp,因為 erlang 就是為了交換機而被發明的語言,所以他很多特性,都是為了電信業而產生。
    • 而 otp 本身就有很多先進的設計,就是為了確保服務儘可能地沒有 downtime,例如 supervisor 可以檢控 process,當他掛掉會自動重新叫起來。例如 code hot swap 可以讓 daemon 持續服務,但是瞬間將版本升級。
  • async
    • 在 erlang 裡面,每一個 app 都是獨立的一個 erlang process,彼此用 message passing 溝通,所以要寄信,直接將相關資料扔給寄信的 process 即可,前端的 web 可以繼續服務。
  • no side effect
    • 也由於資料並沒有 lock 這類的問題,所以儘可能地降低 side effect。
  • 接近人類思考模式
    • erlang 說真的我還是沒認真寫過,但是 elixir 幾乎已經接近人類思考模式了,人並不會做一件事情,只有一個 function 然後裡面一堆 if/else,而是在最開始,就已經把很多事情直接分開。這樣的好處是,每個 function 就只做自己的事情。
  • phoenix framework
    • 如果只有 elixir + mix 這樣的工具,其實還沒辦法好好做事,畢竟什麼東西都還是要有個漂亮的介面。
    • phoenix framework 是用 elixir 寫的 web framework,再加上 ecto 之後,資料庫什麼鬼的都抽象化了。
    • 簡單的說,elixir 就像是 ruby,phoenix 就像是 ror,而依賴著 erlang 天生的高效能,可以快速地生出很棒的服務出來。
  • exrm + rpm
    • exrm 是一個 release tool,經過 exrm 的 task 處理過後,可以生出一個 .tar.gz 檔。
    • 將這個檔案直接 copy 到機器上,解開就可以直接跑起服務。
    • 像是 erlang vm 什麼的,都會在這個 .tar.gz 裡面。根本不需要預先安裝甚麼軟體。
    • 但是只有 exrm 對很多人來說可能還是不夠,所以有人又替 exrm 衍生出 exrm_rpm。
    • exrm_rpm 在 exrm 的基礎上,可以在 task 的最後,把 .tar.gz 轉成一個 .rpm。
    • 這個 rpm 裡面連 init.d 的 script 都有了,直接 yum install xxx.rpm,重新開機就所有服務一次到位。
評估完,就開始將原本用 php 寫的 web tool 改用 elixir 寫,整個工作從去年八月開始,中間加入了一些 linter,code analyst tools,並在今年二月左右,將整個 unit test/integration test 都做完,最後的 code coverage 約 85%。

本篇為起手式,之後會開始從怎樣弄一個 elixir project 講起,到我中間有用過的工具,然後最後以包出一個 rpm 為結束。

(不保證什麼時候會做好。)