The Art of Readable Code && High Performance Comments

Headless-Poller Screenshot

還債系列

這是本系列最後一篇文章 XD 請參考

緣起

在準備這場小聚講題的時候,我給自己設定的主軸是:透過罕用的模式,介紹社群不熟悉的服務。最近恰好對 NoSQL (MongoDB, DynamoDB, CouchBase) 作過一些測試,若能趁熱整理出來,對自己的 out-of-band memory 也有幫助 :p

另一方面,我發現有些公司過度倚賴 EC2:來自用戶的 API Request 要在 load balancer 、dispatcher、worker 之間轉傳多次,才能完成請求。這樣的架構若要達成 HA ,其難度倍增,因為 HA 架構必須將各層之間連線也納入考量。若架構失當,又或根本忽略 HA,只消關鍵伺服器當機,小則服務中斷,大則丟失資料;把 Failover 程度的問題弄成需要 Disaster Recovery,這就不太好了。透過 AWS 的 JS / Mobile SDK 用 Managed Services ,以處理需要 Automated Scaling 與 HA 的服務,是對應此問題的最佳方案。

考慮到主題規模與方向,我決定開發以 JS SDK 整合 DynamoDB 的線上投票專案,以演示相關概念,以及雲端世代的架構流程;Headless-Poller 便由此而生。

Building Blocks

由於需求條件相當龜毛(不使用任何 worker instance / process / connection),只要基本需求敲定,實做大概只剩唯一解(The AWS Way :p)。用戶要能以第三方驗證登入,因此需要 IAM / STS 與 https (以 CF 達成),以整合前面提過的 JS SDK 與 DynamoDB。

網站是全靜態 SPA,我選用比較熟悉的 Angular.js + Require.js 來兜;又因為 CF 對 Index Document 的限制,並且不支援 Rewrite,只有選用 /index.htm#!/ 這樣的 url。

這麼一來,實做前只剩一件事:確認能透過 DynamoDB API 與 Fine-grained Control,在不洩漏用戶資料的前提下,完成客戶端計票。這部份我是用 Python / boto 完成,畢竟文件與功能似乎比較完備。

注意:線上計票的正解是使用 worker process 統計票數,用戶再由 S3 or CF 抓取;很可惜這違反了 Headless Poller 的需求。長知識總有代價的 🙂

設計 DynamoDB 表格

以主鍵 hash key 限制寫入身分這應該很自然:Cardinality 佳且符合 DynamoDB 提供的 Policy Key;而設計為 (User_ID, Quiz_ID) 的主鍵能限制每人(帳號)一票。又因為主鍵對計票無幫助,若未加入其他 Index,只能以 scan 計票。建立 GSI (Quiz_ID, Option_ID) ,並且不 Project 任何欄位,應該能解決問題。

測試後發現 Index 必須 project 至主鍵,且不得限制 client 讀取;若在 Query / Scan 排除(或未選擇)主鍵欄位,會出現各種靈異現象。考量土砲測試條件曠日費時(IAM Policy 需要些時間才會送到 DynamoDB 機器上),還是回頭念文件吧。終於在 API 中發現 DynamoDB 唯一的 Aggregate Function: Select: Count。驗證可行後,這下可以開工了。

Access Pattern & Throughputs

用戶投票時,會發出一個 write request,並對 GSI 產生一次寫入。

每個瀏覽器會在背景每五秒刷新一次票數統計;由於 BatchGetItem 不支援 Select 參數,也無法節省 throughput ,就只能針對每個選項各發一個 query request。若有 30 人同時上線,會有 30 / 5 * 4 = 24 QPS;即便資料再小,單一 query 至少消耗 1 read capacity。又因為 GSI 只支援 eventually consistent read,並且資料緊密儲存,因此在最差狀況(Count 需要遍覽所有 Index Entries)下,一個 read unit 能夠計算 8000 / 30 ~ 260 張票。

因此在與會人數不破百的前提下,設定 GSI read throughput 略高於人數,且 GSI / Table 為人數 1/5 應是合理猜測(假設平均五秒投一票)。

當然,有些工具如 Dynamic Dynamodb 能自動處理 throughput 問題,讓設計者只須留意 Access Pattern;不過這違反專案設計理念 🙂

實做

沒啥好說,就 Angular.js + Require.js 的 SPA XD

隨便扯扯感想:

  • Login with Amazon 的 js 檔很常載入失敗,只好透過 Require.js 在專案裡提供 fallback
  • Promise 滿天飛 XD 除了可以解決 callback hell 以外,快取 promise 能提供比快取 data 更簡潔且一致的 code pattern
  • AWS API 的錯誤訊息與 http code 實在不太營養
  • boto dynamodb / dynamodb2 底層 (layer1 / layer2) 的註解根本是 Copy & Paste DynamoDB API 文件,參考價值近乎於零

感想

做了這專案以後,反而更增添心中對 DynamoDB 實做的疑問;如果有時間,會想針對底下幾個問題作深入測試,如果有時間…

  • Select: Count 使 DynamoDB Read Capacity 爆表
    • 原因?
    • 比 Read & Count 更高效?考慮給 boto 發 PR,因為他們使用 Read & Count 實做 count()
  • Throughput Throttle 實做層級
    • 如果做在 balancer ,則 poor cardinality indexes 在打到 shard server 設定上限前,效能不會降低
  • blah blah blah….

然後這次對時間與範圍的估計,真的出很大一包。如果以後還有機會,會把時間都放在正題上;其他像 IAM, DynamoDB, VPC, EC2 等等的基本操作,就自己念文件吧 XD

另外這專案的 table 六月左右要關啦,想玩的要趁早;參考 Headless-PollerSource

發表迴響

分類

%d 位部落客按了讚: