← ყველა სტატია

RDS კავშირებზე უარს ამბობდა 10% CPU-ზე: ნაგულისხმევი პულები, გამრავლებული რეპლიკებზე

დატვირთვის ტესტები ~420 კავშირის ბრტყელ პლატოზე ჩერდებოდა და „უფრო დიდ ბაზას" ითხოვდნენ. ნამდვილი ჭერი იყო max_connections Npgsql-ის ნაგულისხმევი პულების წინააღმდეგ.

Gambchamp-ის სტეიჯ გარემოზე დატვირთვის ტესტებისას .NET სერვისებმა ბაზასთან კავშირების გახსნა ვეღარ შეძლეს. ჩემამდე მარტივი თხოვნა მოვიდა: „ბევრად დიდი ბაზა გვჭირდება”. RDS ინსტანსის CPU იმავე ფანჯრებში 97%-მდე ადიოდა, ამიტომ თხოვნა გონივრული ჩანდა.

გრაფიკის ფორმა სხვას ამბობდა.

პლატო და არა მრუდი

CloudWatch DatabaseConnections ათეულობით წუთის განმავლობაში 412–424-ზე ბრტყელ ხაზს აჩვენებდა. შვიდდღიანი საშუალო — დაახლოებით 7. ტევადობის პრობლემები მრუდს ღუნავს; ბრტყელი მწვერვალი ლიმიტში შეჯახებაა.

ინსტანსი იყო db.t4g.medium ნაგულისხმევი max_connections ფორმულით — ეს 399-ია. გამოკლებული superuser_reserved_connections=3 და reserved_connections=2 — ჩვეულებრივ როლებს 394 სლოტი რჩება.

საიდან 400 კავშირი

არც ერთ კავშირის სტრიქონში Max Pool Size არ იყო, ამიტომ Npgsql ნაგულისხმევს იღებდა: 100 კავშირი პროცესზე, კავშირის სტრიქონზე. არც PgBouncer, არც RDS Proxy; ყოველი პოდი პირდაპირ უერთდებოდა.

PostgreSQL-ის ლოგში პიკის ფანჯარაში ერთ ბაზაზე სამი კლიენტური IP იყო — ინტეგრაციული სერვისის სამი რეპლიკა — და კიდევ ერთი სხვა ბაზაზე. სამჯერ 100 პლუს 100 — დაახლოებით 400: აპლიკაციების ჭერი თითქმის ზუსტად.

მტკიცებულება — კონკრეტული FATAL ხაზი, ერთ ტესტში 18-ჯერ და შემდეგში 47-ჯერ:

remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role

ორი მაჩვენებელი, რომელიც გზას გიბნევს

  • CloudWatch პიკში max_connections-ზე მაღლა იყო (424 > 399). მეტრიკა ითვლის იმ ბექენდებსაც, რომლებიც დაფორკდნენ და მერე FATAL მიიღეს, ამიტომ შტორმში აჭარბებს.
  • too many clients already არასდროს გამოჩენილა. ეს შეტყობინება ნიშნავს, რომ დარეზერვებული სლოტებიც კი ამოიწურა. „Remaining connection slots are reserved” უფრო ადრე ჩნდება, როცა მხოლოდ ჩვეულებრივი სლოტები მთავრდება — ზუსტად ეს მოხდა.

პრაქტიკულად ამის დანახვა მხოლოდ პიკის ფანჯრით შეზღუდული Logs Insights-ით იყო შესაძლებელი: ჩვეულებრივი ფილტრი უსასრულო შენახვის ლოგ-ჯგუფზე უბრალოდ არ მთავრდებოდა.

დიდი ინსტანსი მხოლოდ კედელს წევს

კოლეგამ ინსტანსი db.m7g.large-მდე გაზარდა — ლიმიტი 832-მდე გაიზარდა (827 აპლიკაციებისთვის). ეს მარაგია, მაგრამ კედლის მოხსნა არა: ბაზის კავშირის სტრიქონებს 17 პოდი იყენებს, ანუ თეორიული მოთხოვნა მინიმუმ 17 × 100 = 1 700-ია.

ნამდვილი შეკეთება აპლიკაციების მხარესაა: თითოეული სერვისისთვის მკაფიო Max Pool Size, რომ „რეპლიკები × პულის ზომა” ჯამი აპლიკაციების სლოტების ~80%-ის ფარგლებში დარჩეს. ციფრებით გადავეცი სერვისების მფლობელებს. თუ რეპლიკების რაოდენობა HPA-ით დინამიკური გახდება, შემდეგი ნაბიჯი RDS Proxy ან PgBouncer-ია. ასევე ნოდების დაშბორდი დავამატე, რომ შემდეგმა ტესტმა ნოდების სატურაციაც აჩვენოს: იმ namespace-ის 20 დეპლოიმენტიდან 19-ს რესურსების ლიმიტი არ ჰქონდა.

დასკვნები

  • კავშირების ბრტყელი პლატო ლიმიტია და არა ტევადობის მრუდი. სანამ რამეს გაზრდი, FATAL-ის ტექსტი შეამოწმე.
  • Npgsql-ის პულები პროცესზე და კავშირის სტრიქონზეა: პოდს, რომელიც სამ ბაზასთან დადის, შეუძლია 300 კავშირი დაიკავოს.
  • კავშირები მკაფიოდ დაგეგმე: Σ(რეპლიკები × პულის ზომა) ≤ ~80% max_connections-ს გამოკლებული დარეზერვებული სლოტები.
  • CloudWatch DatabaseConnections უარების შტორმში შეიძლება max_connections-ს აჭარბებდეს. ეს ლიმიტის ბაგი არ არის.