PG::UndefinedTable: خطأ: العلاقة "voice_rooms" غير موجودة

متابعة النقاش من Fatal: could not read Username for ‘https://github.com’:

أعتقد أن مشاكل GitHub قد حُلّت الآن، لكن عملية الترقية تفشل. هذا هو الحادث الأول:

rake aborted!
StandardError: An error has occurred, this and all later migrations canceled: (StandardError) 
PG::UndefinedTable: ERROR:  relation "voice_rooms" does not exist

أستخدم تكوين الحاويات المزدوج (وقمت بتشغيل data أيضًا، دون أخطاء)، وأنا على أحدث إصدار.

هل لديكم أي أفكار؟

يظهر الخطأ PG::UndefinedTable: ERROR: relation "voice_rooms" does not exist لأن هجرة قاعدة البيانات (migrations) الخاصة بإضافة voice الأساسية المدمجة في النواة لم تُنفَّذ بعد.

ما الذي كان يظهر فوق رسالة rake aborted!؟

يوجد… الكثير :laughing: لكن لا شيء يلفت انتباهي.

x86_64 arch detected.                                                                                                                                                 
Ensuring launcher is up to date                                                                                                                                       
Launcher is up-to-date                                                                                                                                                
Stopping old container                                                                                                                                                
+ /usr/bin/docker stop -t 600 web_only                                                                                                                                
web_only                                                                                                                                                              
2.0.20260812-0036: Pulling from discourse/base                                                                                                                        
Digest: sha256:837e8ed4b5916baa36856b842ad84fe262b6b1b5550701f8844b13cc7acad7a5                                                                                       
Status: Image is up to date for discourse/base:2.0.20260812-0036                                                                                                      
docker.io/discourse/base:2.0.20260812-0036                                                                                                                            
/usr/local/lib/ruby/gems/3.4.0/gems/pups-1.4.0/lib/pups.rb                                                                                                            
/usr/local/bin/pups --stdin                                                                                                                                           
I, [2026-09-03T08:55:23.418621 #1]  INFO -- : Reading from stdin                                                                                                      
I, [2026-09-03T08:55:23.453117 #1]  INFO -- : > thpoff echo "thpoff is installed!"                                                                                    
thpoff is installed!                                                                                                                                                  
I, [2026-09-03T08:55:23.461963 #1]  INFO -- : > /usr/local/bin/ruby -e 'if (ENV["DISCOURSE_SMTP_ADDRESS"] == "smtp.example.com" && ENV["DISCOURSE_SKIP_EMAIL_SETUP"] != "1"); puts "Aborting! Mail is not configured!"; exit 1; end'                                                                                                        
I, [2026-09-03T08:55:23.608522 #1]  INFO -- : > /usr/local/bin/ruby -e 'if ENV["DISCOURSE_HOSTNAME"] == "discourse.example.com"; puts "Aborting! Domain is not configured!"; exit 1; end'                                                                                                                                                   
I, [2026-09-03T08:55:23.743204 #1]  INFO -- : > /usr/local/bin/ruby -e 'if (ENV["DISCOURSE_CDN_URL"] || "")[0..1] == "//"; puts "Aborting! CDN must have a protocol specified"

أعتقد أن هذا قد يكون مرتبطًا بتطابق إصدارات هجرة إضافة resenha السابقة مع إصدارات إضافة الصوت الجديدة - على الأرجح لأنك قمت بتثبيتها من قبل؟ إذا لم تكن متأكدًا، هل يمكنك محاولة تنفيذ الأمر التالي في psql الخاص بك:

  SELECT version, name, direction, created_at, git_version
  FROM schema_migration_details
  WHERE version = '20241107000000'
  ORDER BY created_at;

راجع resenha/db/migrate/20241107000000_create_resenha_rooms.rb at b87ca4e20d649ba6b7bdc4615b289c414854889e · xfalcox/resenha · GitHub و discourse/plugins/voice/db/migrate/20241107000000_create_voice_rooms.rb at main · discourse/discourse · GitHub

يجب أن يكون هناك إصلاح أساسي لهذا التعارض في إصدارات الهجرات.. أتخيل أنه سيؤثر على الآخرين أيضًا. تابعوا معنا.

نعم، جرّبتُه في مراحله الأولى، لكنني أزلته لاحقًا.

حسنًا - على الأرجح أن هجرة (migration) Resenha كان يُفترض أن تكون قد نُفّذت لكنها لم تنفَّذ. وبما أن نفس رقم إصدار الهجرة يُستخدم في النواة (core) لوظيفة الصوت، فإن محاولة إضافة عمود لجدول voice_rooms لن تنجح لأن اسم الجدول لا يزال resenha_rooms.

يمكنك تشغيل هذا الأمر مؤقتًا في psql الخاص بك:

DO
$$
  DECLARE
    has_resenha boolean;
    has_voice   boolean;
    table_pair  record;
  BEGIN
    SELECT EXISTS (SELECT 1
                   FROM unnest(ARRAY [
                     'resenha_rooms',
                     'resenha_room_memberships',
                     'resenha_sessions',
                     'resenha_co_presences',
                     'resenha_recordings',
                     'resenha_invites'
                     ]) AS names(name)
                   WHERE to_regclass(format('public.%I', name)) IS NOT NULL)
    INTO has_resenha;

    SELECT EXISTS (SELECT 1
                   FROM unnest(ARRAY [
                     'voice_rooms',
                     'voice_room_memberships',
                     'voice_sessions',
                     'voice_co_presences',
                     'voice_recordings',
                     'voice_invites'
                     ]) AS names(name)
                   WHERE to_regclass(format('public.%I', name)) IS NOT NULL)
    INTO has_voice;

    IF has_resenha AND has_voice THEN
      RAISE EXCEPTION
        'Both Resenha and Voice tables exist. Nothing was changed.';
    END IF;

    IF NOT has_resenha THEN
      IF to_regclass('public.voice_rooms') IS NOT NULL THEN
        RAISE NOTICE 'The tables have already been renamed.';
        RETURN;
      END IF;

      RAISE EXCEPTION
        'Neither resenha_rooms nor voice_rooms exists. Nothing was changed.';
    END IF;

    IF EXISTS (SELECT 1
               FROM schema_migrations
               WHERE version = '20260831162602') THEN
      RAISE EXCEPTION
        'The rename migration is already recorded. Nothing was changed.';
    END IF;

    FOR table_pair IN
      SELECT *
      FROM (VALUES ('resenha_rooms', 'voice_rooms', '20241107000000'),
                   ('resenha_room_memberships', 'voice_room_memberships',
                    '20241107000000'),
                   ('resenha_sessions', 'voice_sessions', '20260305165400'),
                   ('resenha_co_presences', 'voice_co_presences',
                    '20260305165401'),
                   ('resenha_recordings', 'voice_recordings', '20260717172530'),
                   ('resenha_invites', 'voice_invites',
                    '20260813160047')) AS pairs(old_name, new_name, migration_version)
      LOOP
        IF EXISTS (SELECT 1
                   FROM schema_migrations
                   WHERE version = table_pair.migration_version) AND
           to_regclass(format('public.%I', table_pair.old_name)) IS NULL THEN
          RAISE EXCEPTION
            'Migration % is recorded, but table % is missing. Nothing was changed.',
            table_pair.migration_version,
            table_pair.old_name;
        END IF;

        IF to_regclass(format('public.%I', table_pair.old_name)) IS NOT NULL THEN
          EXECUTE format(
            'ALTER TABLE public.%I RENAME TO %I',
            table_pair.old_name,
            table_pair.new_name
                  );

          RAISE NOTICE 'Renamed % to %', table_pair.old_name, table_pair.new_name;
        END IF;
      END LOOP;
  END
$$;

يقوم هذا الكود بالآتي:

  1. يتحقق من جداول Resenha و Voice الموجودة.
  2. سيرفض المتابعة إذا كانت الجداول من النوعين موجودة (لا أتوقع حدوث ذلك).
  3. يتحقق من أن الهجرات المسجلة هي ما نتوقعه.
  4. يعيد تسمية الجداول الموجودة:
    • resenha_rooms → voice_rooms
    • resenha_room_memberships → voice_room_memberships
    • resenha_sessions → voice_sessions
    • resenha_co_presences → voice_co_presences
    • resenha_recordings → voice_recordings
    • resenha_invites → voice_invites
  5. يساعد هذا في معالجة الحالة الوسيطة الغريبة التي تعاني منها. يجب أن ينجح إعادة البناء بعد ذلك.

يجب أن يسمح هذا لبقية هجرات النواة الخاصة بك بالنجاح (سيكون أحدث هجرة صوت في النواة التي تقوم بإعادة التسمية بدون أي تأثير فعلي).

سأتحقق مع فريقنا لمعرفة ما إذا كان يجب علينا ترحيل هذا كمهمة rake.

شكرًا! عمل بشكل مثالي.

واجهتُ هذه المشكلة أيضًا - وتشغيل ذلك السكربت (النص البرمجي) تطلّب بعض التوجيه من ask.discourse.com، إذ ليس الأمر سهلًا للغاية لمديري الأنظمة الأقل خبرة!

إليك ملخص لبقية الأشخاص:


لمن يواجه هذه المشكلة في التثبيت القياسي عبر docker، إليك الطريقة الفعّالة لتشغيل السكربت المذكور أعلاه.

احصل على واجهة سطر أوامر (shell) وافتح psql.

  • حاوية واحدة:

    cd /var/discourse
    ./launcher start app   ## مطلوب فقط إذا كنت قد واجهت إعادة بناء فاشلة مسبقًا
    ./launcher enter app
    su -c 'psql discourse' postgres
    
    
  • إعداد حاويتين:

    cd /var/discourse
    ./launcher enter data
    su -c 'psql discourse' postgres
    
    

يجب أن ترى موجهًا (prompt) يظهر عليه discourse=#. إذا كنت غير متأكد، نفّذ الأمر \conninfo.

شغّل السكربت.

الصق الكتلة الكاملة DO $$ ... $$; من منشور نات أعلاه عند الموجه واضغط على Enter. عند النجاح، ستظهر إشعارات مثل:

NOTICE:  Renamed resenha_rooms to voice_rooms
NOTICE:  Renamed resenha_room_memberships to voice_room_memberships
...

إذا ظهر استثناء (exception) بدلاً من ذلك (مثل: “Both Resenha and Voice tables exist. Nothing was changed.”)، توقف والصق الرسالة هنا مرة أخرى — السكربت مصمم ليكون آمنًا عمدًا ولا يغيّر شيئًا في هذه الحالة.

اخرج وأعد البناء.

\q
exit

ثم عد إلى المضيف (host):

./launcher rebuild app          # حاوية واحدة
./launcher rebuild web_only     # حاويتان (يمكن أيضًا القيام بذلك عبر bootstrap بالطبع)

قد تحتاج إلى إعادة بناء مرتين — الأولى تُنهي حالة الترحيل (migration) التي طُبّقت جزئيًا، والثانية تكمل العملية بنجاح.