Aplicação 2: Motor de Webhooks Resiliente com Retry Pattern

Integrar sistemas externos é uma tarefa cotidiana, mas fazê-lo de forma resiliente é o verdadeiro desafio.
Nesta aplicação, vamos desenvolver um motor de Webhooks para enviar dados do WordPress para APIs externas de forma assíncrona.
Se o seu servidor tentar enviar um webhook e a API de destino estiver fora do ar, o processo não deve travar.
Utilizaremos uma arquitetura de “Fila e Tentativas” (Retry Pattern).
Isso garante que a informação seja entregue eventualmente, sem degradar a experiência do usuário que está salvando um post.
Vamos usar tabelas personalizadas para gerenciar o status de cada entrega.

Definição da Tabela de Filas

A nossa tabela precisa armazenar o payload (em JSON), a URL de destino e o número de tentativas já realizadas.
O campo next_attempt_at é vital para não sobrecarregar o sistema com tentativas imediatas em caso de falha.
Aplicaremos o conceito de “Backoff Exponencial”.
Isso significa que a cada falha, o tempo de espera para a próxima tentativa aumenta progressivamente.
Esta é uma prática padrão em sistemas de alta disponibilidade.

CREATE TABLE wp_webhook_queue (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    url VARCHAR(255) NOT NULL,
    payload JSON NOT NULL,
    status ENUM('pending', 'processing', 'failed', 'completed') DEFAULT 'pending',
    attempts TINYINT DEFAULT 0,
    next_attempt_at DATETIME,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

O “porquê” de usar o tipo JSON nativo do MySQL é a facilidade de depuração.
Podemos realizar consultas SQL para encontrar webhooks que contenham um ID de usuário específico dentro do payload.
Isso dá uma flexibilidade enorme para o time de suporte técnico.
Agora, precisamos de um worker que processe essa fila.
No WordPress, utilizaremos o wp_schedule_event ou, para sistemas sênior, um processo via CLI rodando em background.

// Função de disparo do worker
function process_webhook_queue() {
    global $wpdb;
    $jobs = $wpdb->get_results("SELECT * FROM wp_webhook_queue WHERE status = 'pending' AND next_attempt_at <= NOW() LIMIT 5");

    foreach ($jobs as $job) {
        $response = wp_remote_post($job->url, ['body' => $job->payload]);
        
        if (is_wp_error($response)) {
            $new_attempts = $job->attempts + 1;
            $delay = pow(2, $new_attempts) * 60; // 2, 4, 8 minutos...
            $wpdb->update('wp_webhook_queue', [
                'status' => 'pending',
                'attempts' => $new_attempts,
                'next_attempt_at' => date('Y-m-d H:i:s', time() + $delay)
            ], ['id' => $job->id]);
        } else {
            $wpdb->update('wp_webhook_queue', ['status' => 'completed'], ['id' => $job->id]);
        }
    }
}

Note como a lógica de retentativa protege o sistema.
Utilizamos o wp_remote_post que é a forma segura do core lidar com requisições HTTP.
Se o sistema de destino estiver com 502 Bad Gateway, nossa aplicação não desiste imediatamente.
Ela aguarda o tempo exponencial e tenta novamente.
Para o desenvolvedor avançado, esta aplicação pode ser conectada ao RabbitMQ para escala massiva.
No MundoPHP, priorizamos arquiteturas que não quebram sob pressão.

Rolar para cima