Slack 在 2022/02/22 發生的 downtime 說明

Slack 針對今年年初的爆炸提出了說明:「Slack’s Incident on 2-22-22」,但真正的重點都在 Hacker News 的討論串上:「Slack’s Incident on 2-22-22 (slack.engineering)」。

大概有三件事情可以講,第一個是掛掉的原因,第二個是剛發出來的時候,一堆人對於標題用的「2-22-22」很感冒,第三個是剛剛 (一個小時前),Cal Henderson (Slack 的 CTO) 跑到 Hacker News 的討論串上回應...

Downtime 的部份

這次的 downtime 主要是發生在 Group Direct Message (GDM) 的部份:

A significant element of the datastore load appeared to be from a query that listed Group Direct Message (GDM) conversations by user. This operation is fronted by our cache tier, so the high query load seemed to indicate something was wrong with our caches.

這個 GDM 的查訊效率不高,而是靠 cache layer 撐住的,加上二月 22 日那天他們在更新 Consul 的 agent,導致 hit rate 的下降,以及遇到一個比較大的 peak time,接著就壓垮了資料庫。

oh,這中間還有 Vitess 一起進來打架,原文講的比較清楚,但需要花一些時間看。

2-22-22

剛發表出來的時候,其實大多數的討論反而是在討論「2-22-22」這件事情,這的確是很差的表示方法,尤其對於一份公告來說,不過這個問題本來就是個 flame war 等級的話題...

Slack CTO (Cal Henderson) 的回應

在重刷頁面的時候發現 iamcal 這個帳號的回應,而 Cal Henderson (Slack CTO) 的個人網站是 www.iamcal.com,雖然不確定這是不是本人帳號,但看起來之前在 2011 註冊後都沒動...

這個帳號回了兩個訊息,一個是提到 AWS 上其實很常看到 failure,需要靠本身架構的穩定性來撐:

Our underlying hardware (AWS) is nothing like this reliable. We see regular (several times a year) failure of racks of machines or whole DCs.

Across the whole fleet (all services), we lose 1-10 servers per day as a baseline. Major events are then on top of that and can impact thousand of hosts at once.

另外一個是反駁自以為的量級估算:

> Even the largest Slack instance probably has under 100,000 users and less than 1000 peak messages per second.

This is not true, by an order of magnitude.

好像還可以繼續在盯一下,不知道還會不會有回應...

限制流量的方式 (rate limit)

Lobsters Daily 上看到這篇 2017 年的文章,Figma 的工程師講怎麼做 rate limit:「An alternative approach to rate limiting」,只要大一點的站台就會遇到 spammer 之類的攻擊,就會希望實做自動化的機制擋住 spammer。

文章裡面提到了三種方式,第一種 (類) 提到了經典的 Token bucketLeaky bucket,這邊文章提供的演算法是讓每個使用者都會有一筆資料紀錄在 Redis 裡面 (這邊的用法可以抽換換成 Memcached),裡面記錄了最後一次的 access time 以及還剩下多少 token 可以用,接下來就可以照時間計算 token 的補充與消耗:

但這個演算法的缺點是 race condition,需要另外設計一些機制確保操作的 atomic:

不過大多數的實做就算不管 atomic 也還行 OK,只是會比較不精確一點。

第二個方法他叫做 Fixed window counters,這個方法把時間切齊為單位 (像是 60 秒為一個 window),所以可以把起點的時間也放到 key 裡面,然後 value 就是數量:

這個作法的好處就是簡單,而且 Redis 與 Memcached 都有提供 atomic 的 +1 操作。但缺點是可能會發生兩倍以上的 request,像是 5 reqs/min 的限制有可能會有連續的一分鐘內達到 10 reqs/min:

不過我覺得就 antispam 來說算是夠用了,當年 (大概是 2007 或是 2008 年?) 在 PIXNET 時用 C 寫 Apache module 就是把資料丟到 Memcached 裡面就是這樣實做的,然後每次學術網路的實驗室跑來掃站的就會自動被擋 XDDD

第三種方式他們稱作 Sliding window log,就是把每個 request 的 timestamp 都存起來,這個部份用 Redis 的資料結構會比用 Memcached 方便一些:

這個方式在控制上更精確,不過空間成本上就高很多... 這樣算是把常見的實做方式都提到了。

Memcached 與 Redis 的比較

在「Memcached vs Redis - More Different Than You Would Expect」這邊看到對 MemcachedRedis 的分析。

這兩套軟體都很常被拿來用作 cache 機制,所以一般來說比較時就是比兩邊都有的東西 (如果你要 pub-sub 之類的東西,在這兩套裡面只有 Redis 有)。

最前面還是先講了對使用者 (開發者) 的差異,很明顯的是 Redis 對各種不同的資聊結構都有支援,這點可以從 Redis 被官方被稱作 Data Structures Server 就可以知道 (在「An introduction to Redis data types and abstractions」這篇可以看到),而 Memcached 只支援了 key-value 架構。

不過如果是以 cache 來說,的確 key-value 架構就還蠻好用的。

後面就開始比較硬的主題了,提到了 Memcached 與 Redis 內部是怎麼使用記憶體的。

Memcached 的部份先提了 page/slab/chunk 的架構以及產生的效能限制與浪費,接著有提到 2020 年 refactor 的部份 (太久沒有看 Memcached 的消息,去年沒跟到這個部份),讓多 CPU 的支援度更好。

Redis 則是靠 jemalloc 來處理這個部份,另外加上 background thread 的機制降低 fragment。

然後是比較 cache expiration 的部份,可以看到兩者用的演算法在現實世界中都夠用 (尤其是當作 cache 來用),這部份跟印象中的架構差不多,應該是沒有太大變化。

最後是比較 cluster 的部份,Memcached 是 share nothing,所以沒什麼好說的,主要是靠 client library 實做 consistent hash 之類的架構打散;而 Redis 的話看起來有實做新的機制出來 (也沒跟到),之後有機會再看看可以做到什麼程度。

不過好像沒提到 proxy 之類的架構,基本上各大公司都有自己幹:

少了這塊對於 cluster 架構的完整性差蠻多的。

文章最後沒有下定論一定要用哪個比較好,兩者都有強項與弱項,還是得看情況來處理。不過我自己還是很喜歡用 Memcached 就是了...

ALB 支援 Sticky Session

又是一個以為很久前就已經支援,但實際上沒支援的功能...

ALB 支援使用 cookie 實現 sticky session 功能:「Application Load Balancer now supports Application Cookie Stickiness」。

使用者的 session 通常會使用 cookie 記錄,而如果有多台 server 提供服務時,session 裡的資訊就需要找一個 shared session storage 放,以確保使用者在連到不同的 server 時都還是可以讀到對應的 session,比較傳統的方案就是直接把 session 塞進資料庫,後來發展出 memcached 或是 Redis 可以用。

但有些買來的軟體並沒有考慮到這點 (常常都是內部系統),導致前面放 load balancer 時,必須想個辦法記錄使用者使用後端的哪台機器,這樣就可以在後端不支援 shared session storage 的情況下,還是可以讓應用正常運作。

透過 cookie 實做的 sticky session 算是蠻常見的作法,只是以為早就有了...

GitHub 在 2/28 遭受的攻擊...

GitHub 在 2/28 遭受 DDoS 攻擊,蠻快就把事故報告丟出來了:「February 28th DDoS Incident Report」。

不過跟 GitHub 其他文章不太一樣,這篇算是 PR 稿吧,簡單來說就是花錢買 Akamai Prolexic 的過濾服務解決... Akamai 方的 PR 稿則是在「Memcached-fueled 1.3 Tbps attacks - The Akamai Blog」這邊可以看到。

17:21 UTC 發現問題,然後判斷超過 100Gbps,所以 17:26 決定讓 Akamai Prolexic 接管過濾:

At 17:21 UTC our network monitoring system detected an anomaly in the ratio of ingress to egress traffic and notified the on-call engineer and others in our chat system. This graph shows inbound versus outbound throughput over transit links:

Given the increase in inbound transit bandwidth to over 100Gbps in one of our facilities, the decision was made to move traffic to Akamai, who could help provide additional edge network capacity. At 17:26 UTC the command was initiated via our ChatOps tooling to withdraw BGP announcements over transit providers and announce AS36459 exclusively over our links to Akamai. Routes reconverged in the next few minutes and access control lists mitigated the attack at their border. Monitoring of transit bandwidth levels and load balancer response codes indicated a full recovery at 17:30 UTC. At 17:34 UTC routes to internet exchanges were withdrawn as a follow-up to shift an additional 40Gbps away from our edge.

就這樣而已,完全就是 PR 稿 XDDD

透過 memcached UDP (Port 11211) 的攻擊...

Cloudflare 發表了一篇關於公開的 memcached 伺服器,利用 UDP (Port 11211) 的放大攻擊:「Memcrashed - Major amplification attacks from UDP port 11211」。

用地圖展示後可以清楚看出來哪些區域受到的攻擊比較大:

另外 Shodan 上的資料頁可以參考,不過就不保證都有開 UDP/11211 了:

這種伺服器還是藏到內部網路啦...

ElastiCache 支援 r4.* 了

每年 AWS re:Invent 要到的時候就會有很多新的消息出來,Amazon ElastiCache 的團隊應該也是配合著這一波放出消息:「Amazon ElastiCache Now Supports the R4 Node Family」。

包括了 memcachedRedis 都支援了:

Amazon ElastiCache now supports R4 node types. R4 nodes are optimized for latency sensitive and memory intensive workloads. They come in six sizes, providing 12.3GiB to 407GiB of available in-memory capacity. By setting up a 15-shard cluster for Redis, you can scale up to 6.1TiB of in-memory capacity. For Memcached, you can set up a 20-node cluster to support up to 8.14 TiB in-memory workloads. Equipped with the Intel Broadwell processor, and improved networking, R4 node family offers superior performance over the popular R3 node family.

這兩個應用都是看記憶體吃飯的...

eBay 把 MongoDB 當 cache layer 的用法...

在「How eBay’s Shopping Cart used compression techniques to solve network I/O bottlenecks」這邊 eBay 描述了他們怎麼解決在 MongoDB 上遇到的問題,不過我看的是他們怎麼用 MongoDB,而不是這次解決的問題:

It’s easier to think of the MongoDB layer as a “cache” and the Oracle store as the persistent copy. If there’s a cache miss (that is, missing data in MongoDB), the services fall back to recover the data from Oracle and make further downstream calls to recompute the cart.

把 MongoDB 當作 cache layer,當 cache miss 的時候還是會回去底層的 Oracle 撈資料計算,這用法頗有趣的...

不拿 memcached 出來用的原因不知道是為什麼,是要找個有 HA 方案的 cache layer 嗎?還是有針對 JSON document 做判斷操作?

Netflix 開發的 Delayed Queue

原來這個叫做 Delayed Queue,難怪之前用其他關鍵字都找不到什麼資料... (就不講其他關鍵字了 XD)

Netflix 發表了他們自己所開發的 Delayed Queue:「Distributed delay queues based on Dynomite」。

本來的架構是用 Cassandra + Zookeeper 來做:

Traditionally, we have been using a Cassandra based queue recipe along with Zookeeper for distributed locks, since Cassandra is the de facto storage engine at Netflix.

但可以馬上想到不少問題,就如同 Netflix 提到的:

Using Cassandra for queue like data structure is a known anti-pattern, also using a global lock on queue while polling, limits the amount of concurrency on the consumer side as the lock ensures only one consumer can poll from the queue at a time.

所以就改放到 Netflix 另外開發的 Dynamite 上:

Dynomite, inspired by Dynamo whitepaper, is a thin, distributed dynamo layer for different storage engines and protocols. Currently these include Redis and Memcached. Dynomite supports multi-datacenter replication and is designed for high availability.

後端是 RedisMemcached 的系統,可以對抗整個機房從 internet 上消失的狀態。

在設計上則是「保證會跑一次」,也就是有可能會有多次的情況,用 Dyno Queues 系統的人必需要考慮進去:

4. At-least-once delivery semantics

雖然整篇講的頗輕鬆,但實際看起來還是很厚重... 暫時還是不會用吧 :o

Facebook 的 mcrouter

這也不知道積了多久,九月 Facebook 的文章,最近被同事提起來才又仔細看:「Introducing mcrouter: A memcached protocol router for scaling memcached deployments」。

memcached 應該當作普通的 cache layer 來用,拿來放掉了也沒關係的資料。如果掉了會很痛的資料應該丟到 Redis 或是 MySQL 這類 persistent storage。

但有些資料介於兩者之間,掉了會讓使用者用起來不太爽,但也不會死人... 於是總是想要在這上面做些改善。

Facebook 開發的 mcrouter 就可以拿來解這類問題。其中一個 scenario 是「寫的不多,但讀德很多」,寫的時候寫到所有機器上,但讀取時只挑一台:

而這個架構其實可以配合用在 memcached 的 HA 機制上。當有機器爛掉重開機變成空的 cache server 回來時可以暖機:

不過程式看起來並不好編,要先搞定 Facebook 的兩個 C++ 的套件後才能編...