Postgres includes a Notifier/Listener pattern to track the messages on database, for example.
@Component
@Slf4j
class Listener {
@Autowired
@Qualifier("pgConnectionFactory")
ConnectionFactory pgConnectionFactory;
PostgresqlConnection receiver;
@PostConstruct
public void initialize() throws InterruptedException {
receiver = Mono.from(pgConnectionFactory.create())
.cast(PostgresqlConnection.class)
.block();
receiver.createStatement("LISTEN mymessage")
.execute()
.flatMap(PostgresqlResult::getRowsUpdated)
.log("listen::")
.subscribe();
receiver.getNotifications()
.delayElements(Duration.ofSeconds(1))
.log()
.subscribe(
data -> log.info("notifications: {}", data)
);
}
@PreDestroy
public void destroy() {
receiver.close().subscribe();
}
}
The whole example is here.
As you mentioned, with Mongo capped collections, it is easy to emit the item to a reactive Flux sink, check my fullstack(frontend+backend) example of Http/SSE, WebSocket, RSocket.
You can emit any data to a connnectable flux by your own logic, such as emitting data by fine-grained domain events, this usage is more generic in real world projects.
@Service
@RequiredArgsConstructor
@Slf4j
public class PostService {
//...
public Mono<Comment> addComment(CommentInput commentInput) {
String postId = commentInput.getPostId();
return this.posts.findById(UUID.fromString(postId))
.flatMap(p -> this.comments.create(commentInput.getContent(), UUID.fromString(postId)))
.flatMap(id -> this.comments.findById(id).map(COMMENT_MAPPER))
.doOnNext(c -> {
log.debug("emitting comment: {}", c);
sink.emitNext(c, Sinks.EmitFailureHandler.FAIL_FAST);
})
.switchIfEmpty(Mono.error(new PostNotFoundException(postId)));
}
private final Sinks.Many<Comment> sink = Sinks.many().replay().latest();
public Flux<Comment> commentAddedEvent() {
return sink.asFlux();
}
}
Any of your client can connect to this commentAddedEvent
. For example, the following is using SSE.
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Comment> commentsStream(){
this.postService.commentAddedEvent();
}
Similarly, if you are using WebSocket, use a WebSocketHandler to adapt it, and for RSocket, use a controller with messaging mapping route instead.