Congestion collapse in AWS Lambda

น
llun
Published in
1 min readJun 15, 2019

ช่วงนี้กำลังทำระบบ Worker queue ใหม่ที่รับ event จากระบบหน้าบ้านต่างๆ เพื่อมาประมวลผลแล้วเก็บเข้าฐานข้อมูล ระบบก็ทำงานด้วยดีมาหลายเดือนแต่สัปดาห์ที่แล้วมีน้องที่ทำงานเพิ่ม event ใหม่เข้าไปแล้วมาปรึกษาว่าทำไม event มันไม่ส่งมาที่ระบบที่น้องทำอยู่ ก็ช่วยกันหาอยู่ว่า event มันไปติดอยู่ตรงไหน จนพบว่ามันไปติดตรง Buffer queue (SQS) ที่โตขึ้นมหาศาล พอขุดต่อก็เจอว่าที่มันโตเพราะประเภท event ที่เพิ่มขึ้นเรื่อยๆ ทำให้ Lambda ที่ตั้ง max concurrency ไว้มันไม่พอ ก็เลยถามเพื่อนที่ทำตอนแรกว่าทำไมถึงใส่ max concurrency ไว้ก็บอกว่าถ้าไม่ใส่ worker ที่ทำงานมันได้ performance ต่ำลงแต่เพื่อนก็ไม่รู้ว่าสาเหตุทำไมมันถึงต่อลง ไล่มาถึงตรงนี้ตอนที่หาสาเหตุก็ยังไม่ได้นึกถึง Congestion Collapse หรอกเพราะไม่เคยได้ยินอะไรแบบนี้ใน pattern นี้เลย เลยได้ไล่ดู logs ว่าจริงๆ ปัญหามันคือะไร

ระบบ Worker ที่ทำอยู่มีประมาณนี้ Event เข้ามาเก็บที่ SQS จากนั้นส่งไปที่ Lambda เอา event มาทำนู่นนี่แล้วก็เก็บลง DynamoDB

ระบบ Worker ที่ทำอยู่ก็ไม่มีอะไรซับซ้อน SQS > Lambda > DynamoDB ทุกอย่างตั้ง Autoscale หมด DynamoDB ทำแบบ Provision ไว้ไม่ได้ใช้ ondemand เพราะค่า write แพงกว่าเยอะ ส่วน AWS Lambda จุดขายคือมัน Autoscale เองตั้งแต่ต้นอยู่แล้ว

ระบบก็ดูไม่มีปัญหาอะไรจนกระทั้งมี Burst event ที่เข้ามา SQS จะยิงเข้า AWS Lambda ให้ scale ขึ้นทันทีตามจำนวน event / message batch จากนั้น lambda ก็จะพยายาม write ต่อไปที่ dynamodb ที่ก็ต้องคอย scale ตาม ปัญหาเกิดคือ DynamoDB Scale ตามไม่ทัน AWS Lambda ก็จะพยายาม retry ซึ่งระหว่าง retry event ก็เข้ามาอยู่ SQS ก็ยิ่งเพิ่ม AWS Lamda ขึ้นไปอีกจนเต็มที่ Lambda จะ scale ได้ (default limit 1500 concurrency per account per region) ยิ่ง burst นาน ปัญหายิ่งเพิ่มเพราะจำนวนที่ต้อง retry จะเพิ่มขึ้นเรื่อยๆ จนกว่า DynamoDB จะ scale ตามขึ้นมาทันได้ แต่ตอนนี้ท่อ lambda ก็เต็มไปก่อนแล้ว

วิธีที่เพื่อนแก้ด้วยการจำกัดจำนวน Concurrency เลยช่วยให้ DynamoDB scale ทันแต่ว่า ตัว max concurrency เองก็ต้อง scale ตามด้วยถ้าต้องการ drain SQS ให้ทัน เพราะถ้า drain ไม่ทันก็จะกลายเป็น Bufferbloat ที่เพื่อนอีกคนเจอแทน (event ไม่ยิงเข้ามาที่ lambda เพราะมันติดอยู่ตรง unlimited buffer ที่ SQS)

ช่วงนี้ก็แก้แบบลวกๆ ไปก่อนด้วยการใส่ max concurrency แบบสูงๆ ไว้ (แบบไม่กระทบกับ function อื่น)​ แต่ก็สงสัยว่าทำไม workers queue ระบบอื่นไม่เจอปัญหาอะไรแบบนี้ ข้อสังเกตุส่วนตัวก็คิดว่า ระบบอื่นมันไม่มี fast scaling หรือ high scaling (scale up ทีเดียว 1000 parallel) เหมือน lambda แล้วค่อยๆ ปรับจำนวน worker เอาตาม resource ที่มี (CPU/Memory) แต่ lambda มันดันทำให้ตรงนี้ง่าย/หายไปอีก เจอ burst ทีอัดตูมเดียวพังเป็นแถบไปเลย

--

--

น
llun
Editor for

Coder, mostly in Javascript/Typescript. Posting in https://llun.me