Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Implementando Mensageria com PostgreSQL

Implementando Mensageria com PostgreSQL

A industria tem feito um marketing agressivo em favor de arquiteturas complexas e muito caras, incluindo microsserviços, brokers de mensageria e bancos NoSQL. Para isso, arquiteturas mais simples e, na maioria das vezes suficientes, tornaram-se os vilões para muitas empresas e profissionais, como monolitos e bancos de dados relacionais. Infelizmente, esse último, há mais de 1 decada tem sido vendido apenas como um simples repositório de dados limitado que não permite escalar para milhões de usuários ou dezenas de milhares de requisições.

Essas falácias sobre bancos relacionais precisam acabar, por esse motivo, nessa talk pretendo apresentar como podemos integrar sistemas e serviços sem abrir mão do mundo relacional, tudo isso na perspectiva de um desenvolvedor(a) de software. Em vez de todo um alto custo e complexidade na adoção de Kafka ou RabbitMQ para implementar mensageria em sistemas, nós abraçaremos recursos nativos do próprio PostgreSQL para implementar troca de eventos via fila de mensagens e comunicação Pub/Sub de forma simples, confiável e escalável.

Você vai se surpreender como esses tipos de soluções de mensageria podem ser resolvidos com algumas linhas de SQL, locking distribuído e algum conhecimento sobre as features oferecidas pelo seu banco de dados relacional. No fim, você vai perceber como PostgreSQL é mais do que um repositório de dados, é uma engine completa e robusta de concorrência.

(GRAVAÇÃO: https://www.youtube.com/watch?v=FF6Am0N6eq4&t=840s&ab_channel=Zup)

Rafael Ponte

August 26, 2022
Tweet

More Decks by Rafael Ponte

Other Decks in Technology

Transcript

  1. mas qual o custo? Segurança Backup Replicação Monitoramento Upgrade Recovery

    Fail-over Alta-disponibilidade Hardware Contratar 
 especialistas
  2. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  3. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  4. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  5. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  6. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  7. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  8. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  9. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  10. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  11. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  12. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  13. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  14. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  15. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  16. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  17. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  18. 4 3 2 5 1 enqueue dequeue QUEUE ( fi

    la) FIFO: First-in, First-out
  19. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  20. @RestController class FinalizaPedidoController { @Transactional @PostMapping(“/api/pedidos/finaliza”) public PedidoResponse finaliza(@RequestBody PedidoRequest

    request) { 
 // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal 
 
 return new PedidoResponse(pedido, Status.CONFIRMADO); } }
  21. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } }
  22. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } }
  23. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } }
  24. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } }
  25. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } }
  26. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } } “Estamos processando seu pedido"
  27. @RestController class FinalizaPedidoController { private PedidoRepository repository; @Transactional @PostMapping(“/api/pedidos/finaliza”) public

    PedidoResponse finaliza(@RequestBody PedidoRequest request) { Pedido pedido = request.toModel(); pedido.setStatus(Status.PENDENTE); 
 repository.save(pedido); 
 return new PedidoResponse(request, Status.PENDENTE); } }
  28. @Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void

    runQuiteOften() { // lógica do job vai aqui } }
  29. @Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void

    runQuiteOften() { // lógica do job vai aqui } }
  30. @Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void

    runQuiteOften() { // lógica do job vai aqui } }
  31. @Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void

    runQuiteOften() { // lógica do job vai aqui } }
  32. @Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void

    runQuiteOften() { // lógica do job vai aqui } }
  33. @Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void

    runQuiteOften() { // lógica do job vai aqui } }
  34. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  35. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  36. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  37. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  38. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  39. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  40. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  41. 9630 9631 QUEUE ( fi la) producer consumer 9629 produz:

    
 1000 itens/min consume: 
 600 itens/min
  42. 9630 9631 QUEUE ( fi la) producer consumer 9629 produz:

    
 1000 itens/min consumer consume: 
 400 itens/min consume: 
 600 itens/min
  43. 9630 9631 QUEUE ( fi la) producer consumer 9629 produz:

    
 1000 itens/min consumer consume: 
 400 itens/min consume: 
 600 itens/min
  44. @Repository public interface PedidoRepository extends JpaRepository<Pedido, Long> { @QueryHints({ @QueryHint(

    name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions }) @Lock(LockModeType.PESSIMISTIC_WRITE) public List<Pedido> findAllByStatus(Status status); }
  45. @Repository public interface PedidoRepository extends JpaRepository<Pedido, Long> { @QueryHints({ @QueryHint(

    name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions }) @Lock(LockModeType.PESSIMISTIC_WRITE) public List<Pedido> findAllByStatus(Status status); }
  46. select p.* from pedido p where p.status = 'PENDENTE' order

    by p.criado_em asc 
 for update skip locked
  47. pg_notify() NOTIFY is a utility command for sending noti fi

    cations to other sessions connected to the same database listening on a channel speci fi ed with the LISTEN command.
  48. LISTEN LISTEN is a utility command which registers the current

    session as a listener on the named channel.
  49. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  50. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  51. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  52. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  53. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  54. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido // envia email de confirmação // gera nota fiscal pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  55. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido repository 
 .pgNotify("pedidos-finalizados", pedido.toJson()); pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  56. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido repository 
 .pgNotify("pedidos-finalizados", pedido.toJson()); pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  57. @Component public class FinalizaPedidoJob { private PedidoRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Pedido> pedidos = repository.findAllByStatus(Status.PENDENTE); pedidos.forEach(pedido -> { // efetua pagamento no gateway // dá baixa no estoque // atualiza pedido repository 
 .pgNotify("pedidos-finalizados", pedido.toJson()); pedido.setStatus(Status.CONFIRMADO); repository.save(pedido); }); } }
  58. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  59. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  60. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  61. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } } Ouvindo um topico!
  62. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  63. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  64. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } } Bloqueia 
 FOREVER até receber novos eventos
  65. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  66. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  67. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  68. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) { for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } } break; } }
  69. try (Connection cn = new org.postgresql.Driver().connect(url, properties)) { cn.createStatement().execute(“LISTEN ‘pedidos-finalizados’");

    for (;;) { PGNotification[] ns = ((PGConnection) cn).getNotifications(0); if (ns != null) for (PGNotification n : ns) { System.out.println("pid=%s, event=%s, payload=%s".formatted( 
 n.getPID(), n.getName(), n.getParameter() )); // invoca logica de negocio } break; } }
  70. INSERT CONFIRMADO CONFIRMADO PENDENTE LISTEN ‘pedidos-finalizados’ LISTEN ‘pedidos-finalizados’ pg_notify( 


    ‘pedidos-finalizados’, ‘{“id”: 42, “nome”: ... }’ 
 ) Queue
  71. INSERT CONFIRMADO CONFIRMADO PENDENTE LISTEN ‘pedidos-finalizados’ LISTEN ‘pedidos-finalizados’ pg_notify( 


    ‘pedidos-finalizados’, ‘{“id”: 42, “nome”: ... }’ 
 ) Pub/Sub