فشل مزامنة التعليقات الصامت بسبب `discourse_permalink` فارغ

البيئة

  • إصدار WP-Discourse: 2.5.7

  • إصدار Discourse: 3.5.0.beta5-dev

  • إصدار WordPress: 6.9

  • الاستضافة: WP Engine (شبكة متعددة مواقع ووردبريس)

  • مثيل Discourse: مستضاف ذاتيًا / استضافة Discourse (forum.avweb.com)

الملخص

تنجح المنشورات المنشورة من ووردبريس في إنشاء مواضيع على Discourse، ولكن يتم تخزين discourse_permalink كسلسلة فارغة في بيانات تعريف المنشور. يؤدي هذا إلى فشل جميع مزامنات التعليقات اللاحقة بصمت، حيث تتوقف دالة sync_comments() في discourse-comment.php مبكرًا عندما يكون الرابط الدائم فارغًا. يتم كتابة معرف موضوع Discourse وعلامة مزامنة الويب هوك بشكل صحيح - فقط الرابط الدائم مفقود.

حددنا 174 منشورًا متأثرًا في موقع واحد في شبكتنا متعددة المواقع. يبدو أن المشكلة بدأت حوالي منتصف ديسمبر 2025.

خطوات التكرار

  1. نشر منشور ووردبريس مُعد لإنشاء موضوع Discourse

  2. يتم إنشاء الموضوع بنجاح على Discourse

  3. يتم حفظ discourse_topic_id في بيانات تعريف المنشور ✓

  4. يتم تعيين wpdc_sync_post_comments على 1 (يتم تشغيل الويب هوك بشكل صحيح) ✓

  5. يتم حفظ discourse_permalink كسلسلة فارغة ✗

  6. لا يتم كتابة discourse_comments_raw أبدًا (لا تكتمل المزامنة)

السلوك المتوقع

يجب أن يحتوي discourse_permalink على عنوان URL الكامل لموضوع Discourse (على سبيل المثال، The China Chickens Come Home - AVweb - News Discussion - AVweb.com Discussion) بعد إنشاء الموضوع بنجاح.

السلوك الفعلي

يوجد discourse_permalink كمفتاح بيانات تعريف ولكن قيمته فارغة. كل استدعاء لاحق لـ sync_comments() يصل إلى هذا الحارس في السطر 209 من lib/discourse-comment.php ويعود مبكرًا:

if ( ! $discourse_permalink ) {
    return 0;
}

نظرًا لأن المزامنة لا تكتمل أبدًا، لا يتم كتابة discourse_last_sync، لذلك تحاول الإضافة تكرار المحاولة عند كل تحميل للصفحة - وتفشل في كل مرة.

التشخيص

تتبعنا المشكلة عبر مسار الكود التالي:

  1. تستدعي DiscourseCommentFormatter::format() الدالة do_action('wpdc_sync_discourse_comments') التي تؤدي إلى تشغيل DiscourseComment::sync_comments()

  2. تتحقق sync_comments() من discourse_permalink - تجده فارغًا، وتعيد 0

  3. تتحقق format() بعد ذلك من discourse_comments_raw في بيانات المنشور المخصصة - تجده مفقودًا، وتعيد bad_response_html()

  4. لا يتم عرض التعليقات للمنشور المتأثر أبدًا

يكتب تدفق إنشاء الموضوع discourse_topic_id ولكنه يفشل في الاحتفاظ بالرابط الدائم. تمكنا من إعادة بناء الروابط الدائمة الصحيحة عن طريق الاستعلام من واجهة برمجة تطبيقات Discourse على /t/{topic_id}.json وإعادة كتابتها إلى بيانات تعريف المنشور، مما أدى إلى حل المزامنة لجميع المنشورات الـ 174.

الحل البديل

كتبنا برنامجًا نصيًا لإصلاح WP-CLI يقوم بما يلي:

  1. يجد المنشورات حيث wpdc_sync_post_comments = 1 ولكن discourse_comments_raw مفقود

  2. يجلب جزء عنوان الموضوع من /t/{topic_id}.json

  3. يعيد بناء وحفظ الرابط الدائم

  4. يجلب ويحفظ التعليقات من /t/{slug}/{topic_id}/wordpress.json

نقوم بتشغيل هذا على مهمة مجدولة كحل مؤقت.


مشكلات إضافية تم العثور عليها أثناء التحقيق

1. قفل MySQL العام يسبب فشل المزامنة الصامت

الملف: lib/discourse-comment.php، السطر 176

$got_lock = $wpdb->get_row( "SELECT GET_LOCK( 'discourse_lock', 0 ) got_it" );

تستخدم sync_comments() قفل MySQL عامًا واحدًا (discourse_lock) مشتركًا عبر جميع المنشورات في التثبيت بأكمله. المهلة هي 0 (غير مانعة)، لذا إذا كان أي منشور تتم مزامنته حاليًا، فإن جميع المنشورات الأخرى تتخطى مزامنتها بصمت - لا يوجد تسجيل، ولا إعادة محاولة.

في موقع عالي الحركة ينشر عدة منشورات يوميًا، يؤدي هذا إلى حالة سباق حيث تخسر المنشورات القفل باستمرار ولا تتم مزامنتها أبدًا. عند دمجها مع فترة المزامنة البالغة 10 دقائق، يمكن أن يعلق المنشور بشكل دائم إذا فقد القفل في محاولاته القليلة الأولى ثم منع الرابط الدائم الفارغ عمليات المزامنة المستقبلية.

الإصلاح المقترح: استخدم قفلًا لكل منشور:

$got_lock = $wpdb->get_row( "SELECT GET_LOCK( 'discourse_lock_{$post_id}', 0 ) got_it" );

مع الإصدار المقابل:

$wpdb->get_results( "SELECT RELEASE_LOCK( 'discourse_lock_{$post_id}' )" );

2. الهروب المزدوج لـ discourse_comments_raw

الملف: lib/discourse-comment.php، السطر 222

update_post_meta( $post_id, 'discourse_comments_raw', esc_sql( $raw_body ) );

تستخدم update_post_meta() الدالة $wpdb->prepare() داخليًا، لذا فإن تغليف القيمة بـ esc_sql() يسبب الهروب المزدوج. على مدى دورات المزامنة المتعددة، تتراكم علامات الاقتباس في جسم JSON أحرف الهروب (\\"\\\\\\"\\\\\\\\\\\\\\" ) حتى يصبح JSON غير قابل للتحليل.

الإصلاح المقترح:

update_post_meta( $post_id, 'discourse_comments_raw', $raw_body );

التأثير

تتراكم هذه المشكلات: يمنع الرابط الدائم الفارغ المزامنة الأولية، ويمنع القفل العام الاسترداد، ويمكن أن يؤدي الهروب المزدوج إلى إتلاف البيانات للمنشورات التي تتمكن من المزامنة. في تثبيتنا، تأثر 174 منشورًا (ما يقرب من شهرين من المحتوى) قبل أن نحدد السبب الجذري.