Skip to main content
Phoenix هو إطار عمل ويب مبني بلغة Elixir، ويعتمد على Ecto والمشغّل Postgrex للاتصال بـ PostgreSQL. في هذا الدليل، ستنشئ تطبيق Phoenix، وتربطه بـ ClickHouse Managed Postgres عبر اتصال TLS مع التحقق من الشهادة، ثم تنفّذ عملية ترحيل (migration) باستخدام Ecto، وتوفّر مورد todos بتنسيق JSON.

المتطلبات الأساسية

  • Elixir 1.15 أو إصدار أحدث، بالإضافة إلى Erlang/OTP. اختُبر هذا الدليل باستخدام Elixir 1.20.4 وErlang/OTP 29 وPhoenix 1.8.15 وecto_sql 3.14.0 وPostgrex 0.22.4.
  • حساب على ClickHouse Cloud

إنشاء خدمة ClickHouse Managed Postgres

في وحدة تحكم ClickHouse Cloud، انقر على New service واختر Postgres. سيكون المثيل جاهزًا خلال بضع دقائق. للاطلاع على شرح تفصيلي للخطوات، راجع دليل البدء السريع.

الحصول على بيانات الاتصال

افتح خدمتك وانقر على Connect في الشريط الجانبي الأيسر. تعرض النافذة المنبثقة اسم المستخدم وكلمة المرور والخادم والمنفذ، وتتضمن مفتاح تبديل بين Directly وvia PgBouncer. أبقِ الخيار Directly محددًا وفعّل Use SSL. انسخ كلمة المرور واسم مضيف الخادم، ثم انقر على Download CA certificate. شهادة CA خاصة بخدمتك وحدها. ويمكنك أيضًا تنزيلها لاحقًا من Settings → CA Certificate. اتصال مباشر أم عبر PgBouncer؟ تطبيق Phoenix عملية تعمل لفترات طويلة، ويدير Ecto مجمّع الاتصالات الخاص به (pool_size، وقيمته الافتراضية عشرة). يتصل هذا الدليل مباشرةً بـ Postgres عبر المنفذ 5432. وإذا كان عدد كبير من مثيلات التطبيق يقترب مجتمعًا من الحد الأقصى لاتصالات Postgres، فراجع استخدام PgBouncer.

إنشاء تطبيق Phoenix

ثبّت مُولِّد مشاريع Phoenix، ثم أنشئ تطبيقًا. تُستخدم Postgres بوصفها قاعدة البيانات الافتراضية:
هل تضيف ذلك إلى تطبيق موجود؟تخطَّ الأمر mix phx.new. إذا كان تطبيقك يستخدم SQLite، وهو الخيار الافتراضي عند استخدام mix phx.new --database sqlite3، فانقله إلى Postgres أولاً:
  • في mix.exs، استبدل {:ecto_sqlite3, ">= 0.0.0"} بـ {:postgrex, ">= 0.0.0"}، ثم شغّل mix deps.get.
  • في lib/<your_app>/repo.ex، غيّر adapter: Ecto.Adapters.SQLite3 إلى adapter: Ecto.Adapters.Postgres.
بعد ذلك، تابع إلى قسم تهيئة اتصال قاعدة البيانات. ينشئ الأمر mix ecto.migrate جداولك الحالية في Postgres، لكنه لا ينقل البيانات من SQLite. احرص على تحديث config/test.exs قبل تشغيل mix test.

تهيئة اتصال قاعدة البيانات

انقل شهادة CA التي نزّلتها إلى دليل priv الخاص بالتطبيق، وغيّر اسمها إلى ca-certificate.pem. يحمل الملف الذي نزّلته اسم خدمتك، مثل your-service-ca-certificate.pem:
شهادة CA ليست بيانات سرية، لذا يمكنك إيداعها (commit) في المستودع مع تطبيقك. كما تُضمَّن الملفات الموجودة في priv في الإصدارات. في config/dev.exs، استبدل أسطر username وpassword وhostname وdatabase في تهيئة Hello.Repo بالسطرين url وssl:
config/dev.exs
عندما تكون ssl قائمة كلمات مفتاحية (keyword list)، يُفعّل Postgrex التحقق من الشهادة (verify: :verify_peer) والتحقق من اسم المضيف، ويُرسل اسم المضيف الوارد في URL بوصفه اسم خادم TLS. لذا يكفي أن تمرّر شهادة CA فقط. أما ssl: true فلا يعمل، لأنه يتحقق من شهادة الخادم بالاستناد إلى مخزن شهادات CA في نظام التشغيل لديك، وهذا المخزن لا يتضمن شهادة CA الخاصة بخدمتك. اضبط DATABASE_URL ليشير إلى خدمتك، مستخدمًا اسم قاعدة بيانات جديدًا. يستخدم هذا الدليل الاسم guide_phoenix:
لا تُضِف الاستعلام إلى DATABASE_URL. إذ يتجاهل Ecto المعلمتين sslmode وsslrootcert الواردتين في عنوان URL المعروض في النافذة المنبثقة Connect. ومن دون الإعداد ssl، لن يستخدم Postgrex بروتوكول TLS ولن يتحقق من هوية الخادم.
أنشئ قاعدة البيانات:
يتصل الأمر mix ecto.create بقاعدة البيانات الافتراضية postgres باستخدام إعدادات TLS ذاتها لتنفيذ CREATE DATABASE.

إنشاء مورد JSON وتشغيل الترحيل

أنشئ سياقًا ومخططًا وترحيلًا ووحدة تحكم JSON للمهام (todos):
في الملف lib/hello_web/router.ex، ألغِ تعليق نطاق /api وأضِف المورد إليه:
lib/hello_web/router.ex
طبّق الترحيل. وفي حال كان التطبيق قائمًا مسبقًا، فسيؤدي ذلك أيضًا إلى إنشاء بقية جداول تطبيقك في قاعدة البيانات الجديدة:
يسجّل Ecto عمليات الترحيل المطبّقة في جدول schema_migrations، فلا يشغّل في الاستدعاءات اللاحقة إلا عمليات الترحيل الجديدة.

التشغيل والتحقق

تحقّق من أن الاتصال مشفّر. يقرأ الاستعلام pg_stat_ssl الخاص بالاتصال الحالي:
شغّل الخادم على المنفذ 3116:
في طرفية ثانية، أنشئ مهمتين، ثم حدّد الأولى على أنها مكتملة، واحذف الثانية:
يُعيد طلب PATCH المهمة بعد تحديثها:
افتح http://localhost:3116/api/todos في المتصفح لعرض المهام المتبقية:
لعرض البيانات في وحدة التحكم، افتح SQL console من الشريط الجانبي الأيسر لخدمتك، ثم وسّع guide_phoenix ثم public، وانقر على الجدول todos. ويتضمن المخطط public أيضًا الجدول schema_migrations:

تهيئة بيئة الإنتاج

في بيئة الإنتاج، يقرأ Phoenix إعدادات قاعدة البيانات من الملف config/runtime.exs. وفي تهيئة Hello.Repo ضمن الكتلة if config_env() == :prod do، استبدل السطر # ssl: true, بمسار شهادة CA:
config/runtime.exs
تحلّ الدالة Application.app_dir المسار داخل الإصدار أو النسخة المنشورة من تطبيقك، ولذلك يعمل هذا الإعداد أيًّا كان الدليل الذي يبدأ منه التطبيق. عيّن DATABASE_URL في بيئة النشر لديك، ودون استعلام هنا أيضًا. إذا كان تطبيقك قد بدأ باستخدام SQLite، فإن config/runtime.exs يقرأ DATABASE_PATH بدلًا من ذلك. استبدل المتغير database_path واستدعاء config :hello, Hello.Repo المرتبط به بما يلي:
config/runtime.exs
تُنفّذ التطبيقات المُنشأة باستخدام --database sqlite3 أيضًا عمليات الترحيل المعلّقة عند بدء تشغيل الإصدار، وذلك عبر العملية الفرعية Ecto.Migrator في lib/<your_app>/application.ex. ويستمر هذا السلوك كما هو عند استخدام Postgres.

استخدام PgBouncer

إذا كانت مثيلات تطبيقك تفتح مجتمعةً عددًا مفرطًا من الاتصالات المباشرة، فاتصل بدلًا من ذلك عبر PgBouncer المضمَّن. اختر via PgBouncer في نافذة Connect، واستخدم المنفذ 6432 في DATABASE_URL، ثم أضف prepare: :unnamed إلى تهيئة المستودع في كلٍّ من config/dev.exs وconfig/runtime.exs:
config/dev.exs
يعمل PgBouncer في وضع تجميع المعاملات:
  • العبارات المُحضَّرة: افتراضيًا، يُنفّذ Ecto كل استعلام بوصفه عبارة مُحضَّرة مسمّاة، ويدعم PgBouncer المرفق العبارات المسمّاة. ويحرّر Postgrex العبارات برسالة Close على مستوى البروتوكول، ولا يستخدم عبارة SQL DEALLOCATE إطلاقًا. ومع ذلك، تظهر مشكلتان عبر PgBouncer عند استخدام الإعداد الافتراضي prepare: :named:
    • قد يتوقف استعلام يبلغ طول نص SQL الخاص به نحو 4 KB عن الاستجابة إلى أن تنتهي مهلته، فيتلقى المستدعي الخطأ DBConnection.ConnectionError بعد دقيقة تقريبًا.
    • بعد أن يغيّر ترحيلٌ نوعَ أحد الأعمدة، تظل الاستعلامات التي تقرأ هذا العمود تفشل مع الخطأ cached plan must not change result type، حتى بعد إعادة تشغيل التطبيق، لأن PgBouncer يعيد استخدام عبارته المُحضَّرة على الخادم.
    أما مع prepare: :unnamed، فيُحضّر Postgrex كل استعلام من جديد في كل مرة يُنفَّذ فيها، فتزول المشكلتان كلتاهما، مقابل رحلة ذهاب وإياب إضافية واحدة عبر الشبكة لكل استعلام.
  • الترحيلات: يعمل mix ecto.migrate عبر PgBouncer باستخدام قفل الترحيل الافتراضي في Ecto، الذي يقفل الجدول schema_migrations داخل معاملة. لا تضبط migration_lock: :pg_advisory_lock لاتصال PgBouncer؛ إذ قد يُحرَّر القفل الاستشاري على مستوى الجلسة عبر اتصال Postgres مختلف، فيفشل ذلك مع الخطأ failed to release advisory lock ويبقى القفل محتجزًا، فتظل عمليات التشغيل اللاحقة تنتظره، بما فيها تلك التي تجري على المنفذ 5432. ولتشغيل الترحيلات عبر الاتصال المباشر، اضبط للأمر mix ecto.migrate قيمة DATABASE_URL تستخدم المنفذ 5432.
  • حالة الجلسة: لا تسري عبارات SET، مثل SET search_path في دالة رد النداء after_connect، إلا على اتصال Postgres الخاص بالمعاملة الحالية. ويؤدي تحديد search_path في الخيار parameters الخاص بـ Postgrex إلى الفشل مع الخطأ unsupported startup parameter: search_path. أما LISTEN وNOTIFY، اللذان يستخدمهما Postgrex.Notifications، فيتطلبان اتصالًا مباشرًا.
راجع الأسئلة الشائعة لمزيد من المعلومات حول العبارات المُحضَّرة وPgBouncer.

استكشاف الأخطاء وإصلاحها

  • CLIENT ALERT: Fatal - Unknown CA: لا يشير cacertfile إلى شهادة CA الخاصة بخدمتك، أو أنك ضبطت ssl: true. نزّل الشهادة مجددًا من Settings → CA Certificate، علمًا بأن لكل خدمة شهادة CA خاصة بها.
  • CLIENT ALERT: Fatal - Bad Certificate مع hostname_check_failed: المضيف المحدد في DATABASE_URL لا يطابق شهادة الخادم، كأن تكون قد استخدمت عنوان IP مثلًا. استخدم اسم المضيف الظاهر في النافذة المنبثقة Connect.
  • Invalid CA certificate file ... no such file or directory: مسار cacertfile غير صحيح. تحقّق من وجود الملف priv/ca-certificate.pem.

الخطوات التالية

آخر تعديل في ١٤ أكتوبر ٢٠٢٦