Class RelayM2m

java.lang.Object
br.com.xadm.comum.mensageria.RelayM2m

@Singleton public class RelayM2m extends Object
Relay durável do outbox. A cada fixed-delay carrega o lote de saída (PENDENTE, ERRO) abaixo do teto e despacha cada mensagem pela SPI tipada. Cada transição de estado roda em transação curta própria (via MensageriaService, cross-bean) — isolamento por mensagem: falha (ou exceção inesperada do enviador) marca só aquela linha, o lote segue.

Sob N réplicas do MESMO app o tick é serializado por advisory lock (GuardaCluster, chave <app>_relay_m2m). O filtro por tipo do executar() separa apps DIFERENTES; réplicas do mesmo app registram os mesmos tipos, casam o mesmo predicado e, sem a guarda, entregam a mesma linha ao mesmo tempo — o efeito externo (o PUT) sai em dobro.

Contrato público — três pontos de entrada, de propósito distintos:

  • executar() — passe direto, SEM eleição. Drena o lote e pronto.
  • executarSeEleito() — passe sob eleição: é o mesmo trabalho, mas só roda na réplica que ganha o advisory lock. É por aqui que o adotante exercita a guarda no teste dele.
  • tick() — o disparo periódico (@Scheduled). Não é API: package-private, sem compromisso de SemVer; só aplica o gate mensageria.relay.enabled e delega.

Isso remove a duplicação evitável, não a inerente: segue AT-LEAST-ONCE (crash entre a resposta do destino e o commit ainda reentrega) e o receptor segue obrigado a deduplicar por chave natural (decisão 0021). Não é lock de linha, e não pode ser: o relay seleciona fora de transação e despacha cada mensagem na transação curta dela, então um FOR UPDATE SKIP LOCKED no select seria solto antes do envio. Reivindicar por linha exigiria estado persistido (status em-voo + marca de reivindicação + reaper de órfão) — o estado a mais que a 0021 descartou.

  • Constructor Details

  • Method Details

    • executarSeEleito

      public boolean executarSeEleito()
      Processa um lote de saída sob eleição: adquire o advisory lock <app>_relay_m2m e só então delega ao executar(). A réplica que não adquire coalesce (não roda, não enfileira) e o próximo passe reencaixa.

      É o seam da guarda: o adotante prova no teste dele que, com o lock ocupado por outra sessão, o relay não despacha — sem depender do agendador nem do tick(), que não é API. O gate mensageria.relay.enabled não se aplica aqui (é do disparo periódico); a escotilha mensageria.relay.cluster-lock=false sim: desligada a guarda, este método vira passe direto e retorna true.

      Returns:
      true se rodou (ganhou o lock, ou a guarda está desligada); false se coalesceu porque outra réplica detinha o lock.
    • executar

      public int executar()
      Processa um lote de saída sem eleição: passe direto, para o teste e para um eventual "rodar agora". Quem precisa da guarda entre réplicas usa executarSeEleito().
      Returns:
      quantidade de mensagens processadas neste passe.