ขอขอบคุณภาพตัวอย่าง

Mongo Connection Pool ภัยร้ายที่ใกล้ตัว

Puttapong Khemcharoen
Scale360 Engineering
2 min readJun 25, 2018

--

สวัสดีครับ…วันนี้ผมจะมาแชร์ประสบการณ์เกี่ยวกับ Connection Pool ของ Mongo ที่ทุกคนเครมว่าดี แต่หากใช้ไม่พึงระวังก็อาจนำไปสู่ต้นเหตุของปัญหา

สรุปฉบับย่อ ให้เลื่อนลงไปด้านล่างสุดได้เลยนะครับ

เนื้อหาต่อจากนี้ไป เป็นฉบับเต็มสำหรับคนชอบ detail

Connection Pool คือการทำ cache ของการเชื่อมต่อฐานข้อมูล ข้อดีเด่นๆที่เราใช้กันก็คือ เพื่อลด overhead mongo connection นะครับทำให้เราสามารถส่งข้อมูลหากันได้ไวขึ้นเพราะ ไม่ต้องทำการสร้าง authenticated conection DB ใหม่ อ่านเพิ่มเติมคลิ๊กที่ลิ้งได้เลยนะครับพี่เขาเขียนไว้ดีมากอยุ่แล้ว

เอาละในเมื่อเรารู้แล้วว่ามันดียังไง ทีนี้ผมจะมาอธิบายการทำงานของ Mongo client คร่าวๆที่เราใช้เชื่อมต่อให้นะครับ โดยปกติแล้ว Mongo client จะจัดการกับ connection pool ให้อัตโนมัติอยุ่แล้ว โดยเวลาเราเชื่อม Mongo Client เนี่ยเรามักจะใช้การเชื่อมแบบนี้

mongoClient = new MongClient(URI, connectionOptions);

syntax อาจจะต่างกันไปแต่ละภาษานะครับ แต่โครงสร้างจะเหมือนกัน หากเราใส่แค่ URI ไม่ใช่ connectionOptions มันจะทำการสร้าง default connection pool settings ให้อัตโนมัติ โดยขึ้นอยุ่กับ lib หรือ ภาษาที่ใช้ เช่น

  • Node จะให้ max pool คือ 5
  • Scala, Python จะให้ max pool คือ 100

นอกเหนือจาก Maxpool แล้วจะยังมี MaxConnectionIdleTime, และ MaxConnectionLifeTime ที่บางภาษาตั้งค่า default เป็น No limit ไว้ให้

ผมจะขออธิบายเพิ่มเติมเกี่ยวกับสองตัวนี้นะครับ คือ

  • MaxConnectionIdleTime คือ เวลาของแต่ละ conection pool ที่จะอยุ่ได้ก่อนปิดตัวเอง โดยนับจากวินาทีที่มันว่างงานไม่มีการถูกเรียกใช้
  • MaxConnectionLifeTime คือ เวลาสูงสุดของแต่ละ connection pool ที่จะอยุ่ได้ก่อนปิดตัวเอง โดยนับจากวินาทีแรกที่สร้าง connection ขึ้นมา

หลักจากที่เรารู้จัก Connection Pool settings ที่สำคัญทั้งสามตัวมาแล้ว ก็คงจะมีคำถามกันมาบ้างว่า ปัญหาจริงๆมันคืออะไร ?

ปัญหาของ Connection pool แบบ default ก็คือ No limit MaxConnection

ผมอยากให้เพื่อนๆนึกถึง Service ของคุณที่มีคนใช้งานเยอะขึ้นตลอดเวลา Pool ของคุณจะมีการทำ Cache Connection เพิ่มขึ้นไปเรื่อยๆ จาก 2 -> 3 -> … -> จนถึง N Connections โดยที่ไม่มีการลดลงเลย มันจะเป็นการเปลืองทรัพยากรโดยใช่เหตุครับ เช่น กรณีที่คุณเช่า MongoDB Atlas ซึ่งจะมี limit connection ให้ตาม ราคา package ที่คุณจ่าย ซึ่งช่วง Peek time ของแต่ละ Service จะมีการจอง connection pool มากตามจำนวน request ต่างๆที่เข้ามาพร้อมกัน แต่ดันไม่เคยปิดหรือลด connection pool ที่ไม่ได้ใช้แล้ว โดยเมื่อเราเอาจำนวนของ connection แต่ละ service ที่จองทั้งหมดมารวมกัน มันอาจจะเต็ม limit package ที่เราซื้อได้ ทำให้ connection ใหม่ๆที่เข้ามา อาจจะนำไปสู่การทำให้ database ล่ม เนื่องมาจากไม่มีช่อง connection ให้เชื่อม เพราะถูกจองไปทำ pool หมดแล้ว ผมจึงแนะนำว่าเราควรที่จะ ตั้งค่า connection pool ให้ดี ซึ่งต้องดูจากการใช้งานของ Service นั้นๆ ให้เหมาะสมกับการใช้งาน โดยการเซ็ต min, max connection pool และควรมีการเซ็ต maxConnectionIdleTime และ MaxConnectionLifeTime เพื่อให้ทำการลด connection pool ที่ไม่ได้ใช้เป็นเวลานานๆลงเพื่อป้องกันการจอง connection ที่นานเกินความจำเป็น เป็นสาเหตุให้นำไปสู่ปัญหา connection เต็ม

หมายเหตุ : กรณีที่คุณ scale up service ของคุณ connection pool ต้องคิดโดยคูณ จำนวนตัวที่ scale up ไปด้วย เนื่องมาจาก pool จะนับจาก mongo client ที่เปิดเชื่อม ของแต่ละครั้ง scale up 3 ตัวก็ 3 mongo client และหากคุณบอกว่า Database คุณเป็น clustering ให้คุณคิด limit จากตัว master จะปลอดภัยสุด สมมุติเช่น คุณมี DB cluster 3 ตัว ตัวละ 700 connections ค่าที่คุณรับได้สูงสุดไม่ใช่่ 2100 แต่จะอยุ่ราวๆที่ 700 ครับ เพราะถ้าตัว master มี connection เต็ม request ที่เข้ามาจะไม่ถูกส่งกระจายไปที่ตัวอื่นครับ

เพิ่มเติม สำหรับการสร้าง Mongo Client เราควรที่จะสร้างมันแค่ครั้งเดียวก่อนการ start application แล้ว reuse collection เพื่อประหยัดจำนวน connection ตั้งต้นครับ และหากคุณต้องเชื่อม Mongo Client ทุกครั้ง ที่ต้องใช้งาน connection pool จะไม่มีความหมายอะไรเลย เพราะมันจะทำการสร้างใหม่ทุกครั้ง ทำให้ MongoClient ไม่สามารจัดการ pool ที่ถูกสร้างจากอันอื่นได้

คำสั่งสำหรับแสดง connection mongo

lsof -i|27017 | grep -E "ESTABLISHED" จะทำการโชว์ connection ที่เปิด

Bash Script สำหรับการแสดง connection mongo ตลอดเวลา โดยสร้างไฟล์ .sh แล้วใส่คำสั่งด้านล่าง เวลารันก็เรียก ./{ชื่อไฟล์}

#!/bin/bashwhile :; doecho -ne " MongoDB established $(sudo lsof -i:27017 | grep -E "ESTABLISHED" | wc -l) connections.\r"done

ขอขอบคุณ ตัวอย่าง Script จาก พี่เจม Back-end

สรุป ฉบับย่อ

  1. Connection Pool Default นำไปสู่ ปัญหา การจอง Connection เกินความจำเป็นที่ใช้ ทำให้ Database ล่มได้เนื่องจากไม่มี ช่อง connection ให้เชื่อม
  2. เราสามารถแก้ปัญหาจากข้อ 1 ได้ ดังนี้
  • Min Connection Pool จำนวนน้อยที่สุดที่จะเปิด connection pool ค้างไว้
  • Max Connection Pool จำนวนมากที่สุดที่จะเปิด connection pool ได้
  • Max Connection Life Time เวลาของแต่ละ connection pool ที่จะอยุ่ได้ก่อนปิดตัวเอง โดยนับจากวินาทีแรกที่สร้าง
  • Max Conection Idle Time เวลาของแต่ละ conection pool ที่จะอยุ่ได้ก่อนปิดตัวเอง โดยนับจากวินาทีที่มันว่างงาน

3. Mongo Client ควรถูกเรียกใช้งานครั้งเดียวเมื่อ start application เพื่อป้องกันการสร้าง connection เกินความจำเป็น และเพื่อให้สามารถใช้ Connection pool ได้

เขียนโดย Puttapong Khemcharoen 25/06/2018

อ่านบทความของผมในหัวข้ออื่นๆเพิ่มเติมได้ที่นี่ Puttapong Khemcharoen

--

--

Puttapong Khemcharoen
Scale360 Engineering

Jobs is a hat that you wear, it does not mentally who you are.