`discourse_permalink` vazio causa falhas silenciosas de sincronização de comentários

Ambiente

  • Versão do WP-Discourse: 2.5.7

  • Versão do Discourse: 3.5.0.beta5-dev

  • Versão do WordPress: 6.9

  • Hospedagem: WP Engine (WordPress multisite)

  • Instância do Discourse: Auto-hospedada / Hospedagem do Discourse (forum.avweb.com)

Resumo

Publicações publicadas a partir do WordPress criam tópicos no Discourse com sucesso, mas discourse_permalink é armazenado como uma string vazia no meta da publicação. Isso faz com que todas as sincronizações de comentários subsequentes falhem silenciosamente, pois sync_comments() em discourse-comment.php é interrompido antecipadamente quando o permalink está vazio. O ID do tópico do Discourse e a flag de sincronização do webhook são gravados corretamente — apenas o permalink está faltando.

Identificamos 174 publicações afetadas em um único site em nossa rede multisite. O problema parece ter começado por volta de meados de dezembro de 2025.

Passos para Reproduzir

  1. Publique uma postagem do WordPress configurada para criar um tópico no Discourse

  2. O tópico é criado com sucesso no Discourse

  3. discourse_topic_id é salvo no meta da publicação ✓

  4. wpdc_sync_post_comments é definido como 1 (o webhook dispara corretamente) ✓

  5. discourse_permalink é salvo como uma string vazia ✗

  6. discourse_comments_raw nunca é gravado (a sincronização nunca é concluída)

Comportamento Esperado

discourse_permalink deve conter o URL completo do tópico do Discourse (ex: The China Chickens Come Home - AVweb - News Discussion - AVweb.com Discussion) após a criação bem-sucedida do tópico.

Comportamento Atual

discourse_permalink existe como uma chave meta, mas seu valor está vazio. Cada chamada subsequente para sync_comments() atinge esta verificação na linha 209 de lib/discourse-comment.php e retorna antecipadamente:

if ( ! $discourse_permalink ) {
    return 0;
}

Como a sincronização nunca é concluída, discourse_last_sync nunca é gravado, então o plugin tenta novamente a cada carregamento de página — e falha todas as vezes.

Diagnóstico

Rastreamos o problema através do seguinte caminho de código:

  1. DiscourseCommentFormatter::format() chama do_action('wpdc_sync_discourse_comments') que aciona DiscourseComment::sync_comments()

  2. sync_comments() verifica discourse_permalink — encontra-o vazio, retorna 0

  3. format() então verifica discourse_comments_raw no custom da publicação — encontra-o ausente, retorna bad_response_html()

  4. Comentários nunca são exibidos para a publicação afetada

O fluxo de criação de tópico está gravando discourse_topic_id, mas falhando em persistir o permalink. Conseguimos reconstruir os permalinks corretos consultando a API do Discourse em /t/{topic_id}.json e gravando-os de volta no meta da publicação, o que resolveu a sincronização para todas as 174 publicações.

Solução Alternativa (Workaround)

Escrevemos um script de reparo WP-CLI que:

  1. Encontra publicações onde wpdc_sync_post_comments = 1, mas discourse_comments_raw está ausente

  2. Busca o slug do tópico em /t/{topic_id}.json

  3. Reconstrói e salva o permalink

  4. Busca e salva comentários de /t/{slug}/{topic_id}/wordpress.json

Estamos executando isso em um cron agendado como uma medida paliativa.


Problemas Adicionais Encontrados Durante a Investigação

1. Bloqueio Global do MySQL Causa Falhas Silenciosas de Sincronização

Arquivo: lib/discourse-comment.php, linha 176

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

sync_comments() usa um único bloqueio MySQL global (discourse_lock) compartilhado entre todas as publicações em toda a instalação. O tempo limite é 0 (não bloqueante), então se alguma publicação estiver sincronizando no momento, todas as outras publicações silenciosamente ignoram sua sincronização — sem registro, sem nova tentativa.

Em um site de alto volume que publica várias postagens por dia, isso cria uma condição de corrida onde as publicações consistentemente perdem o bloqueio e nunca sincronizam. Combinado com o período de sincronização de 10 minutos, uma publicação pode ficar permanentemente presa se perder o bloqueio em suas primeiras tentativas e, em seguida, o problema do permalink vazio impedir sincronizações futuras.

Correção sugerida: Usar um bloqueio por publicação:

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

Com o correspondente liberação:

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

2. Dupla Codificação de discourse_comments_raw

Arquivo: lib/discourse-comment.php, linha 222

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

update_post_meta() usa $wpdb->prepare() internamente, então envolver o valor em esc_sql() causa dupla codificação. Ao longo de vários ciclos de sincronização, as aspas no corpo JSON se acumulam em caracteres de escape (\\\"\\\\\\\"\\\\\\\\\\\\\\\") até que o JSON se torne impossível de analisar.

Correção sugerida:

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

Impacto

Esses problemas se acumulam: o permalink vazio impede a sincronização inicial, o bloqueio global impede a recuperação e a dupla codificação pode corromper dados para publicações que conseguem sincronizar. Em nossa instalação, 174 publicações (aproximadamente 2 meses de conteúdo) foram afetadas antes de identificarmos a causa raiz.