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

Messaging with PostgreSQL: Designing Scalable a...

Avatar for Rafael Ponte Rafael Ponte
September 03, 2025

Messaging with PostgreSQL: Designing Scalable and Reliable Queues and Pub/Sub within your Database

(🎥 RECORDING: https://vimeo.com/1115459661)

The industry has aggressively marketed complex and expensive architectures, including microservices, messaging brokers, and NoSQL databases. As a result, simpler architectures, which are often sufficient, have been vilified by many companies and professionals—architectures such as monoliths and relational databases. Unfortunately, for over a decade, the latter has been dismissed as merely a limited data repository incapable of scaling to millions of users or handling tens of thousands of requests.

These misconceptions about relational databases need to end. That’s why, in this talk, I will demonstrate how we can integrate systems and services without abandoning the relational world, all from the perspective of a software developer. Instead of bearing the high cost and complexity of adopting Kafka or RabbitMQ to implement messaging in systems, we will leverage PostgreSQL’s native capabilities to implement message queuing and Pub/Sub communication in a simple, reliable, and scalable way.

You’ll be surprised at how these types of messaging solutions can be addressed with just a few lines of SQL, distributed locking, and a solid understanding of the features offered by your relational database. By the end of this talk, you’ll realize that PostgreSQL is more than just a data repository—it’s a complete and robust concurrency engine.

Avatar for Rafael Ponte

Rafael Ponte

September 03, 2025
Tweet

More Decks by Rafael Ponte

Other Decks in Programming

Transcript

  1. No!

  2. /rponte Rafael Ponte Brazilian Software Engineer at aka Prince of

    the Ocean aka Maharaja of the Legacies 20+ years of experience working with Java and distributed systems Loves working with "boring techs"
  3. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  4. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  5. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  6. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  7. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  8. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  9. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  10. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  11. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  12. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  13. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  14. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  15. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  16. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  17. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  18. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  19. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  20. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  21. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  22. @RestController class CheckoutController { @Transactional @PostMapping("/api/orders/checkout") public OrderResponse checkout(@RequestBody OrderRequest

    request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  23. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification return new OrderResponse(order, Status.CONFIRMED); } }
  24. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } }
  25. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } }
  26. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } }
  27. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } }
  28. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } }
  29. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } } “We are processing your order.”
  30. @RestController class CheckoutController { private OrderRepository repository; @Transactional @PostMapping("/api/orders/checkout") public

    OrderResponse checkout(@RequestBody OrderRequest request) { Order order = request.toModel(); order.setStatus(Status.PENDING); repository.save(order); return new OrderResponse(order, Status.PENDING); } }
  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 ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Order> orders = repository.findAllByStatus(Status.PENDING); orders.forEach(order -> { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); }); } }
  34. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Order> orders = repository.findAllByStatus(Status.PENDING); orders.forEach(order -> { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); }); } }
  35. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { List<Order> orders = repository.findAllByStatus(Status.PENDING); orders.forEach(order -> { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); }); } }
  36. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  37. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  38. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  39. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  40. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  41. No!

  42. select o.* from order o where o.status = 'PENDING' order

    by o.created_at asc limit 1 for update
  43. No!

  44. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @QueryHints({ @QueryHint(

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

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

    name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions }) @Lock(LockModeType.PESSIMISTIC_WRITE) public Order findFirstByStatus(Status status); }
  47. select o.* from order o where o.status = 'PENDING' order

    by o.created_at asc limit 1 for update
  48. select o.* from order o where o.status = 'PENDING' order

    by o.created_at asc limit 1 for update skip locked
  49. Point-to-Point Pub/Sub Consumers pull messages, process and remove them Publish

    messages to many subscribers and each subscriber gets a copy of the message to process https://eda-visuals.boyney.io/visuals/queues-vs-streams-vs-pubsub MESSAGING PATTERNS
  50. Point-to-Point Consumers pull messages, process and remove them https://eda-visuals.boyney.io/visuals/queues-vs-streams-vs-pubsub Pub/Sub

    Publish messages to many subscribers and each subscriber gets a copy of the message to process MESSAGING PATTERNS
  51. https://eda-visuals.boyney.io/visuals/queues-vs-streams-vs-pubsub Pub/Sub Publish messages to many subscribers and each subscriber

    gets a copy of the message to process Point-to-Point Consumers pull messages, process and remove them MESSAGING PATTERNS
  52. 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.
  53. LISTEN LISTEN is a utility command which registers the current

    session as a listener on the named channel.
  54. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  55. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  56. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  57. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  58. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  59. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status // generate invoice // send confirmation email/notification order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  60. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status repository .publish(“orders-confirmed-topic“, order.toJson()); order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  61. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status repository .publish(“orders-confirmed-topic“, order.toJson()); order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  62. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status repository .publish(“orders-confirmed-topic“, order.toJson()); order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  63. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status repository .publish(“orders-confirmed-topic“, order.toJson()); order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  64. @Component public class ProcessPendingOrdersJob { private OrderRepository repository; @Scheduled(fixedDelay =

    60_000) public void execute() { Order order; while ((order = repository.findFirstByStatus(PENDING)) != null) { // authorize payment with the gateway // reserve or deduct items from inventory // confirm and update order status repository .publish(“orders-confirmed-topic“, order.toJson()); order.setStatus(Status.CONFIRMED); repository.save(order); } } }
  65. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query( nativeQuery

    = true, value = "SELECT pg_notify(:topic, :message)" ) public void publish(String topic, String message); // other methods }
  66. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query( nativeQuery

    = true, value = "SELECT pg_notify(:topic, :message)" ) public void publish(String topic, String message); // other methods }
  67. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query( nativeQuery

    = true, value = "SELECT pg_notify(:topic, :message)" ) public void publish(String topic, String message); // other methods }
  68. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query( nativeQuery

    = true, value = "SELECT pg_notify(:topic, :message)" ) public void publish(String topic, String message); // other methods }
  69. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query( nativeQuery

    = true, value = "SELECT pg_notify(:topic, :message)" ) public void publish(String topic, String message); // other methods }
  70. @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query( nativeQuery

    = true, value = "SELECT pg_notify(:topic, :message)" ) public void publish(String topic, String message); // other methods }
  71. INSERT PENDING PG_NOTIFY( ‘orders-confirmed-topic’, ‘{“id”: 42, “name”: ... }’ )

    PENDING PENDING LISTEN ‘orders-confirmed-topic’ LISTEN ‘orders-confirmed-topic
  72. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  73. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  74. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  75. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’”);

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } } Listen to a topic
  76. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  77. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  78. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } } Blocks FOREVER until new events are received
  79. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  80. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  81. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  82. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  83. try (Connection conn = new org.postgresql.Driver().connect(url, properties)) { conn.createStatement().execute(“LISTEN ‘orders-confirmed-topic’");

    while (true) { PGNotification[] ns = ((PGConnection) conn).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() )); // executes the business logic here } } } }
  84. @Component public class OrdersConfirmedListener { @EventListener(ApplicationReadyEvent.class) public void onApplicationReady() {

    Thread thread = new Thread(this::listenEvents, "pg-listener-thread"); thread.setDaemon(true); thread.start(); } private void listenEvents() { // executes our listener code here } }
  85. INSERT CONFIRMED CONFIRMED PENDING LISTEN ‘orders-confirmed-topic’ pg_notify( ‘orders-confirmed-topic’, ‘{“id”: 42,

    “name”: ... }’ ) LISTEN ‘orders-confirmed-topic’ LISTEN ‘orders-confirmed-topic
  86. INSERT CONFIRMED CONFIRMED PENDING LISTEN ‘orders-confirmed-topic’ pg_notify( ‘orders-confirmed-topic’, ‘{“id”: 42,

    “name”: ... }’ ) LISTEN ‘orders-confirmed-topic’ LISTEN ‘orders-confirmed-topic
  87. No!