مقالتنا عن توافق مع PostgREST توضح بالتفصيل ما يغطيه الخادم وظيفيًا (المرشحات، والتضمين، وRPC، وRLS) وما يتركه لك. هذا واحد يأتي من مكان آخر. إنه يوثق، باستخدام كود Aurabase ووثائق PostgREST الرسمية كمصادر، أين ولماذا وصل PostgREST فعليًا إلى مستوى ثابت على نطاق واسع. نحن لا نعيد هنا إنتاج بنك الأحمال الذي لم نديره بأنفسنا. تشرح منهجيتنا المعيارية لماذا لا يبدو الرقم المعزول، بدون بروتوكول منشور، موثوقًا بالنسبة لنا.
الأساسيات
- PostgREST نفسها خفيفة الوزن: 50 إلى 250 مليكور من وحدة المعالجة المركزية، و64 إلى 128 ميجابايت من ذاكرة الوصول العشوائي لكل نسخة متماثلة على مثيلات Aurabase المخصصة. إن إنتاجية HTTP الخام لا تعد أبدًا العامل المقيد في الإنتاج.
- السقف الحقيقي هو ميزانية اتصال Postgres:
PGRST_DB_POOL× النسخ المتماثلة. تم التحقق منها في كود Aurabase: 20 اتصالاً لكل مشروع على المستوى المخصص (10×2)، و4 على المستوى المشترك (2×2). يعد هذا اختيارًا متعمدًا لاستيعاب المزيد من المستأجرين في نفسmax_connections. Prefer: count=exactيفرض إجراء مسح MVCC باهظ الثمن على طاولات كبيرة. يوثق PostgREST بديلين أرخص:count=plannedوcount=estimatedبتكلفة إجمالية تقريبية.- يقوم الحد الأقصى
db-max-rows(1000 سطر افتراضيًا في Aurabase) باقتطاع الاستجابة دون الإبلاغ عنها فيContent-Range(يتم قياسها في الظروف الحقيقية، المفصلة أدناه). - بعد ترحيل DDL، تتم إعادة تحميل ذاكرة التخزين المؤقت لمخطط PostgREST بشكل غير متزامن. تعيد بوابة Aurabase المحاولة حتى 8 مرات (حوالي 3.5 ثانية تراكمية في أسوأ الحالات) قبل الاستسلام، وهو سلوك موثق مباشرة في التعليمات البرمجية.
ما الذي يقيسه معيار PostgREST وما لا يقيسه
يقيس اختبار إنتاجية HTTP على PostgREST بشكل أساسي Postgres، ونادرًا ما يقيس PostgREST. الخادم عبارة عن طبقة رقيقة من الترجمة أمام القاعدة. في الغالبية العظمى من عمليات التحميل في العالم الحقيقي، يهيمن على وقت الاستجابة استعلام SQL الذي تم تنفيذه، وليس العملية التي أنشأته.
يحتفظ مشروع PostgREST بمستودع مخصص لهذا الموضوع، PostgREST/postgrest-benchmark على GitHub، والذي يتتبع اختلافات الإنتاجية من إصدار إلى إصدار بدلاً من نشر رقم تسويقي معزول. لم نقم بأدائها أو إعادة نشرها هنا. تعتمد نتائجها على الأجهزة والحجم التخطيطي والسيناريو الذي تم اختباره، وهي بالضبط المتغيرات التي يتطلب بروتوكول القياس الخاص بنا توثيقها قبل اقتباس أي رقم.
أسفل PostgREST، يوجد pgbench الذي يقيس الطبقة المهمة حقًا: وقت معاملة SQL تحت التحميل المتزامن. هذه هي أداة قياس أداء PostgreSQL الرسمية (postgresql.org/docs/current/pgbench.html، تم الوصول إليها في 24 أغسطس 2026). بدلاً من إعادة إنتاج هذا البروتوكول هنا، توثق هذه المقالة أربعة قيود معمارية ملموسة لـ PostgREST في الإنتاج، ويتم التحقق من كل منها في كود مصدر Aurabase أو في وثائق المشروع الرسمية.
البصمة الفعلية لمثيل PostgREST في Aurabase
يتلقى كل مشروع محرك Aurabase Postgres نسختين متماثلتين مخصصتين من PostgREST، في موقع مشترك مع مجموعته. إن بيان Kubernetes الذي ينشرها يحدد موارد متواضعة.
ما تستهلكه هذه النسخ المتماثلة حقًا ليس وحدة المعالجة المركزية: إنها اتصالات بـ Postgres الأساسي. يتصل كل مثيل PostgREST مباشرة بالمثيل الأساسي (-rw)، دون المرور عبر مجمع PgBouncer المنشور للمستأجر. تم تفصيل هذا الاختيار بالفعل في مقالتنا حول توافق PostgREST: تتطلب آلية إعادة تحميل مخطط LISTEN/NOTIFY اتصالاً مستمرًا، غير متوافق مع المُجمِّع في وضع المعاملة. ما تضيفه هذه المقالة: ما هي التكلفة الفعلية، من حيث الاتصالات، وأين تبلغ ذروتها.
يختلف حجم هذا التجمع لكل نسخة متماثلة (PGRST_DB_POOL) عمدًا وفقًا لمستوى المشروع، الذي تم التحقق منه في k8s_tenant.rs، الوظيفة التي تبني بيان PostgREST لكل مشروع:
| تحمل | PGRST_DB_POOL / النسخة المتماثلة | النسخ المتماثلة | اتصالات / مشروع استيقظ |
|---|---|---|---|
| مخصص (ممتاز، A1) | 10 (افتراضي PostgREST) | 2 | 20 |
| مشترك (أسطول، مجاني/محترف/فريق) | 2 (Aurabase الافتراضي، منخفض) | 2 | 4 |
على المستوى المخصص، تم تخفيف القيد: يمتلك المشروع مجموعة CNPG الخاصة به، وبالتالي max_connectionsخاص به، مع عدم وجود جيران إضافيين. على المستوى المشترك، تشترك العديد من المشاريع من نفس المؤسسة في مجموعة واحدة: وهذا هو السياق الذي يجعل ميزانية الاتصالات حاسمة، وهو ما تم تطويره في القسم التالي.
تحدد ميزانية الاتصالات عدد المستأجرين الذين يعملون في نفس الوقت
في مجموعة Postgres المشتركة، ليس إنتاجية HTTP هي التي تحدد عدد المشاريع النشطة في وقت واحد. هذا هو عدد الاتصالات التي تحتفظ بها مثيلات PostgREST مفتوحة على المستوى الأساسي، مقارنةً بـ max_connectionsالمتوفر.
تستمد Aurabase هذه الميزانية مباشرة من حدود المجموعة الفعلية، والتي تم تحديدها في fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé، الطابق عند 1. الاحتياطي الثابت هو 10 اتصالات (المستخدم المتميز، ومدير مثيل CNPG، ومصدر المقاييس، وهامش المشرف المسؤول). بالنسبة للعيوب التي تم تسليمها (مجموعة من نسختين لكل نسخة متماثلة، أو نسختين متماثلتين، أو 4 اتصالات لكل مشروع تم تنشيطه)، يعطي الحساب ثلاث ميزانيات مختلفة اعتمادًا على مستوى تغيير حجم المجموعة.
المصدر: مشتق من fleet.rs::derive_wake_budget وwake_budget_for_org_plan، كود Aurabase، أعيد قراءته في 24 أغسطس 2026.
هذه الميزانية ليست حصة من المشاريع المملوكة: يمكن لمؤسسة team الاحتفاظ بـ 50 مشروعًا، معظمها خامل. هذا هو الحد الأقصى للتزامن : عدد المشاريع التي يمكنها الاحتفاظ باتصالات مفتوحة في نفس الوقت على المستوى الأساسي. لا يفشل التنبيه على الميزانية، بل يتم تأجيله حتى يعود مشروع شقيق إلى وضع السكون، ويتم التحقق منه في نفس الملف. تم توسيع موضوع تغيير الحجم max_connections نفسه في مقالتنا حول ضبط max_connectionsوالمفاضلة المخصصة/المتبادلة ككل في القاعدة المخصصة مقابل القاعدة المشتركة.
لماذا تفضل: count=exact يبطئ الاستعلام على جدول كبير
إن المطالبة بإجمالي دقيق يجبر Postgres على حساب الصفوف المرئية للنتيجة التي تمت تصفيتها مع كل استعلام، وهي تكلفة تنمو مع الجدول، وليست عملية مجانية.
لا يحتفظ PostgreSQL بأي عدادات صفوف مفهرسة خارج الصندوق. ضمن MVCC، تعتمد رؤية الصف على المعاملة التي تقرأه. لذلك يجب على COUNT(*) بالضبط زيارة الصفوف المرشحة بدلاً من قراءة قيمة محسوبة مسبقًا. يعد هذا قيدًا هيكليًا موثقًا جيدًا في نظام Postgres البيئي، بما في ذلك موردي التحليلات مثل ClickHouse، الذين يقارنون عداداتهم التقريبية بسلوك معاملات Postgres.
يوثق PostgREST استراتيجيات العد الثلاثة هذه محليًا (postgrest.org، تم الوصول إليه في 24 أغسطس 2026). تضمن استراتيجية exact إجمالي سعر الفحص. planned يُرجع تقديرًا مجانيًا تقريبًا من مخطط الاستعلام. estimated يقوم بالتبديل تلقائيًا بين الاثنين بناءً على الحد. الاختيار ليس تجميليًا: فالترقيم الذي يتطلب count=exact على جدول يضم عدة ملايين من الصفوف يدفع ثمن هذا الفحص في كل صفحة، حتى عندما لا يراجع المستخدم الصفحة الأخيرة مطلقًا.
اقتطاع صفوف db-max غير مرئي بدون العد = بالضبط
يمكن أن يؤدي استخدام رأس الصف إلى اقتطاع استجابة PostgREST دون أي إشارة في النص أو الرؤوس، ما لم يتم طلب إجمالي دقيق بشكل صريح. لقد قمنا بقياسها في ظروف حقيقية على مثيل Aurabase مخصص، وهو أمر غير مفترض.
في جدول اختبار مكون من 10 صفوف مع PGRST_DB_MAX_ROWS=5، يعرض PostgREST v12.2.3 نفس رأس Content-Range تمامًا في حالتين مختلفتين تمامًا:
| الاستعلام | الخطوط المقدمة | نطاق المحتوى | ميتا (أوراباسي) |
|---|---|---|---|
| ?limit=50 (بدون إحصاء) | 5/10 حقيقي | 0-4/* | {} |
| ?limit=50&count=exact | 5/10 حقيقي | 0-4/10 | {المجموع: 10} |
بدون count=exact، لا يمكن تمييز استجابة 5 صفوف عن جدول يحتوي بالفعل على 5 فقط: Content-Range: 0-4/* يصف الصفوف المقدمة، ولا يتم تطبيق الحد أبدًا. لا يظهر الحد الأقصى الفعلي في أي مكان في هذه الحالة، ويتم قياسه مباشرةً على مسار PostgREST الخاص بحزمة Aurabase SDK.
إذا قام نشر PostgREST بتعيين db-max-rows (إعدادات Aurabase الافتراضية هي 1000)، فقد يكون العميل الذي يقارن data.length بالحد المطلوب لاكتشاف صفحة كاملة مخطئًا. يظهر الخطأ بمجرد أن يكون سقف الخادم أقل من هذا الحد. الإشارة الوحيدة الموثوقة هي مقارنة عدد الخطوط المستلمة مع total التي يتم إرجاعها بواسطة count=exact، والتي تؤدي مباشرة إلى مقايضة التكلفة الموضحة في القسم السابق.
إعادة تحميل ذاكرة التخزين المؤقت للمخطط بعد الترحيل
يحتفظ PostgREST بمخطط Postgres في الذاكرة عند بدء التشغيل. بعد DDL (إنشاء جدول، إضافة عمود)، يجب إعادة تحميل ذاكرة التخزين المؤقت هذه قبل أن يستجيب المسار الجديد، وتكون عملية إعادة التحميل هذه غير متزامنة.
الكتابة التي تصل إلى هذه النافذة قد تتلقى 404 عابرًا (ذاكرة التخزين المؤقت لم يتم تحديثها بعد)، على الرغم من أن الجدول موجود بالفعل على جانب Postgres. تمتص بوابة Aurabase هذا من خلال حلقة إعادة محاولة محدودة، تم التحقق منها في postgrest_proxy.rs: ما يصل إلى 8 محاولات، وزيادة التراجع (250 مللي ثانية بالإضافة إلى 100 مللي ثانية لكل محاولة)، 3.5 ثانية تراكمية في أسوأ الحالات. تؤثر هذه الآلية فقط على عمليات الكتابة، ولا تقرأ أبدًا.
لا تصدر البوابة أي إشارة إعادة تحميل: إنها تنتظر فقط. المشغل الحقيقي الوحيد هو pg_notify('pgrst', 'reload schema') الصادر عن خدمة قاعدة البيانات على مسار DDL. إذا نسي مسار الترحيل إرسال هذه الإشارة، فإن المحاولات الثماني تستنفد ذاكرة التخزين المؤقت التي لن تتغير أبدًا، وهو خطر موثق كما هو في تعليق الكود، وليس مخفيًا.
بالنسبة لنشر PostgREST المستضاف ذاتيًا، يتم تعميم الدرس. يجب أن يؤدي كل مسار DDL في تطبيقك إلى تشغيل عملية إعادة التحميل، عبر NOTIFY أو إشارة SIGUSR1 إلى العملية. وبخلاف ذلك، فإن الترحيل ينتج عنه ارتفاع في وقت الاستجابة p99 متخفيًا في شكل أخطاء متقطعة مباشرة بعد النشر.
ما شرائح الهندسة المعمارية، وليس الإنتاجية الخام
تشترك القيود الأربعة الموثقة هنا في شيء واحد مشترك: لم يتم رؤية أي منها في اختبار إنتاجية HTTP معزول، ومع ذلك تحدد الأربعة جميعًا ما إذا كان نشر PostgREST يمتد إلى الإنتاج.
- ميزانية الاتصال: تحدد عدد المستأجرين النشطين في نفس الوقت على مجموعة مشتركة، بغض النظر عن الإنتاجية لكل مستأجر.
- تكلفة COUNT بالضبط: تنمو مع الجدول، وليس مع الحمل؛ تم تجاوزه بـ
planned/estimated. - الاقتطاع الصامت: لا يزال من الممكن لغطاء الصف الذي تم تكوينه بشكل صحيح أن يقطع الترحيل ذي الأدوات السيئة.
- إعادة تحميل المخطط: نافذة زمن الوصول بعد كل عملية ترحيل، تكون محدودة إذا كانت إشارة إعادة التحميل موصلة جيدًا، وغير محدودة بخلاف ذلك.
سواء كنت تختار بين PostgREST المستضافة ذاتيًا، أو طبقة GraphQL بنمط Hasura، أو واجهة برمجة التطبيقات المخصصة، فإن هذه المحاور الأربعة تعد نقطة مقارنة أفضل من شكل الطلب/الطلبات المعزولة. انظر مقارنتنا PostgREST vs Hasura vs custom API. إن اختيار المجمع الذي يقف أمام قاعدة البيانات الخاصة بك له نفس القدر من الأهمية: مقارنتنا PgBouncer vs Supavisor vs PgCat تفاصيل لماذا لا يمكن لـ PostgREST المرور عبر المجمع في وضع المعاملة.
الأسئلة الشائعة
ماذا تتذكر
لا ينقطع PostgREST أبدًا تحت تحميل HTTP وحده: فبنيته بسيطة جدًا لذلك. ما يكسر الإنتاج هو ما يحيط به: كم عدد الاتصالات التي تظل نسخها المتماثلة مفتوحة، وكم التكلفة الإجمالية الدقيقة. ويتضمن ذلك أيضًا ما إذا كان الاقتطاع سيظل مرئيًا، ومدة استمرار النافذة بعد الترحيل.
هذه القيود الأربعة ليست خاصة بـ Aurabase: فهي تنطبق على أي نشر لـ PostgREST، مستضاف ذاتيًا أو مُدار. ما يوضحه هذا الرمز هو كيف أن النشر متعدد المستأجرين يجعلهم واضحين بدلاً من تركهم مفاجئين في الإنتاج.