Skip to content

Commit 4606c8d

Browse files
committed
Simplify GRPC stream handling
Signed-off-by: Artur Ciocanu <[email protected]>
1 parent 7fdf043 commit 4606c8d

File tree

1 file changed

+16
-53
lines changed

1 file changed

+16
-53
lines changed

sdk/src/main/java/io/dapr/client/DaprClientImpl.java

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,6 @@
134134
import java.util.Iterator;
135135
import java.util.List;
136136
import java.util.Map;
137-
import java.util.concurrent.BlockingQueue;
138-
import java.util.concurrent.LinkedBlockingQueue;
139-
import java.util.concurrent.atomic.AtomicBoolean;
140-
import java.util.concurrent.atomic.AtomicReference;
141137
import java.util.function.Consumer;
142138
import java.util.function.Function;
143139
import java.util.stream.Collectors;
@@ -497,43 +493,15 @@ public <T> Flux<T> subscribeToEvents(String pubsubName, String topic, TypeRef<T>
497493

498494
return Flux.create(sink -> {
499495
var interceptedStub = this.grpcInterceptors.intercept(this.asyncStub);
500-
BlockingQueue<DaprProtos.SubscribeTopicEventsRequestAlpha1> ackQueue = new LinkedBlockingQueue<>(50);
501-
AtomicReference<StreamObserver<DaprProtos.SubscribeTopicEventsRequestAlpha1>> streamRef =
502-
new AtomicReference<>();
503-
AtomicBoolean running = new AtomicBoolean(true);
504-
505-
// Thread to send acknowledgments back to Dapr
506-
Thread acker = new Thread(() -> {
507-
while (running.get()) {
508-
try {
509-
var ackResponse = ackQueue.take();
510-
if (ackResponse == null) {
511-
continue;
512-
}
513496

514-
var stream = streamRef.get();
515-
if (stream == null) {
516-
Thread.sleep(100);
517-
continue;
518-
}
497+
// Use array wrapper to allow assignment within anonymous class (Java's effectively final requirement)
498+
// This is simpler than AtomicReference since we don't need atomicity - just mutability
499+
@SuppressWarnings("unchecked")
500+
StreamObserver<DaprProtos.SubscribeTopicEventsRequestAlpha1>[] streamHolder = new StreamObserver[1];
519501

520-
stream.onNext(ackResponse);
521-
} catch (InterruptedException e) {
522-
Thread.currentThread().interrupt();
523-
return;
524-
} catch (Exception e) {
525-
try {
526-
Thread.sleep(100);
527-
} catch (InterruptedException ex) {
528-
Thread.currentThread().interrupt();
529-
return;
530-
}
531-
}
532-
}
533-
});
534-
535-
// Create the gRPC streaming observer
536-
var stream = interceptedStub.subscribeTopicEventsAlpha1(new StreamObserver<>() {
502+
// Create the gRPC bidirectional streaming observer
503+
// Note: StreamObserver.onNext() is thread-safe, so we can send acks directly
504+
streamHolder[0] = interceptedStub.subscribeTopicEventsAlpha1(new StreamObserver<>() {
537505
@Override
538506
public void onNext(DaprProtos.SubscribeTopicEventsResponseAlpha1 response) {
539507
try {
@@ -562,50 +530,45 @@ public void onNext(DaprProtos.SubscribeTopicEventsResponseAlpha1 response) {
562530
sink.next(data);
563531
}
564532

565-
// Send SUCCESS acknowledgment
533+
// Send SUCCESS acknowledgment directly (no blocking queue or thread needed)
566534
var ack = buildAckRequest(id, SubscriptionListener.Status.SUCCESS);
567-
ackQueue.put(ack);
535+
streamHolder[0].onNext(ack);
568536

569537
} catch (Exception e) {
570538
// On error during processing, send RETRY acknowledgment
571539
try {
572540
var id = response.getEventMessage().getId();
573541
if (id != null && !id.isEmpty()) {
574542
var ack = buildAckRequest(id, SubscriptionListener.Status.RETRY);
575-
ackQueue.put(ack);
543+
streamHolder[0].onNext(ack);
576544
}
577-
} catch (InterruptedException ex) {
578-
Thread.currentThread().interrupt();
545+
} catch (Exception ex) {
546+
// If we can't send ack, propagate the error
547+
sink.error(DaprException.propagate(ex));
548+
return;
579549
}
580550
sink.error(DaprException.propagate(e));
581551
}
582552
}
583553

584554
@Override
585555
public void onError(Throwable throwable) {
586-
running.set(false);
587556
sink.error(DaprException.propagate(throwable));
588557
}
589558

590559
@Override
591560
public void onCompleted() {
592-
running.set(false);
593561
sink.complete();
594562
}
595563
});
596564

597-
streamRef.set(stream);
598-
acker.start();
599-
600565
// Send initial request to start receiving events
601-
stream.onNext(request);
566+
streamHolder[0].onNext(request);
602567

603568
// Cleanup when Flux is cancelled or completed
604569
sink.onDispose(() -> {
605-
running.set(false);
606-
acker.interrupt();
607570
try {
608-
stream.onCompleted();
571+
streamHolder[0].onCompleted();
609572
} catch (Exception e) {
610573
// Ignore cleanup errors
611574
}

0 commit comments

Comments
 (0)