PostgreSQL 'Too Many Connections' Xatosi: PgBouncer Yechimi

Muallif: Javohir Abdullayev · · Server va DevOps

PostgreSQL 'Too Many Connections' Xatosi: PgBouncer Yechimi

Kirish: Muammoning Kutilmaganda Paydo Bo'lishi

Har bir backend dasturchi yoki DevOps muhandisi hayotida hech bo'lmaganda bir marta ushbu qo'rqinchli xatoga duch keladi: FATAL: sorry, too many clients already. Yaqinda biz yurgazayotgan o'rta hajmdagi e-tijorat loyihasida aynan shu holat yuz berdi. Marketing aksiyasi e'lon qilingach, saytga tashrif buyuruvchilar soni keskin oshdi. Natijada Django API so'rovlarga javob bermay qoldi, foydalanuvchilar ekranida 500 va 502 xatoliklari paydo bo'ldi.

Server monitoringini tekshirganimizda, CPU yoki RAM to'lmagan edi. Muammo ma'lumotlar bazasida edi: PostgreSQL o'ziga ruxsat berilgan ulanishlar (connections) chegarasiga yetib borgan va yangi so'rovlarni qabul qilmay to'xtab qolgan edi.

Nega PostgreSQL Ulanishlari Tez To'lib Qoladi?

PostgreSQL arxitekturasi har bir yangi mijoz ulanishi uchun alohida jarayon (process) ochadi. Har bir jarayon serverdan sezilarli darajada operativ xotira (RAM) talab qiladi (odatda 5-10 MB yoki undan ko'p). Standart holatda PostgreSQL-da max_connections = 100 qilib belgilangan bo'ladi.

Bizning Django loyihamiz Gunicorn yordamida ishlayotgan edi. 8 ta worker, har birida bir nechta thread va fonda ishlayotgan Celery workerlari bir vaqtning o'zida bazaga ulanish hosil qildi. Foydalanuvchilar ko'payishi bilan har bir Gunicorn workeri o'z ulanishini ochiq ushlab turdi. Natijada 100 ta ulanish bir necha daqiqa ichida tugadi.

Noto'g'ri Yechim: max_connections Sonini Oshirish

Birinchi fikr oddiy bo'ldi: postgresql.conf fayliga kirib, max_connections qiymatini 500 yoki 1000 ga ko'tarish. Ammo bu jiddiy xato hisoblanadi. PostgreSQL uchun yuzlab faol jarayonlarni boshqarish operatsion tizim kontekst almashinuvi (context switching) tufayli keskin sekinlashadi va oxir-oqibat butun serverning ishdan chiqishiga (crash) olib keladi.

Haqiqiy Yechim: PgBouncer Connection Pooler

Ushbu muammoni professional tarzda hal qilish uchun PgBouncer o'rnatishga qaror qildik. PgBouncer — bu PostgreSQL uchun juda yengil va tezkor ulanishlar boshqaruvchisi (connection pooler). U ilova va ma'lumotlar bazasi o'rtasida vositachi sifatida ishlaydi.

PgBouncer 3 xil pooling rejimiga ega:

  • Session pooling: Mijoz ulangan paytda sessiya tugaguncha ulanish beriladi.
  • Transaction pooling: Har bir tranzaksiya tugashi bilan ulanish hovuzga (pool) qaytadi. Bu eng samarali va keng tarqalgan rejimdir.
  • Statement pooling: Har bir SQL buyrug'i tugashi bilan ulanish bo'shaydi (tranzaksiyalarni qo'llab-quvvatlamaydi).

Biz Django ilovasi uchun Transaction pooling rejimini tanladik. Bu orqali 500 ta veb-so'rov PostgreSQL-dagi atigi 20-30 ta faol ulanish orqali navbatma-navbat muammosiz bajarilishi ta'minlanadi.

PgBouncer O'rnatish va Sozlash

Ubuntu serverida PgBouncer o'rnatish juda oson kechadi:

sudo apt update
sudo apt install pgbouncer -y

O'rnatilgach, /etc/pgbouncer/pgbouncer.ini konfiguratsiya faylini tahrirlaymiz:

[databases]
my_database = host=127.0.0.1 port=5432 dbname=my_database

[pgbouncer]
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5

Foydalanuvchi parollarini /etc/pgbouncer/userlist.txt fayliga kiritamiz:

"db_user" "md5_parol_hesh_qiymati"

PgBouncer xizmatini ishga tushiramiz va tekshiramiz:

sudo systemctl enable pgbouncer
sudo systemctl restart pgbouncer

Django Loyihasini PgBouncer-ga Ulash

Django loyihamiz sozlamalarida (settings.py) ma'lumotlar bazasi portini 5432 dan PgBouncer porti bo'lgan 6432 ga o'zgartiramiz:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'my_database',
        'USER': 'db_user',
        'PASSWORD': 'secret_password',
        'HOST': '127.0.0.1',
        'PORT': '6432',
    }
}

Muhim eslatma: Transaction pooling rejimida Django-ning CONN_MAX_AGE parametrini 0 qilib qoldirish kerak, aks holda kutilmagan sessiya xatolari kelib chiqishi mumkin.

Natija va Xulosa

PgBouncer joriy etilgandan so'ng qayta yuklama sinovlarini (load testing) o'tkazdik. Natijalar hayratlanarli bo'ldi:

  • Bir vaqtda kelgan 800 ta parallel so'rov paytida ham PostgreSQL atigi 25 ta ulanish bilan barqaror ishladi.
  • FATAL: sorry, too many clients already xatosi butunlay yo'qoldi.
  • Server xotirasidan foydalanish hajmi 40 foizga kamaydi.
  • API javob qaytarish vaqti (latency) barqaror 45-60 ms oralig'ida saqlandi.

Agar sizning Python yoki Django loyihangiz ma'lumotlar bazasi yuklamasi sababli sekinlashayotgan bo'lsa, PostgreSQL parametrlarini ko'r-ko'rona oshirish o'rniga PgBouncer o'rnatishni tavsiya qilaman. Bu arzon, ishonchli va yuqori samaradorlik beruvchi yechimdir.

Teglar: #PostgreSQL #PgBouncer #Django #DevOps #Backend