Postgres 18 ترقية بدون توقف: pg_createsubscriber
١٣ مايو ٢٠٢٦
لترقية Postgres 17 إلى 18 بدون توقف، قم بتشغيل pg_createsubscriber على نسخة احتياطية فيزيائية (physical standby) لتحويلها إلى مشترك منطقي (logical subscriber)، ثم قم بـ pg_upgrade لهذا المشترك إلى Postgres 18.3 بينما يستمر الأساسي القديم في استقبال عمليات الكتابة. يقوم النسخ المتماثل المنطقي (Logical replication) بتحديث العنقود (cluster) الجديد، وتقوم بمزامنة المتسلسلات (sequences) يدويًا، وينتقل تدفق البيانات (traffic) في ثوانٍ بدلاً من دقائق.12
مسار pg_upgrade الكلاسيكي سريع — عادةً بضع دقائق لعنقود صغير — ولكنه لا يزال يتطلب إغلاق العنقود القديم أثناء تشغيله.3 بالنسبة لتطبيق يرسل تنبيهات للدعم الفني عند أول انقطاع لمدة 30 ثانية، فإن "بضع دقائق" ليست صفرًا. تجعل نسخة PostgreSQL 18.3 (الصادرة في 26 فبراير 2026)4 مسار "وقت التوقف الصفري" أسهل بكثير مما كان عليه سابقًا من خلال تحسين pg_createsubscriber، وهو الملف الثنائي الذي يحول النسخة الاحتياطية الفيزيائية المتدفقة إلى مشترك منطقي في خطوة واحدة. يستعرض هذا البرنامج التعليمي التدفق الكامل باستخدام Docker Compose، لينتهي بعنقود Postgres 18.3 بدأ حياته كعنقود Postgres 17.9 أساسي.
ملخص
ستقوم بتشغيل Postgres 17.9 أساسي في Docker، وربط نسخة احتياطية فيزيائية متدفقة، وتحويل تلك النسخة الاحتياطية إلى مشترك منطقي باستخدام pg_createsubscriber --all (وهو علم "flag" جديد في PG 185)، ثم pg_upgrade إلى Postgres 18.3، ومزامنة المتسلسلات، وتحويل تدفق البيانات. إجمالي وقت التوقف عن الكتابة: تبديل اتصال واحد، عادةً في أقل من ثانية واحدة. تم التحقق من كل أمر وإصدار في هذا البرنامج التعليمي مقابل وثائق postgresql.org و Docker Hub في 13 مايو 2026.
ما ستتعلمه
- لماذا لا يمكن لـ
pg_upgradeبمفرده توفير وقت توقف صفري، وما الذي يغيره الجمع بينpg_createsubscriber+pg_upgrade. - كيفية تكوين Postgres 17.9 للنسخ المتماثل المنطقي وإضافة نسخة احتياطية فيزيائية متدفقة في Docker.
- كيفية تشغيل
pg_createsubscriber --allلتحويل تلك النسخة الاحتياطية إلى مشترك منطقي، بما في ذلك متطلبات GUC الأساسية. - كيفية إجراء
pg_upgradeللمشترك الجديد إلى Postgres 18.3 بينما يستمر الناشر (publisher) في خدمة عمليات الكتابة. - كيفية التعامل مع المتسلسلات (sequences) — وهي جزء الحالة الوحيد الذي لا ينقله النسخ المتماثل المنطقي — دون التصادم مع معرفات (IDs) الناشر.
- كيفية التحقق من اكتمال النسخ المتماثل عبر
pg_subscription_rel، ثم إجراء عملية التحويل (cutover). - خمسة أنماط فشل شائعة وخطة تراجع (rollback) نظيفة.
المتطلبات الأساسية
- Docker 27.0 أو أحدث مع Compose v2 (مدمج في حزم Docker Desktop و Docker Engine الحديثة).6
- صدفة POSIX (bash أو zsh).
- الإلمام بمصطلحات SQL ونسخ Postgres المتماثل (الناشر، المشترك، فتحة النسخ المتماثل).
- منافذ TCP شاغرة 5532 و 5533 و 5534 على
localhost. تم إزاحة المنافذ عمدًا عن 5432 لتجنب التصادم مع نسخة Postgres الموجودة لدى المطور. - حوالي 2 جيجابايت من مساحة القرص الحرة لمجلدات Docker المستخدمة في هذا الشرح.
لماذا يترك pg_upgrade وحده فجوة في وقت التوقف
يقوم pg_upgrade بإعادة كتابة كتالوجات النظام و (باستخدام --link) بربط ملفات البيانات في العنقود الجديد، وهذا هو سبب سرعته.3 ولكن يجب إيقاف العنقود القديم قبل تشغيل pg_upgrade، ولا يتم بدء تشغيل العنقود الجديد حتى ينتهي pg_upgrade. خلال تلك النافذة — دقائق لقاعدة بيانات صغيرة، وأطول بكثير لقاعدة كبيرة — يتم رفض كل عملية كتابة. أضف إلى ذلك الوقت المستغرق لتفريغ الاتصالات، وتشغيل analyze على العنقود الجديد، وتسخين الذاكرة المؤقتة (caches)، وغالبًا ما تتحول "بضع دقائق" إلى نافذة صيانة حقيقية.
يتجنب النسخ المتماثل المنطقي هذه المشكلة لأن الناشر والمشترك قد يشغلان إصدارات رئيسية مختلفة في نفس الوقت.7 تقوم ببناء نسخة Postgres 18.3 تتبع نسخة Postgres 17.9 الأساسية، وتنتظر حتى تلحق بها، ثم تقوم بالتحويل. وقت التوقف الوحيد هو تبديل الاتصال.
سير عمل pg_createsubscriber و pg_upgrade
قبل إصدار PG 17، كانت عملية الترقية بدون وقت توقف تتضمن pg_dump --schema-only، وعبارات CREATE PUBLICATION و CREATE SUBSCRIPTION يدوية، وانتظار انتهاء عملية COPY الأولية — وهي عملية تستغرق ساعات في قاعدة بيانات بحجم عدة تيرابايت لأن كل صف كان يجب إعادة نسخه عبر الشبكة. قدم PostgreSQL 17 أداة pg_createsubscriber لتخطي خطوة النسخ تلك تمامًا: فهي تأخذ نسخة احتياطية فيزيائية متدفقة موجودة (وهي بالفعل نسخة مطابقة للأصل بايت ببايت من الأساسي) وتحولها إلى مشترك منطقي عن طريق إيقافها، وتسجيل LSN، وإنشاء المنشورات والاشتراكات، وإعادة لف موضع التطبيق إلى ذلك الLSN.8 لا يقوم الملف الثنائي بنسخ أي بيانات؛ بل يقوم فقط بتبديل وضع النسخ المتماثل.
أضاف PostgreSQL 18 ثلاثة أعلام (flags) مفيدة فوق ذلك: --all (للتعامل مع كل قاعدة بيانات مستخدم في استدعاء واحد)، --clean (لإسقاط المنشورات المنقولة من تشغيل سابق)، و --enable-two-phase (لتفعيل دعم 2PC).5 علم --all هو الأهم بالنسبة للترقيات، لأن عناقيد الإنتاج نادرًا ما تحتوي على قاعدة بيانات واحدة.
اجمع بين pg_createsubscriber و pg_upgrade وسيصبح المسار كالتالي:
- إنشاء نسخة احتياطية فيزيائية متدفقة من نسخة PG 17 الأساسية الحالية.
- إيقاف النسخة الاحتياطية بشكل نظيف.
- تشغيل
pg_createsubscriber --allمقابل دليل بيانات النسخة الاحتياطية باستخدام ملفات PG 17 الثنائية. - تشغيل
pg_upgradeعلى دليل بيانات النسخة الاحتياطية للتحويل من PG 17 ← PG 18.3. - بدء تشغيل مثيل PG 18.3 الجديد؛ يستأنف النسخ المتماثل المنطقي من الLSN المسجل ويلحق بكل ما قبله الناشر أثناء تشغيل الترقية.
- مزامنة المتسلسلات، تحويل تدفق البيانات، وإسقاط المنشورات على الأساسي القديم.
الخطوة 1: تشغيل ناشر Postgres 17.9
قم بإنشاء دليل للمشروع وضع فيه ملف Docker-compose.yml:
# Docker-compose.yml
name: pg18-zdt
services:
publisher:
image: postgres:17.9
container_name: pg17-publisher
ports:
- "5532:5432"
environment:
POSTGRES_PASSWORD: dev
POSTGRES_DB: shop
volumes:
- publisher_data:/var/lib/postgresql/data
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_wal_senders=10"
- "-c"
- "max_replication_slots=10"
- "-c"
- "listen_addresses=*"
volumes:
publisher_data:
هناك إعدادان مهمان:
wal_level=logicalيكتب المعلومات الإضافية التي يحتاجها فك التشفير المنطقي (logical decoder).9 في Postgres 18 يمكنك تركwal_levelالمكون عندreplicaوسيقوم المستوى الفعلي بالترقية التلقائية بمجرد إنشاء أول فتحة منطقية؛10 بالنسبة لـ PG 17 لا تزال بحاجة لضبطه صراحة وإعادة التشغيل، ولهذا السبب قمنا بكتابته هنا بشكل ثابت.max_wal_senders=10وmax_replication_slots=10هي القيم الافتراضية ولكن من الجيد التصريح عنها. تحتاجpg_createsubscriberإلى فتحة واحدة على الأقل لكل قاعدة بيانات، بالإضافة إلى مساحة إضافية لأي نسخ احتياطية فيزيائية موجودة.11
قم بتشغيله وبذر مجموعة بيانات صغيرة:
Docker compose up -d publisher
Docker exec -i pg17-publisher psql -U postgres -d shop <<'SQL'
CREATE TABLE products (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sku text NOT NULL UNIQUE,
price_eur numeric(10,2) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO products (sku, price_eur) VALUES
('NRD-001', 19.99),
('NRD-002', 29.50),
('NRD-003', 9.00);
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'replpw';
SQL
افتح pg_hba.conf للسماح باتصال النسخ الاحتياطي (replication) من الخادم الاحتياطي (standby) ثم أعد التحميل:
Docker exec pg17-publisher bash -c "echo 'host replication replicator 0.0.0.0/0 scram-sha-256' >> /var/lib/postgresql/data/pg_hba.conf"
Docker exec pg17-publisher psql -U postgres -c "SELECT pg_reload_conf();"
في بيئة الإنتاج، يجب عليك تحديد نطاق CIDR هذا ليكون العنوان الفعلي للخادم الاحتياطي. بالنسبة لعرض تجريبي على localhost، فإن استخدام 0.0.0.0/0 مع كلمة مرور SCRAM يعد أمرًا جيدًا.
الخطوة 2: إضافة خادم احتياطي فيزيائي متدفق (streaming physical standby)
قم بتشغيل pg_basebackup من حاوية PG 17.9 جديدة إلى وحدة تخزين (volume) Docker مسماة، ثم ابدأ تشغيل نسخة ثانية من Postgres على وحدة التخزين تلك:
Docker volume create pg18-zdt_standby_data
Docker run --rm --network pg18-zdt_default \
-v pg18-zdt_standby_data:/var/lib/postgresql/data \
-e PGPASSWORD=replpw \
postgres:17.9 \
pg_basebackup -h publisher -p 5432 -U replicator \
-D /var/lib/postgresql/data \
-Fp -R -X stream -P
تقوم هذه الأعلام (flags) بمهام هامة:
-Fpيكتب شجرة أدلة بتنسيق عادي (مطلوب بواسطةpg_createsubscriber، الذي يعمل علىPGDATAحقيقي).12-Rيكتبstandby.signalوسطرprimary_conninfoفيpostgresql.auto.conf، بحيث يظهر المستلم كخادم احتياطي متدفق دون أي إعدادات إضافية.12-X streamيقوم ببث WAL جنبًا إلى جنب مع النسخة الاحتياطية الأساسية بحيث يكون دليل البيانات متسقًا في اللحظة التي ينتهي فيها الأمر.
الآن ابدأ تشغيل الخادم الاحتياطي على المنفذ الخاص به:
standby:
image: postgres:17.9
container_name: pg17-standby
ports:
- "5533:5432"
volumes:
- standby_data:/var/lib/postgresql/data
command: ["postgres", "-c", "listen_addresses=*"]
volumes:
publisher_data:
standby_data:
external: true
name: pg18-zdt_standby_data
أضف خدمة standby إلى ملف Docker-compose.yml ومدخل وحدة التخزين standby_data، ثم قم بتشغيل Docker compose up -d standby. تأكد من أن النسخ الاحتياطي المتدفق يعمل بشكل سليم من الناشر:
Docker exec pg17-publisher psql -U postgres -c \
"SELECT application_name, state, sync_state FROM pg_stat_replication;"
يجب أن ترى صفًا واحدًا في حالة streaming. أدخل صفًا في الناشر واقرأه مرة أخرى من المنفذ 5533 للتأكد من أن إعادة التشغيل (replay) تعمل مباشرة.
الخطوة 3: تحويل الخادم الاحتياطي باستخدام pg_createsubscriber --all
يتطلب pg_createsubscriber أن يكون الخادم المستهدف متوقفًا قبل تشغيله.1 أوقف الخادم الاحتياطي بشكل نظيف، ثم استدعِ الملف الثنائي باستخدام حاوية PG 17.9 تعمل لمرة واحدة وتقوم بتركيب نفس وحدة تخزين البيانات:
Docker compose stop standby
Docker run --rm --network pg18-zdt_default \
-v pg18-zdt_standby_data:/var/lib/postgresql/data \
-e PGPASSWORD=dev \
postgres:17.9 \
pg_createsubscriber --all \
-D /var/lib/postgresql/data \
-P "host=publisher port=5432 user=postgres dbname=postgres" \
-p 5432 \
--verbose
يخبر العلم --all الملف الثنائي باكتشاف كل قاعدة بيانات مستخدم على الخادم الاحتياطي وإنشاء زوج من المنشور/الاشتراك (publication/subscription) لكل واحدة منها — وهي ميزة الراحة الجديدة في PG 18 التي تحل محل الإدراج اليدوي لكل قاعدة بيانات باستخدام -d.5 تتبع أسماء الفتحات (slots) اتفاقية التسمية الموثقة pg_createsubscriber_<DB_OID>_<random_int> حتى لا تتعارض مع أي فتحات موجودة لديك بالفعل.13
خلف الكواليس، يقوم الملف الثنائي بستة أشياء:1
- يتحقق من المتطلبات الأساسية: يجب أن يكون لدى المصدر
wal_level = logical، وأن تكون قيمmax_replication_slotsوmax_wal_sendersعالية بما يكفي، ويجب أن يكون لدى الهدف قيمmax_active_replication_originsوmax_logical_replication_workersوmax_worker_processesعالية بما يكفي. - ينشئ منشورًا
FOR ALL TABLESعلى المصدر لكل قاعدة بيانات. - ينشئ فتحة نسخ منطقي (logical replication slot) على المصدر لكل قاعدة بيانات.
- يكتب معلمات الاسترداد (recovery parameters) في دليل بيانات الهدف مع ضبط
recovery_target_lsnوrecovery_target_action = promote. - يعيد تشغيل الهدف لفترة وجيزة حتى يقوم بإعادة التشغيل حتى ذلك الـ LSN ويتم ترقيته.
- ينشئ الاشتراك المطابق على الهدف — دون نسخ البيانات الأولية، لأن النسخة الفيزيائية تمتلكها بالفعل.
عندما ينتهي الملف الثنائي، أعد تشغيل حاوية الخادم الاحتياطي:
Docker compose start standby
Docker exec pg17-standby psql -U postgres -d shop \
-c "SELECT subname, subenabled FROM pg_subscription;"
يجب أن ترى pg_createsubscriber_<oid>_<rand> مدرجًا، مع subenabled = t. أصبح الخادم الاحتياطي الآن مشتركًا منطقيًا في Postgres 17.9.
الخطوة 4: ترقية الخادم الاحتياطي إلى Postgres 18.3 باستخدام pg_upgrade
الخطوة التالية هي عملية pg_upgrade قياسية من PG 17.9 إلى PG 18.3، مع خطوة مسبقة بالغة الأهمية: تعطيل الاشتراكات على المشترك قبل تشغيل pg_upgrade، وفقًا لوثائق الترقية الرسمية.14 إذا تخطيت هذه الخطوة، فسترفض الترقية ترحيل حالة الاشتراك.
Docker exec pg17-standby psql -U postgres -d shop \
-c "ALTER SUBSCRIPTION pg_createsubscriber_5_<rand> DISABLE;"
Docker compose stop standby
استبدل <rand> باللاحقة الفعلية من pg_subscription. مع استخدام --all، سيكون لديك اشتراك واحد لكل قاعدة بيانات مستخدم — قم بتعطيل كل واحد منها.
تتطلب أداة pg_upgrade توفر كل من ملفات PostgreSQL الثنائية القديمة والجديدة على نفس المضيف. لا توفر صورة Docker الرسمية لـ postgres:17.9 ولا لـ postgres:18.3 كلا الإصدارين، لذا سنقوم ببناء صورة صغيرة مزدوجة الإصدار. ضع ملف Dockerfile.upgrade هذا بجوار ملف compose:
# Dockerfile.upgrade
FROM postgres:18.3-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends postgresql-17 \
&& rm -rf /var/lib/apt/lists/*
يوفر مستودع PGDG المكون داخل صورة postgres:18.3-bookworm الرسمية بالفعل كل إصدار رئيسي مدعوم،15 لذا فإن إضافة حزمة postgresql-17 تمنحنا /usr/lib/postgresql/17/bin/ جنبًا إلى جنب مع /usr/lib/postgresql/18/bin/ في نفس الصورة. قم ببنائها مرة واحدة:
Docker build -t pg-upgrade:17-to-18 -f Dockerfile.upgrade .
الآن قم بتشغيل pg_upgrade مقابل دليل بيانات الخادم الاحتياطي. لاحظ أن PostgreSQL 18 قام بتغيير موقع PGDATA الافتراضي لصورة Docker الخاصة به — فهو يعيش الآن في /var/lib/postgresql/18/Docker بحيث يمكن لترقيات الإصدارات الرئيسية المستقبلية تركيب وحدة تخزين مشتركة واحدة.16 نحن نتبع نفس الاتفاقية هنا، حيث نخبر pg_upgrade بكتابة المجموعة (cluster) الجديدة في /var/lib/postgresql/18/Docker. يتم تشغيل البرنامج النصي كجذر (root) حتى يتمكن من تنفيذ chown على وحدة التخزين التي تم تركيبها حديثًا، ثم ينتقل إلى مستخدم postgres عبر gosu (المثبت مسبقًا في الصورة الرسمية) لعمليات Postgres الفعلية:
Docker volume create pg18-zdt_subscriber_v18
Docker run --rm --network pg18-zdt_default \
-v pg18-zdt_standby_data:/var/lib/postgresql/data \
-v pg18-zdt_subscriber_v18:/var/lib/postgresql/18 \
pg-upgrade:17-to-18 bash -c '
chown -R postgres:postgres /var/lib/postgresql/18 &&
gosu postgres /usr/lib/postgresql/18/bin/initdb \
-D /var/lib/postgresql/18/Docker \
-U postgres --auth-local=trust --auth-host=scram-sha-256 &&
printf "wal_level = logical\nmax_replication_slots = 10\nmax_active_replication_origins = 10\n" \
| gosu postgres tee -a /var/lib/postgresql/18/Docker/postgresql.conf &&
cd /tmp &&
gosu postgres /usr/lib/postgresql/18/bin/pg_upgrade \
--old-bindir=/usr/lib/postgresql/17/bin \
--new-bindir=/usr/lib/postgresql/18/bin \
--old-datadir=/var/lib/postgresql/data \
--new-datadir=/var/lib/postgresql/18/Docker \
--link
'
ثلاثة أشياء يجب ملاحظتها:
--linkيقوم بإنشاء روابط صلبة (hardlinks) لملفات البيانات في المجموعة الجديدة، وهذا هو السبب في أنpg_upgradeسريع حتى في المجموعات الكبيرة. لا يمكنك بدء تشغيل مجموعة PG 17 القديمة بعد اكتمال ترقية--link، لذا فإن الخادم الاحتياطي أصبح الآن بشكل دائم نسخة Postgres 18.- سطر
printf | teeيكتب إعدادات GUC الثلاثة التي تتحقق منهاpg_upgradeقبل أن تقوم بترحيل الفتحات المنطقية — تحتاج المجموعة الجديدة إلىwal_level = logicalوmax_replication_slotsعلى الأقل بنفس حجم عدد الفتحات في المجموعة القديمة.14 - يستمر الناشر في قبول عمليات الكتابة طوال الوقت الذي يتم فيه تشغيل
pg_upgrade— سيتم إعادة تشغيل تلك التغييرات من خلال الفتحة المنطقية الموجودة في اللحظة التي تظهر فيها المجموعة التي تمت ترقيتها.
أضف المجموعة التي تمت ترقيتها إلى ملف compose الخاص بك. اضبط PGDATA صراحةً حتى تكتشف نقطة الإدخال الرسمية دليل البيانات في موقع PG 18 الجديد، وقم بتثبيت إعدادات GUC التي أشارت إليها الترقية:
subscriber_v18:
image: postgres:18.3
container_name: pg18-subscriber
ports:
- "5534:5432"
environment:
PGDATA: /var/lib/postgresql/18/Docker
volumes:
- subscriber_v18:/var/lib/postgresql/18
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_replication_slots=10"
- "-c"
- "max_active_replication_origins=10"
- "-c"
- "listen_addresses=*"
volumes:
publisher_data:
standby_data:
external: true
name: pg18-zdt_standby_data
subscriber_v18:
external: true
name: pg18-zdt_subscriber_v18
max_active_replication_origins هو إعداد GUC جديد في Postgres 18 — تم فصله عن max_replication_slots حتى يتمكن المشتركون من تحديد حجم تتبع أصل النسخ الاحتياطي بشكل مستقل. قيمته الافتراضية هي 10 ويتم ضبطه عند بدء تشغيل الخادم.17 قم بتشغيل المجموعة وأعد تمكين الاشتراك:
Docker compose up -d subscriber_v18
Docker exec pg18-subscriber psql -U postgres -d shop \
-c "ALTER SUBSCRIPTION pg_createsubscriber_5_<rand> ENABLE;"
الخطوة 5: مزامنة المتتاليات (sequences) والتحويل النهائي (cut over)
لا يقوم النسخ المتماثل المنطقي (Logical replication) بنسخ المتسلسلات (sequences) في Postgres 18.18 تتدفق قيم الأعمدة من نوع IDENTITY و serial المدعومة بمتسلسلات بشكل جيد لأنها تنتقل كبيانات أعمدة عادية، لكن المؤشر الداخلي للمتسلسلة على المشترك (subscriber) يظل يشير إلى قيمة البداية. أول عملية إدخال (insert) مباشرة على العنقود (cluster) الجديد ستعطي id موجوداً بالفعل — مما يؤدي فوراً إلى خطأ مفتاح مكرر (duplicate-key error).
مزامنة المتسلسلات مدرجة في خارطة الطريق لنسخة Postgres 19،19 ولكن بالنسبة لـ 18 عليك القيام بذلك يدوياً. قم بإنشاء سكربت setval() على الناشر (publisher) وقم بتطبيقه على المشترك مباشرة قبل عملية التحويل (cutover)، مع ترك هامش (buffer) لاستيعاب أي معاملات قيد التنفيذ:
-- Run on publisher, capture output
SELECT format(
'SELECT pg_catalog.setval(%L, %s, true);',
schemaname || '.' || sequencename,
COALESCE(last_value, 1) + 1000
)
FROM pg_sequences
WHERE schemaname NOT IN ('pg_catalog','information_schema')
ORDER BY schemaname, sequencename;
هامش الـ + 1000 هو هامش الأمان التقليدي — فهو يضمن أن قيمة المتسلسلة التالية للمشترك تتجاوز أي قيمة قدمها الناشر بالإضافة إلى أي شيء قيد التنفيذ خلال نافذة التحويل الخاصة بك.
قم بتطبيق عبارات setval() التي تم إنشاؤها على pg18-subscriber، ثم قم بإجراء التحويل:
# 1. Block writes on publisher (revoke INSERT/UPDATE/DELETE for app role,
# or flip the load balancer to read-only). Keep reads alive.
# 2. Wait for the subscription to drain to the publisher's current LSN:
Docker exec pg18-subscriber psql -U postgres -d shop -c "
SELECT subname, latest_end_lsn FROM pg_stat_subscription;"
Docker exec pg17-publisher psql -U postgres -c "SELECT pg_current_wal_lsn();"
# 3. When latest_end_lsn >= pg_current_wal_lsn, redirect application traffic
# to port 5534 (the PG 18 cluster).
# 4. Drop the subscription and the publisher's slot to clean up.
Docker exec pg18-subscriber psql -U postgres -d shop -c "
ALTER SUBSCRIPTION pg_createsubscriber_5_<rand> DISABLE;
ALTER SUBSCRIPTION pg_createsubscriber_5_<rand> SET (slot_name = NONE);
DROP SUBSCRIPTION pg_createsubscriber_5_<rand>;"
التطبيق الآن يقوم بالكتابة في Postgres 18.3.
التحقق
هناك فحصان يخبرانك أن الترقية تمت بنجاح. أولاً، يجب أن يكون كل جدول تم نسخه في حالة r (جاهز) في pg_subscription_rel:20
SELECT srrelid::regclass AS table_name, srsubstate, srsublsn
FROM pg_subscription_rel
ORDER BY srrelid::regclass;
أي حالة srsubstate بخلاف r في أي صف تعني أن الجدول لا يزال قيد المزامنة أو عالقاً. ثانياً، يجب أن تكون مؤشرات المتسلسلة متقدمة على أي معرفات (IDs) موجودة — يجب أن يكون MAX(id) في كل جدول أقل من nextval('<seq>'). قم بإدخال صف تجريبي على العنقود الجديد وتأكد من عدم وجود خطأ مفتاح مكرر.
الأخطاء الشائعة
pg_createsubscriber: error: target server must be shut down — الخادم الاحتياطي (standby) لا يزال يعمل. قم بتنفيذ Docker compose stop standby وأعد التشغيل. يرفض الملف الثنائي العمل على عنقود نشط عن قصد.1
max_active_replication_origins must be at least N — إعداد GUC الجديد في PG 18، والذي تم فصله عن max_replication_slots في PG 18، قيمته الافتراضية 10 وهو المكافئ لكل مشترك لمحاسبة فتحات النسخ المتماثل. أعد التشغيل مع زيادة القيمة.17
subscription has tables in srsubstate = 's' بعد pg_upgrade — يتطلب pg_upgrade أن يكون كل جدول اشتراك في حالة i (بدء) أو r (جاهز) قبل تشغيله.14 انتظر حتى تنتهي المزامنة الأولية قبل الترقية.
duplicate key value violates unique constraint مباشرة بعد التحويل — المتسلسلات. أعد تشغيل سكربت setval() مع هامش أكبر؛ هذا من بين أكثر الأخطاء شيوعاً في عمليات التحويل الخاصة بالنسخ المتماثل المنطقي.18
يبدو النسخ المتماثل عالقاً، pg_replication_slots.confirmed_flush_lsn لا يتقدم — تأكد من أن عامل التطبيق (apply worker) نشط على المشترك (يجب أن يظهر pg_stat_subscription قيمة received_lsn غير فارغة)، وتحقق من pg_stat_database_conflicts على الناشر. إذا كان WAL الخاص بالناشر يمتلئ لأن المشترك متأخر جداً، فقم بزيادة max_slot_wal_keep_size أو أصلح ما يمنع التطبيق (معاملة طويلة الأمد، تعارض قفل، مفتاح أساسي مفقود).
التراجع (Rollback)
نظراً لأن الترقية أبقت خادم Postgres 17.9 الأساسي الأصلي يعمل ويقبل عمليات الكتابة طوال الوقت، فإن التراجع هو مجرد تبديل لسلسلة الاتصال (connection-string). إذا بدا أن هناك خطأ ما في Postgres 18.3 خلال دقائق من التحويل، فقم بإعادة توجيه التطبيق مرة أخرى إلى المنفذ 5532. لا يزال خادم PG 17.9 الأساسي هو المرجع؛ البيانات الوحيدة التي لا يملكها هي ما كتبه التطبيق في PG 18.3 بعد التحويل. للحصول على شبكة أمان حقيقية ثنائية الاتجاه، قم بإعداد اشتراك منطقي عكسي PG 18 ← PG 17 قبل التحويل (الناشر على العنقود الجديد، والمشترك على القديم)، بحيث تتدفق عمليات الكتابة بعد التحويل مرة أخرى. هذا يستحق ساعة إضافية من الإعداد لأعباء عمل OLTP الحرجة.
الخطوات التالية
نفس سير عمل pg_createsubscriber يدعم عمليات النشر من نوع blue-green — قم بإعداد نسخة PG 18 متماثلة، استنزف القراءات، حول الكتابات، وأوقف تشغيل الخادم الأساسي القديم وفقاً لجدول زمني بدلاً من الساعة 3 صباحاً. ادمجه مع PgBouncer أو Supabase Supavisor أمام Postgres بحيث يكون تبديل الاتصال عبارة عن إعادة تحميل لتكوين المجمع (pooler) بدلاً من إعادة نشر التطبيق. إذا كنت بحاجة إلى أحداث واجهة مستخدم في الوقت الفعلي فوق هذه المجموعة، فإن Postgres LISTEN/NOTIFY للتواجد المباشر يضاف فوقها بسلاسة دون أي نسخ متماثل جديد. وإذا كنت تريد بيئة تطوير كاملة تعتمد على Docker-Compose خلف HTTPS بمعايير الإنتاج، فراجع دليل الوكيل العكسي Caddy.
Footnotes
-
توثيق PostgreSQL 18، pg_createsubscriber. https://www.postgresql.org/docs/current/app-pgcreatesubscriber.html ↩ ↩2 ↩3 ↩4
-
توثيق PostgreSQL 18، الفصل 29. النسخ المتماثل المنطقي — الترقية. https://www.postgresql.org/docs/current/logical-replication-upgrade.html ↩
-
توثيق PostgreSQL 18، pg_upgrade. https://www.postgresql.org/docs/current/pgupgrade.html ↩ ↩2
-
أخبار PostgreSQL، إصدار PostgreSQL 18.3، 17.9، 16.13، 15.17، و 14.22! (26 فبراير 2026). https://www.postgresql.org/about/news/postgresql-183-179-1613-1517-and-1422-released-3246/ ↩
-
توثيق PostgreSQL 18، أداة pg_createsubscriber — مفاتيح
--all، و--clean، و--enable-two-phase. https://www.postgresql.org/docs/current/app-pgcreatesubscriber.html ↩ ↩2 ↩3 -
Docker، مواصفات Compose. https://docs.Docker.com/compose/ ↩
-
توثيق PostgreSQL 18، الفصل 29. النسخ المتماثل المنطقي (Logical Replication). https://www.postgresql.org/docs/current/logical-replication.html ↩
-
pgPedia، أداة pg_createsubscriber. https://pgpedia.info/p/pg_createsubscriber.html ↩
-
توثيق PostgreSQL 18، 19.5. سجل الكتابة المسبقة (Write Ahead Log) — wal_level. https://www.postgresql.org/docs/current/runtime-config-wal.html ↩
-
توثيق PostgreSQL 18، 19.5. سجل الكتابة المسبقة (Write Ahead Log) — الترقية التلقائية الفعالة لمستوى WAL عند وجود فتحة منطقية (logical slot). https://www.postgresql.org/docs/current/runtime-config-wal.html ↩
-
توثيق PostgreSQL 18، pg_createsubscriber — المتطلبات الأساسية لـ
max_replication_slotsوmax_wal_sendersعلى المصدر. https://www.postgresql.org/docs/current/app-pgcreatesubscriber.html ↩ -
توثيق PostgreSQL 18، pg_basebackup. https://www.postgresql.org/docs/current/app-pgbasebackup.html ↩ ↩2
-
pgPedia، pg_createsubscriber — اتفاقية تسمية الفتحات (slots)
pg_createsubscriber_%u_%x. https://pgpedia.info/p/pg_createsubscriber.html ↩ -
توثيق PostgreSQL 18، الفصل 29.13. الترقية — متطلبات pg_upgrade الأساسية لعناقيد النسخ المتماثل المنطقي، بما في ذلك تعطيل الاشتراكات قبل الترقية. https://www.postgresql.org/docs/current/logical-replication-upgrade.html ↩ ↩2 ↩3
-
مستودع PostgreSQL APT، PostgreSQL Apt Repository — يخدم جميع الإصدارات الرئيسية المدعومة حالياً. https://wiki.postgresql.org/wiki/Apt ↩
-
Docker-library/postgres GitHub PR / مناقشة — تغيير VOLUME في صورة Postgres 18 إلى
/var/lib/postgresqlلتمكين الترقيات الرئيسية جنباً إلى جنب عبرpg_upgrade --link. https://GitHub.com/Docker-library/postgres/issues/37 ↩ -
توثيق PostgreSQL 18، 19.6. النسخ المتماثل —
max_active_replication_origins(جديد في PG 18). https://www.postgresql.org/docs/current/runtime-config-replication.html ↩ ↩2 -
توثيق PostgreSQL 18، 29.8. القيود — لا يتم نسخ التسلسلات (sequences) والكائنات الكبيرة (large objects). https://www.postgresql.org/docs/current/logical-replication-restrictions.html ↩ ↩2
-
مدونة depesz، في انتظار PostgreSQL 19 — مزامنة التسلسلات في النسخ المتماثل المنطقي (نوفمبر 2025). https://www.depesz.com/2025/11/11/waiting-for-postgresql-19-sequence-synchronization-in-logical-replication/ ↩
-
توثيق PostgreSQL 18، كتالوج نظام pg_subscription_rel. https://www.postgresql.org/docs/current/catalog-pg-subscription-rel.html ↩