`discourse_permalink` vide provoque des échecs de synchronisation de commentaires silencieux

Environnement

  • Version de WP-Discourse : 2.5.7

  • Version de Discourse : 3.5.0.beta5-dev

  • Version de WordPress : 6.9

  • Hébergement : WP Engine (multisite WordPress)

  • Instance Discourse : Auto-hébergée / Hébergement Discourse (forum.avweb.com)

Résumé

Les articles publiés depuis WordPress créent des sujets sur Discourse avec succès, mais discourse_permalink est stocké comme une chaîne vide dans les métadonnées de l’article. Cela provoque l’échec silencieux de toutes les synchronisations de commentaires ultérieures, car sync_comments() dans discourse-comment.php s’arrête prématurément lorsque le permalien est vide. L’ID du sujet Discourse et le drapeau de synchronisation webhook sont écrits correctement — seul le permalien est manquant.

Nous avons identifié 174 articles affectés sur un seul site de notre réseau multisite. Le problème semble avoir commencé vers la mi-décembre 2025.

Étapes pour reproduire

  1. Publier un article WordPress configuré pour créer un sujet Discourse

  2. Le sujet est créé avec succès sur Discourse

  3. discourse_topic_id est enregistré dans les métadonnées de l’article ✓

  4. wpdc_sync_post_comments est défini sur 1 (le webhook se déclenche correctement) ✓

  5. discourse_permalink est enregistré comme une chaîne vide ✗

  6. discourse_comments_raw n’est jamais écrit (la synchronisation ne se termine jamais)

Comportement attendu

discourse_permalink devrait contenir l’URL complète du sujet Discourse (par exemple, The China Chickens Come Home - AVweb - News Discussion - AVweb.com Discussion) après la création réussie du sujet.

Comportement actuel

discourse_permalink existe en tant que clé de métadonnée mais sa valeur est vide. Chaque appel subséquent à sync_comments() atteint cette garde à la ligne 209 de lib/discourse-comment.php et retourne prématurément :

if ( ! $discourse_permalink ) {
    return 0;
}

Comme la synchronisation ne se termine jamais, discourse_last_sync n’est jamais écrit, de sorte que le plugin tente à nouveau à chaque chargement de page — et échoue à chaque fois.

Diagnostic

Nous avons retracé le problème via le chemin de code suivant :

  1. DiscourseCommentFormatter::format() appelle do_action('wpdc_sync_discourse_comments') qui déclenche DiscourseComment::sync_comments()

  2. sync_comments() vérifie discourse_permalink — le trouve vide, retourne 0

  3. format() vérifie ensuite discourse_comments_raw dans les métadonnées de l’article — le trouve manquant, retourne bad_response_html()

  4. Les commentaires ne s’affichent jamais pour l’article affecté

Le flux de création de sujet écrit discourse_topic_id mais ne parvient pas à persister le permalien. Nous avons pu reconstruire les permaliens corrects en interrogeant l’API Discourse à l’adresse /t/{topic_id}.json et en les réécrivant dans les métadonnées de l’article, ce qui a résolu la synchronisation pour les 174 articles.

Solution de contournement

Nous avons écrit un script de réparation WP-CLI qui :

  1. Trouve les articles où wpdc_sync_post_comments = 1 mais discourse_comments_raw est manquant

  2. Récupère le slug du sujet depuis /t/{topic_id}.json

  3. Reconstruit et enregistre le permalien

  4. Récupère et enregistre les commentaires depuis /t/{slug}/{topic_id}/wordpress.json

Nous exécutons ceci sur une tâche cron planifiée à titre de mesure temporaire.


Problèmes supplémentaires trouvés lors de l’enquête

1. Verrouillage MySQL global provoquant des échecs de synchronisation silencieux

Fichier : lib/discourse-comment.php, ligne 176

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

sync_comments() utilise un seul verrou MySQL global (discourse_lock) partagé entre tous les articles de l’installation entière. Le délai d’attente est de 0 (non bloquant), donc si un article est en cours de synchronisation, tous les autres articles sautent silencieusement leur synchronisation — pas de journalisation, pas de nouvelle tentative.

Sur un site à fort trafic publiant plusieurs articles par jour, cela crée une condition de concurrence où les articles perdent constamment le verrou et ne se synchronisent jamais. Combiné à la période de synchronisation de 10 minutes, un article peut rester bloqué de façon permanente s’il perd le verrou lors de ses premières tentatives et que le problème de permalien vide empêche les synchronisations futures.

Correction suggérée : Utiliser un verrou par article :

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

Avec la libération correspondante :

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

2. Double échappement de discourse_comments_raw

Fichier : lib/discourse-comment.php, ligne 222

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

update_post_meta() utilise $wpdb->prepare() en interne, donc l’encapsulation de la valeur dans esc_sql() provoque un double échappement. Sur plusieurs cycles de synchronisation, les guillemets dans le corps JSON accumulent des caractères d’échappement (\\"\\\\\\"\\\\\\\\\\\\\\"), jusqu’à ce que le JSON devienne impossible à analyser.

Correction suggérée :

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

Impact

Ces problèmes s’additionnent : le permalien vide empêche la synchronisation initiale, le verrou global empêche la récupération, et le double échappement peut corrompre les données des articles qui parviennent à se synchroniser. Sur notre installation, 174 articles (environ 2 mois de contenu) ont été affectés avant que nous n’identifiions la cause première.