Entorno
-
Versión de WP-Discourse: 2.5.7
-
Versión de Discourse: 3.5.0.beta5-dev
-
Versión de WordPress: 6.9
-
Alojamiento: WP Engine (WordPress multisite)
-
Instancia de Discourse: Autoalojada / Alojamiento de Discourse (forum.avweb.com)
Resumen
Las publicaciones publicadas desde WordPress crean temas con éxito en Discourse, pero discourse_permalink se almacena como una cadena vacía en los metadatos de la publicación. Esto provoca que todas las sincronizaciones de comentarios posteriores fallen silenciosamente, ya que sync_comments() en discourse-comment.php se detiene prematuramente cuando el permalink está vacío. El ID del tema de Discourse y la bandera de sincronización de webhook se escriben correctamente; solo falta el permalink.
Identificamos 174 publicaciones afectadas en un solo sitio de nuestra red multisitio. El problema parece haber comenzado alrededor de mediados de diciembre de 2025.
Pasos para Reproducir
-
Publicar una entrada de WordPress configurada para crear un tema de Discourse
-
El tema se crea con éxito en Discourse
-
discourse_topic_idse guarda en los metadatos de la publicación ✓ -
wpdc_sync_post_commentsse establece en1(el webhook se activa correctamente) ✓ -
discourse_permalinkse guarda como una cadena vacía ✗ -
discourse_comments_rawnunca se escribe (la sincronización nunca se completa)
Comportamiento Esperado
discourse_permalink debería contener la URL completa del tema de Discourse (p. ej., The China Chickens Come Home - AVweb - News Discussion - AVweb.com Discussion) después de la creación exitosa del tema.
Comportamiento Actual
discourse_permalink existe como clave de metadatos pero su valor está vacío. Cada llamada posterior a sync_comments() alcanza esta comprobación en la línea 209 de lib/discourse-comment.php y retorna anticipadamente:
if ( ! $discourse_permalink ) {
return 0;
}
Debido a que la sincronización nunca se completa, discourse_last_sync nunca se escribe, por lo que el complemento reintenta en cada carga de página, y falla cada vez.
Diagnóstico
Rastreé el problema a través de la siguiente ruta de código:
-
DiscourseCommentFormatter::format()llama ado_action('wpdc_sync_discourse_comments')lo que activaDiscourseComment::sync_comments() -
sync_comments()compruebadiscourse_permalink— lo encuentra vacío, devuelve 0 -
format()luego compruebadiscourse_comments_rawen los metadatos personalizados de la publicación — lo encuentra ausente, devuelvebad_response_html() -
Los comentarios nunca se muestran para la publicación afectada
El flujo de creación de temas está escribiendo discourse_topic_id pero no logra persistir el permalink. Pudimos reconstruir los permalinks correctos consultando la API de Discourse en /t/{topic_id}.json y escribiéndolos de nuevo en los metadatos de la publicación, lo que resolvió la sincronización para las 174 publicaciones.
Solución Temporal
Escribimos un script de reparación de WP-CLI que:
-
Encuentra publicaciones donde
wpdc_sync_post_comments = 1perodiscourse_comments_rawestá ausente -
Obtiene la etiqueta del tema de
/t/{topic_id}.json -
Reconstruye y guarda el permalink
-
Obtiene y guarda los comentarios de
/t/{slug}/{topic_id}/wordpress.json
Estamos ejecutando esto en un cron programado como medida provisional.
Problemas Adicionales Encontrados Durante la Investigación
1. Bloqueo Global de MySQL Causa Fallos Silenciosos de Sincronización
Archivo: lib/discourse-comment.php, línea 176
$got_lock = $wpdb->get_row( "SELECT GET_LOCK( 'discourse_lock', 0 ) got_it" );
sync_comments() utiliza un único bloqueo global de MySQL (discourse_lock) compartido entre todas las publicaciones de toda la instalación. El tiempo de espera es 0 (no bloqueante), por lo que si alguna publicación se está sincronizando actualmente, todas las demás publicaciones omiten silenciosamente su sincronización; sin registro, sin reintento.
En un sitio de alto volumen que publica varias entradas por día, esto crea una condición de carrera en la que las publicaciones pierden consistentemente el bloqueo y nunca se sincronizan. Combinado con el período de sincronización de 10 minutos, una publicación puede quedarse permanentemente atascada si pierde el bloqueo en sus primeros intentos y luego el problema del permalink vacío impide futuras sincronizaciones.
Corrección sugerida: Usar un bloqueo por publicación:
$got_lock = $wpdb->get_row( "SELECT GET_LOCK( 'discourse_lock_{$post_id}', 0 ) got_it" );
Con la liberación correspondiente:
$wpdb->get_results( "SELECT RELEASE_LOCK( 'discourse_lock_{$post_id}' )" );
2. Doble Escape de discourse_comments_raw
Archivo: lib/discourse-comment.php, línea 222
update_post_meta( $post_id, 'discourse_comments_raw', esc_sql( $raw_body ) );
update_post_meta() utiliza $wpdb->prepare() internamente, por lo que envolver el valor en esc_sql() causa un doble escape. En múltiples ciclos de sincronización, las comillas en el cuerpo JSON acumulan caracteres de escape (\\\" → \\\\\\\" → \\\\\\\\\\\\\\\") hasta que el JSON se vuelve irrecuperable.
Corrección sugerida:
update_post_meta( $post_id, 'discourse_comments_raw', $raw_body );
Impacto
Estos problemas se acumulan: el permalink vacío evita la sincronización inicial, el bloqueo global evita la recuperación y el doble escape puede corromper los datos de las publicaciones que logran sincronizarse. En nuestra instalación, 174 publicaciones (aproximadamente 2 meses de contenido) se vieron afectadas antes de que identificáramos la causa raíz.