الدالة pool.connect() تستعير اتصالًا، وعلى الشيفرة أن تُعيده بـ client.release(). في 4.1 يخرج
مسار الخطأ return res.status(422) من الدالة دون ROLLBACK ولا release(). فكل طلب مرفوض يحتفظ
باتصال، وسط معاملة مفتوحة، مع قفل FOR UPDATE على سطر المخزون. وبعد 20 رفضًا يفرغ المُجمّع: فينتظر كل
طلب 5 ثوانٍ (connectionTimeoutMillis) ثم يفشل بعبارة «timeout exceeded when trying to connect».
كانت متغيرات البيئة كاملة، وكان PostgreSQL ما زال يقبل الاتصالات (فقد اتصلت به عبر psql): إنه
مُجمّع التطبيق هو الذي نضب.
تمّ حلّ الحادث
التحليل والإصلاح
التصحيح: تغليف المعاملة داخل try { … } catch { ROLLBACK } finally { client.release() }
(أو إلغاء 4.1 بـ git revert)، ثم pm2 reload hanout-api. وفي حالة الطوارئ، يخفّف
pg_terminate_backend على الجلسات «idle in transaction» الضغط، لكنه لا يصلح شيئًا.
حتى لا يتكرر ذلك:
- للاستعلام الواحد، استعمال
pool.query()التي تُعيد الاتصال تلقائيًا؛ وحصرpool.connect()في المعاملات، دائمًا معfinally؛ - من جهة PostgreSQL:
idle_in_transaction_session_timeout = '30s'لدور الواجهة البرمجية؛ - مراقبة المُجمّع (
pool.totalCountوpool.idleCountوpool.waitingCount) والتنبيه عند وجود طلبات منتظرة؛ - اختبار تكامل يرسل 50 طلبًا مرفوضًا متتاليًا ثم يتحقق من أن الواجهة ما زالت تجيب.