fix: The sink is recreated, if it gets canceled
authorKai Moritz <kai@juplo.de>
Sat, 7 Jan 2023 01:16:57 +0000 (02:16 +0100)
committerKai Moritz <kai@juplo.de>
Sun, 15 Jan 2023 18:35:59 +0000 (19:35 +0100)
- The sink is automatically cancelled, if the last consumer leaves.
- Turning auto-cancel of, is not an option, because this buffers new
  messages, until the buffer overflows, after which new messages are
  droped. Hence, if a new subscriber arrives, it would see some old
  messages and then, after a gap, current messages.
- Because of that, the sink is now automatically recreated, if the
  last subscriber leaves and triggers the automatically cancelation
  of the sink.
- The sink can be recreated without conflicts, because all methods,
  that may access the sink are synchronized.

src/main/java/de/juplo/kafka/chat/backend/domain/Chatroom.java

index 966a28e..60f7274 100644 (file)
@@ -18,7 +18,8 @@ public class Chatroom
   @Getter
   private final String name;
   private final PersistenceStrategy persistence;
-  private final Sinks.Many<Message> sink;
+  private final int bufferSize;
+  private Sinks.Many<Message> sink;
 
   public Chatroom(
       UUID id,
@@ -29,7 +30,8 @@ public class Chatroom
     this.id = id;
     this.name = name;
     this.persistence = persistence;
-    this.sink = Sinks.many().multicast().onBackpressureBuffer(bufferSize);
+    this.bufferSize = bufferSize;
+    this.sink = createSink();
   }
 
 
@@ -41,7 +43,14 @@ public class Chatroom
   {
     return persistence
         .persistMessage(Message.MessageKey.of(user, id), timestamp, text)
-        .doOnNext(message -> sink.tryEmitNext(message).orThrow());
+        .doOnNext(message ->
+        {
+          Sinks.EmitResult result = sink.tryEmitNext(message);
+          if (result.isFailure())
+          {
+            log.warn("Emitting of message failed with {} for {}", result.name(), message);
+          }
+        });
   }
 
 
@@ -50,9 +59,11 @@ public class Chatroom
     return persistence.getMessage(Message.MessageKey.of(username, messageId));
   }
 
-  public Flux<Message> listen()
+  synchronized public Flux<Message> listen()
   {
-    return sink.asFlux();
+    return sink
+        .asFlux()
+        .doOnCancel(() -> sink = createSink()); // Sink hast to be recreated on auto-cancel!
   }
 
   public Flux<Message> getMessages()
@@ -64,4 +75,12 @@ public class Chatroom
   {
     return persistence.getMessages(first, last);
   }
+
+  private Sinks.Many<Message> createSink()
+  {
+    return Sinks
+        .many()
+        .multicast()
+        .onBackpressureBuffer(bufferSize);
+  }
 }