لماذا الترقية الآن — نهاية دعم PG 15 وإشارة Supabase
يصل PostgreSQL 15 إلى نهاية دعمه الرسمي في نوفمبر 2026. بعد هذا التاريخ، لن تصدر مجموعة تطوير PostgreSQL Global Development Group أي تصحيحات أمنية أو إصلاحات للأخطاء.
لكن الترقية لا تحتمل الانتظار حتى نوفمبر لسبب أكثر إلحاحًا: قامت Supabase بالتحول إلى صورة مرجعية postgres:17 في يونيو 2026 (النقاش رقم 46080 في سجل التغييرات العام). البيئات التي تشير إلى image: supabase/postgres دون تثبيت إصدار تلقّت PG 17 عند تنفيذ docker compose pull التالي، دون أي تحذير مرئي في سجلات البدء.
إذا كانت بيئتك تُثبّت بالفعل postgres:15، فأنت محمي على المدى القصير. أما إذا كانت تشير إلى postgres:latest أو وسم Supabase متحرك، فقد تكون قد انتقلت بالفعل دون علمك. التحقق يتم بأمر واحد:
docker exec <container> psql -U postgres -c 'SELECT version();'لا تفترض — تحقق.
ما الذي يتغير فعلًا بين PG 15 وPG 17
- تشديد
search_pathمنذ PG 15.1 (ADV-2022-00007): لم يعد مخططpublicمدرجًا فيsearch_pathالافتراضي للأدوار غير المشرف. أي استعلام يفترضpublic.my_tableدون تأهيل صريح قد يعيد 0 صف بصمت بدلًا من إرجاع خطأ. pg_dumpينتج نسخًا احتياطية غير متوافقة للأسفل: لا يمكن استعادة نسخة PG 17 على PG 15. العكس مدعوم، وهذا هو اتجاه الترقية. احتفظ بنسخ PG 15 الاحتياطية لمدة 30 يومًا على الأقل بعد التحويل.- رفع القيمة الافتراضية لـ
wal_levelإلىlogicalفي PG 16+: إذا كانpostgresql.confيُجبرwal_level = minimal، سيتغير السلوك بعد الترقية. قد تصبح فتحات النسخ المتماثل المنطقي الموجودة غير صالحة. - حذف الدوال القديمة:
lo_import،lo_exportوبعض دوالpg_catalogتم حذفها أو إعادة تسميتها بين PG 15 وPG 17 — تحقق من الدوال المستخدمة في امتداداتك المخصصة. -
pg_stat_statementsيعدّل تطبيع الاستعلامات: لوحات Grafana/PgHero التي تجمع بـ query fingerprint ستبدأ من الصفر بعد الترقية. pg_partman(إدارة التقسيم) يتطلب الإصدار ≥ 5.x لـ PG 17 — الإصدار 4.x غير متوافق.timescaledbيتطلب الإصدار ≥ 2.13 لـ PG 17; الإصدارات السابقة ترفض التحميل وتوقف بدء تشغيل الحاوية.
جرد الامتدادات: ما يمر وما يكسر
قبل أي ترقية، استخرج قائمة الامتدادات النشطة في كل قاعدة بيانات:
docker exec <pg15_container> psql -U postgres -c \
"SELECT datname, extname, extversion FROM pg_extension e JOIN pg_database d ON d.oid = e.extnamespace ORDER BY datname, extname;"الامتدادات المتوافقة دون إجراء: pgcrypto، uuid-ossp، hstore، ltree، citext، pg_trgm، unaccent، intarray، tablefunc.
الامتدادات التي تتطلب تحديثًا: pgvector (الترقية إلى ≥ 0.7.0 لـ PG 17)، PostGIS (≥ 3.4 لـ PG 17)، TimescaleDB (≥ 2.13 إلزامي)، pg_partman (≥ 5.0 إلزامي).
أمر التحقق بعد الترقية:
docker exec <pg17_container> psql -U postgres -d mydb \
-c 'SELECT extname, extversion FROM pg_extension ORDER BY extname;'قارن عمود installed_version مع جرد PG 15 — الامتداد المفقود (NULL) يشير إلى مشكلة في التحميل يجب حلها قبل التحقق من صحة الترقية.
إجراء الترقية بدون توقف: pg_dump/pg_restore
تثبيت إصدار صورة PG 15
قبل أي عملية، ثبّت الصورة الحالية في ملف docker-compose.yml الخاص بك بوسمها الدقيق:
docker inspect <pg15_container> --format '{{.Config.Image}}'
# مثال: postgres:15.7
# حدّث Compose:
# image: postgres:15.7أودع هذا التغيير في نظام إدارة الإصدارات قبل المتابعة. لا يمكن إجراء الترقية في مكانها: يرفض PG 17 البدء على بيانات PGDATA الخاصة بـ PG 15.
التقاط النسخ الاحتياطية العالمية وقواعد البيانات
صدّر أولًا الكائنات العالمية (الأدوار، والمساحات)، ثم كل قاعدة بيانات على حدة:
# الكائنات العالمية
docker exec <pg15_container> pg_dumpall \
-U postgres \
--globals-only \
> backup_globals.sql
# كل قاعدة بيانات تطبيق
docker exec <pg15_container> pg_dump \
-U postgres \
--no-owner \
--no-acl \
--format=custom \
--file=/tmp/mydb_pg15.dump \
mydb
docker cp <pg15_container>:/tmp/mydb_pg15.dump ./mydb_pg15.dumpتحقق من سلامة النسخة الاحتياطية قبل المتابعة:
pg_restore --list mydb_pg15.dump | head -20تشغيل حاوية PG 17 بالتوازي على منفذ مختلف
أضف خدمة ثانية في docker-compose.yml لـ PG 17 على منفذ مختلف (مثلًا 5433)، مع وحدة تخزين بيانات جديدة. تبقى خدمة PG 15 نشطة طوال هذه الخطوة — دون انقطاع لتطبيقاتك:
postgres17:
image: postgres:17
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pg17_data:/var/lib/postgresql/data
ports:
- "5433:5432"
shm_size: 256mb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pg17_data:شغّل هذه الخدمة فقط:
docker compose up -d postgres17
docker compose exec postgres17 pg_isreadyالاستعادة على PG 17 والتحقق من الامتدادات
استعد الكائنات العالمية أولًا، ثم قاعدة البيانات:
# الأدوار والمساحات
docker exec -i <pg17_container> psql -U postgres < backup_globals.sql
# استعادة قاعدة البيانات
docker cp mydb_pg15.dump <pg17_container>:/tmp/mydb_pg15.dump
docker exec <pg17_container> pg_restore \
-U postgres \
--no-owner \
--no-acl \
-d mydb \
/tmp/mydb_pg15.dumpإذا أبلغ pg_restore عن أخطاء في الامتدادات، أصلحها قبل المتابعة:
docker exec <pg17_container> psql -U postgres -d mydb \
-c 'CREATE EXTENSION IF NOT EXISTS pgvector;'تحويل التطبيقات والتحقق
ضع تطبيقاتك في وضع الصيانة أو للقراءة فقط، ثم قم بتحديث متغير DATABASE_URL لكل خدمة تطبيق للإشارة إلى حاوية PG 17 على المنفذ 5432. أعد تشغيل خدمات التطبيق:
curl -sf http://localhost/api/health | jq .databaseإذا اجتازت عملية التحقق، أوقف حاوية PG 15، وأعد تعيين المنفذ 5432 إلى postgres17.
search_path هو التغيير الصامت الأكثر شيوعًا. منذ PG 15.1، لم يعد مخطط public مدرجًا في search_path الافتراضي للأدوار غير المشرف. إذا فشلت ترحيلات Flyway أو Liquibase أو بيانات Artisan الأولية بخطأ relation "X" does not exist بعد الترقية، أضف SET search_path TO public, "$user"; في بداية الجلسة أو أهّل أسماء جداولك صراحةً.
حالة Supabase: postgres:17 بدون تحذير
توثّق المناقشة رقم 46080 في سجل تغييرات Supabase العام (github.com/orgs/supabase/discussions/46080) التحول إلى postgres:17 الذي جرى في يونيو 2026. البيئات المستضافة ذاتيًا التي تشير إلى image: supabase/postgres دون وسم إصدار تلقّت PG 17 عند تنفيذ docker compose pull التالي، دون ترقية تلقائية للبيانات.
السلوك الملاحظ: تبدأ حاوية PG 17، وترفض قراءة PGDATA الخاص بـ PG 15 (التنسيق غير متوافق)، وتتوقف فورًا. سجلات الخطأ في الحاوية:
docker logs <supabase_db_container> 2>&1 | head -20
# FATAL: database files are incompatible with server
# DETAIL: The data directory was initialized by PostgreSQL version 15, which is not compatible with this version 17.أفضل ممارسة لأي نشر Supabase مستضاف ذاتيًا هي تثبيت الوسم بإصدار ثانوي دقيق في docker-compose.yml:
db:
image: supabase/postgres:15.8.1.040
# أو أحدث إصدار 17.x بعد اكتمال الترقية
# image: supabase/postgres:17.4.1.016استراتيجيات الترقية: pg_dump/restore مقابل pg_upgrade مقابل النسخ المتماثل المنطقي
| الاستراتيجية | مدة التوقف | التعقيد |
|---|---|---|
| pg_dump / pg_restore (هذا الدليل) | 5-30 دقيقة حسب حجم البيانات | منخفض — أدوات أصلية، قابلة للتكرار |
| pg_upgrade في المكان | 1-5 دقائق (ترقية ثنائية سريعة) | مرتفع — يتطلب PG 15 وPG 17 معًا، صعب في Docker |
| النسخ المتماثل المنطقي (صفر توقف حقيقي) | أقل من دقيقة | مرتفع جدًا — يتطلب `wal_level = logical` وفتحات النسخ المتماثل |
استكشاف الأخطاء: الأخطاء الشائعة وأسبابها
الأخطاء الأكثر شيوعًا أثناء ترقية PG 15 إلى 17 في بيئة Docker:
FATAL: database files are incompatible with server
السبب: بدأت حاوية PG 17 على نفس وحدة التخزين الخاصة بـ PG 15. الحل: استخدم دائمًا وحدة تخزين جديدة لـ PG 17 واستعد عبر pg_restore.
ERROR: extension "timescaledb" is not available (أو pg_partman، pg_cron)
السبب: الامتداد غير مترجم لـ PG 17 في صورة postgres:17 الرسمية. الحل: استخدم صورة مشتقة تحتوي على الامتدادات المطلوبة (مثل timescale/timescaledb:latest-pg17).
ERROR: role "X" already exists عند استعادة الكائنات العالمية
السبب: النسخة الاحتياطية pg_dumpall --globals-only تتضمن CREATE ROLE لجميع الأدوار. الحل: استخدم --if-not-exists أو احذف سطور CREATE ROLE postgres من backup_globals.sql قبل الاستعادة.
ERROR: relation "public.X" does not exist في التطبيقات
السبب: تغيير search_path الافتراضي منذ PG 15.1. الحل: أضف options=-csearch_path=public إلى سلسلة الاتصال، أو نفّذ ALTER ROLE app_user SET search_path = 'public'; بعد الاستعادة.
pg_restore: error: invalid byte sequence for encoding "UTF8"
السبب: بيانات مرمّزة بـ LATIN1 في PG 15 وتمت تهيئة PG 17 بـ UTF8. الحل: استعد على مثيل PG 17 تمت تهيئته باستخدام POSTGRES_INITDB_ARGS: --encoding=LATIN1.
قائمة التحقق من التحويل — تحقق بالترتيب قبل كل خطوة
- قبل البدء: قائمة الامتدادات النشطة مستخرجة (
pg_extension)، إصدار PG 15 مثبّت في Compose، النسخة الاحتياطية الكاملة مُتحقَّق منها (pg_restore --listبدون أخطاء). - بعد الاستعادة على PG 17: جميع الامتدادات موجودة بالإصدار الصحيح، تم التحقق من
search_pathعبر دور التطبيق (ليس مشرفًا)، مقارنة عدد الصفوف في الجداول الخمسة الحيوية بين PG 15 وPG 17. - قبل تحويل التطبيق: إعلان نافذة الصيانة، تفعيل وضع القراءة فقط إن أمكن، التقاط آخر نسخة احتياطية من PG 15.
- بعد تحويل التطبيق: نقطة نهاية
/healthتعيد 200 معdatabase: ok، سجلات التطبيق بدون أخطاءrelation does not exist. - الاحتفاظ بـ PG 15: الاحتفاظ بوحدة تخزين PG 15 لمدة 30 يومًا على الأقل، النسخ الاحتياطية لمدة 90 يومًا، توثيق إجراء التراجع.
- بعد الترقية: تشغيل
ANALYZE VERBOSE;على جميع قواعد البيانات لإعادة حساب إحصائيات المخطط، التحقق من نشاطautovacuum، تحديث المراقبة لـ PG 17.
إدارة الترقية عبر محفظة عملاء
بالنسبة لوكالة تدير بيئات عملاء متعددة، فإن ترقية PG 15 إلى 17 ليست حدثًا منفردًا — بل هي عمل يجب التخطيط له عبر المحفظة.
الجرد أولًا. الأمر التالي يسرد جميع إصدارات PostgreSQL قيد التشغيل على VPS يستضيف مشاريع Compose متعددة:
docker ps --format '{{.Names}}' | xargs -I{} sh -c \
'docker exec {} psql -U postgres -qtAX -c "SELECT current_setting(\"server_version\")" 2>/dev/null && echo " <- {}"'تحديد الأولويات حسب المخاطر: البيئات التي تحتوي على امتدادات مترجمة مخصصة أو timescaledb/pg_partman تحتاج إلى صورة مشتقة مختبرة قبل التحويل.
توحيد الصورة: تعريف صورة مشتركة في سجل داخلي (registry.yourcompany.com/postgres:17-base) تحتوي على الامتدادات التي تحققت منها الوكالة. جميع مشاريع Compose في المحفظة تشير إلى هذه الصورة.
النسخ الاحتياطية اليومية المضمّنة في خطط Agency على ServOrbit توفر شبكة أمان لكل ترقية: إذا أظهرت بيئة عميل سلوكًا غير متوقع في الـ 24 ساعة التالية للتحويل، تبدأ الاستعادة من نسخة احتياطية من اليوم السابق.