قواعد البيانات12 دقيقة قراءة

ترقية PostgreSQL من الإصدار 15 إلى 17 في Docker

يصل PostgreSQL 15 إلى نهاية دعمه الرسمي في نوفمبر 2026. قامت Supabase بالتحول بشكل صامت إلى `postgres:17` في يونيو 2026، مما أدى إلى تعطل البيئات المستضافة ذاتيًا التي لم تُثبّت إصدار الصورة. إذا كانت بيئة Compose لا تزال تعمل على PG 15، فإن نافذة الترقية مفتوحة — وتضيق. يفصّل هذا الدليل الامتدادات غير المتوافقة، ويوثّق التغييرات السلوكية الحقيقية بين PG 15 وPG 17، ويقدم إجراء ترقية بدون توقف قابلًا للتطبيق على أي بيئة Compose إنتاجية.

لماذا الترقية الآن — نهاية دعم 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

01

تثبيت إصدار صورة 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.

02

التقاط النسخ الاحتياطية العالمية وقواعد البيانات

صدّر أولًا الكائنات العالمية (الأدوار، والمساحات)، ثم كل قاعدة بيانات على حدة:

# الكائنات العالمية
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
03

تشغيل حاوية 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
04

الاستعادة على 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;'
05

تحويل التطبيقات والتحقق

ضع تطبيقاتك في وضع الصيانة أو للقراءة فقط، ثم قم بتحديث متغير 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 ساعة التالية للتحويل، تبدأ الاستعادة من نسخة احتياطية من اليوم السابق.

ترقية مخططة، محفظة تحت السيطرة

الوكالات التي تدير بيئات عملاء متعددة لا تستطيع تحمّل اكتشاف انقطاع في مساء ترقية تلقائية للإصدار. تقوم ServOrbit بمركزة إدارة VPS لمحافظ العملاء — بنية تحتية محكومة، تحديثات مخططة، نسخ احتياطية يومية مضمّنة في خطط Agency.

بحاجة إلى مساعدة؟

تصفّح مركز المساعدة والأسئلة الشائعة، أو تواصل مع فريقنا — معاودة اتصال أو WhatsApp أو بريد إلكتروني. الدعم بـالعربية والفرنسية والإنجليزية.