Fix some connection errors
This commit is contained in:
parent
58770ca649
commit
677ceb70a1
@ -148,7 +148,7 @@ public class AtomixReactiveApi implements ReactiveApi {
|
|||||||
if (publisher != null) {
|
if (publisher != null) {
|
||||||
publisher.handleRequest(req.data());
|
publisher.handleRequest(req.data());
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Dropped request because no session is found: {}", req);
|
LOG.debug("Dropped request because no session is found: {}", req);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
|
@ -67,6 +67,7 @@ import reactor.core.publisher.Flux;
|
|||||||
import reactor.core.publisher.FluxSink.OverflowStrategy;
|
import reactor.core.publisher.FluxSink.OverflowStrategy;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.publisher.Sinks.EmitFailureHandler;
|
import reactor.core.publisher.Sinks.EmitFailureHandler;
|
||||||
|
import reactor.core.publisher.Sinks.EmitResult;
|
||||||
import reactor.core.publisher.Sinks.Many;
|
import reactor.core.publisher.Sinks.Many;
|
||||||
import reactor.core.scheduler.Scheduler.Worker;
|
import reactor.core.scheduler.Scheduler.Worker;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
@ -503,8 +504,21 @@ public abstract class ReactiveApiPublisher {
|
|||||||
|
|
||||||
public void handleRequest(OnRequest<TdApi.Object> onRequestObj) {
|
public void handleRequest(OnRequest<TdApi.Object> onRequestObj) {
|
||||||
handleRequestInternal(onRequestObj, response -> {
|
handleRequestInternal(onRequestObj, response -> {
|
||||||
|
EmitResult status;
|
||||||
synchronized (this.responses) {
|
synchronized (this.responses) {
|
||||||
this.responses.emitNext(response, EmitFailureHandler.FAIL_FAST);
|
status = this.responses.tryEmitNext(response);
|
||||||
|
}
|
||||||
|
if (status.isFailure()) {
|
||||||
|
switch (status) {
|
||||||
|
case FAIL_ZERO_SUBSCRIBER ->
|
||||||
|
LOG.warn("Failed to send response of request {}, user {}, client {}: no subscribers",
|
||||||
|
onRequestObj.userId(), onRequestObj.userId(), onRequestObj.clientId());
|
||||||
|
case FAIL_OVERFLOW ->
|
||||||
|
LOG.warn("Failed to send response of request {}, user {}, client {}: too many unsent responses",
|
||||||
|
onRequestObj.userId(), onRequestObj.userId(), onRequestObj.clientId());
|
||||||
|
default -> LOG.error("Failed to send response of request {}, user {}, client {}: {}",
|
||||||
|
onRequestObj.userId(), onRequestObj.userId(), onRequestObj.clientId(), status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -185,16 +185,28 @@ public class ReactorUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
AtomicReference<Throwable> startEx = new AtomicReference<>();
|
||||||
var disposable = flux
|
var disposable = flux
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
.publishOn(Schedulers.boundedElastic())
|
.publishOn(Schedulers.boundedElastic())
|
||||||
.subscribe(queue::add);
|
.subscribe(queue::add, ex -> {
|
||||||
|
startEx.set(ex);
|
||||||
|
var refVal = ref.get();
|
||||||
|
if (refVal != null) {
|
||||||
|
refVal.error(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
queue.startQueue();
|
queue.startQueue();
|
||||||
return Flux.create(sink -> {
|
return Flux.create(sink -> {
|
||||||
sink.onDispose(() -> {
|
sink.onDispose(() -> {
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
queue.close();
|
queue.close();
|
||||||
});
|
});
|
||||||
|
var startExVal = startEx.get();
|
||||||
|
if (startExVal != null) {
|
||||||
|
sink.error(startExVal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ref.set(sink);
|
ref.set(sink);
|
||||||
sink.onCancel(() -> ref.set(null));
|
sink.onCancel(() -> ref.set(null));
|
||||||
});
|
});
|
||||||
|
@ -99,7 +99,9 @@ public class Stats extends Thread {
|
|||||||
processedUpdatesRateSum += processedUpdatesRate;
|
processedUpdatesRateSum += processedUpdatesRate;
|
||||||
clientBoundEventsRateSum += clientBoundEventsRate;
|
clientBoundEventsRateSum += clientBoundEventsRate;
|
||||||
sentClientBoundEventsRateSum += sentClientBoundEventsRate;
|
sentClientBoundEventsRateSum += sentClientBoundEventsRate;
|
||||||
out.append(String.format("%d:\t", clientIds.getLong(i)));
|
if (LOG.isTraceEnabled()) {
|
||||||
|
out.append(String.format("%d:\t", clientIds.getLong(i)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
receivedUpdatesRate = receivedUpdatesRateSum;
|
receivedUpdatesRate = receivedUpdatesRateSum;
|
||||||
diskBuffered = diskBufferedSum;
|
diskBuffered = diskBufferedSum;
|
||||||
@ -110,16 +112,18 @@ public class Stats extends Thread {
|
|||||||
sentClientBoundEventsRate = sentClientBoundEventsRateSum;
|
sentClientBoundEventsRate = sentClientBoundEventsRateSum;
|
||||||
out.append("Total:\t");
|
out.append("Total:\t");
|
||||||
}
|
}
|
||||||
out.append(String.format(
|
if (i == currentClients || LOG.isTraceEnabled()) {
|
||||||
"\tUpdates:\t[received %03.2fHz\tbuffered: %03.2fHz (RAM: %d HDD: %d)\tprocessed: %03.2fHz]\tClient bound events: %03.2fHz\tProcessed events: %03.2fHz\t%n",
|
out.append(String.format(
|
||||||
receivedUpdatesRate,
|
"\tUpdates:\t[received %03.2fHz\tbuffered: %03.2fHz (RAM: %d HDD: %d)\tprocessed: %03.2fHz]\tClient bound events: %03.2fHz\tProcessed events: %03.2fHz\t%n",
|
||||||
bufferedUpdatesRate,
|
receivedUpdatesRate,
|
||||||
ramBuffered,
|
bufferedUpdatesRate,
|
||||||
diskBuffered,
|
ramBuffered,
|
||||||
processedUpdatesRate,
|
diskBuffered,
|
||||||
clientBoundEventsRate,
|
processedUpdatesRate,
|
||||||
sentClientBoundEventsRate
|
clientBoundEventsRate,
|
||||||
));
|
sentClientBoundEventsRate
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out.append(String.format("%n"));
|
out.append(String.format("%n"));
|
||||||
|
|
||||||
|
@ -47,14 +47,13 @@ public class TdlibChannelsSharedHost implements Closeable {
|
|||||||
private final TdlibChannelsServers tdServersChannels;
|
private final TdlibChannelsServers tdServersChannels;
|
||||||
private final Disposable responsesSub;
|
private final Disposable responsesSub;
|
||||||
private final AtomicReference<Disposable> requestsSub = new AtomicReference<>();
|
private final AtomicReference<Disposable> requestsSub = new AtomicReference<>();
|
||||||
private final Many<OnResponse<TdApi.Object>> responses = Sinks.many().multicast().directAllOrNothing();
|
private final Many<OnResponse<TdApi.Object>> responses = Sinks.many().multicast().onBackpressureBuffer(65_535);
|
||||||
private final Map<String, Many<Flux<ClientBoundEvent>>> events;
|
private final Map<String, Many<Flux<ClientBoundEvent>>> events;
|
||||||
private final Flux<Timestamped<OnRequest<Object>>> requests;
|
private final Flux<Timestamped<OnRequest<Object>>> requests;
|
||||||
|
|
||||||
public TdlibChannelsSharedHost(Set<String> allLanes, TdlibChannelsServers tdServersChannels) {
|
public TdlibChannelsSharedHost(Set<String> allLanes, TdlibChannelsServers tdServersChannels) {
|
||||||
this.tdServersChannels = tdServersChannels;
|
this.tdServersChannels = tdServersChannels;
|
||||||
this.responsesSub = Mono.defer(() -> tdServersChannels.response()
|
this.responsesSub = tdServersChannels.response().sendMessages(responses.asFlux()/*.log("responses", Level.FINE)*/)
|
||||||
.sendMessages(responses.asFlux()/*.log("responses", Level.FINE)*/))
|
|
||||||
.repeatWhen(REPEAT_STRATEGY)
|
.repeatWhen(REPEAT_STRATEGY)
|
||||||
.retryWhen(RETRY_STRATEGY)
|
.retryWhen(RETRY_STRATEGY)
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
@ -64,8 +63,7 @@ public class TdlibChannelsSharedHost implements Closeable {
|
|||||||
Flux<ClientBoundEvent> outputEventsFlux = Flux
|
Flux<ClientBoundEvent> outputEventsFlux = Flux
|
||||||
.merge(sink.asFlux().map(flux -> flux.publishOn(Schedulers.parallel())), Integer.MAX_VALUE, 256)
|
.merge(sink.asFlux().map(flux -> flux.publishOn(Schedulers.parallel())), Integer.MAX_VALUE, 256)
|
||||||
.doFinally(s -> LOG.debug("Output events flux of lane \"{}\" terminated with signal {}", lane, s));
|
.doFinally(s -> LOG.debug("Output events flux of lane \"{}\" terminated with signal {}", lane, s));
|
||||||
Mono.defer(() -> tdServersChannels.events(lane)
|
tdServersChannels.events(lane).sendMessages(outputEventsFlux)
|
||||||
.sendMessages(outputEventsFlux))
|
|
||||||
.repeatWhen(REPEAT_STRATEGY)
|
.repeatWhen(REPEAT_STRATEGY)
|
||||||
.retryWhen(RETRY_STRATEGY)
|
.retryWhen(RETRY_STRATEGY)
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
@ -92,9 +90,9 @@ public class TdlibChannelsSharedHost implements Closeable {
|
|||||||
if (eventsSink == null) {
|
if (eventsSink == null) {
|
||||||
throw new IllegalArgumentException("Lane " + lane + " does not exist");
|
throw new IllegalArgumentException("Lane " + lane + " does not exist");
|
||||||
}
|
}
|
||||||
eventsSink.emitNext(eventFlux.takeUntilOther(canceller.asMono()),
|
synchronized (events) {
|
||||||
EmitFailureHandler.busyLooping(Duration.ofMillis(100))
|
eventsSink.emitNext(eventFlux.takeUntilOther(canceller.asMono()), EmitFailureHandler.FAIL_FAST);
|
||||||
);
|
}
|
||||||
return () -> canceller.tryEmitEmpty();
|
return () -> canceller.tryEmitEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import reactor.core.publisher.Sinks.EmitFailureHandler;
|
|||||||
import reactor.core.publisher.Sinks.Empty;
|
import reactor.core.publisher.Sinks.Empty;
|
||||||
import reactor.core.publisher.Sinks.Many;
|
import reactor.core.publisher.Sinks.Many;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
import reactor.util.concurrent.Queues;
|
|
||||||
|
|
||||||
public class ConsumerConnection<T> {
|
public class ConsumerConnection<T> {
|
||||||
|
|
||||||
@ -61,20 +60,7 @@ public class ConsumerConnection<T> {
|
|||||||
return remote.doOnError(ex -> {
|
return remote.doOnError(ex -> {
|
||||||
synchronized (ConsumerConnection.this) {
|
synchronized (ConsumerConnection.this) {
|
||||||
if (remoteCount <= 1) {
|
if (remoteCount <= 1) {
|
||||||
if (remoteCount > 0 && localTerminationState == null) {
|
onRemoteLastError(ex);
|
||||||
localTerminationState = Optional.of(ex);
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("%s Local connection ended with failure".formatted(this.printStatus()), ex);
|
|
||||||
}
|
|
||||||
if (remoteCount <= 1) {
|
|
||||||
var sink = localTerminationSink;
|
|
||||||
reset();
|
|
||||||
sink.emitError(ex, EmitFailureHandler.FAIL_FAST);
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("%s Local connection ended with failure, emitted termination failure".formatted(this.printStatus()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
remoteCount--;
|
remoteCount--;
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
@ -88,17 +74,7 @@ public class ConsumerConnection<T> {
|
|||||||
synchronized (ConsumerConnection.this) {
|
synchronized (ConsumerConnection.this) {
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote connection ending with status {}", this.printStatus(), s);
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote connection ending with status {}", this.printStatus(), s);
|
||||||
if (remoteCount <= 1) {
|
if (remoteCount <= 1) {
|
||||||
if (remoteCount > 0 && localTerminationState == null) {
|
onLastFinally(s);
|
||||||
assert connectedState;
|
|
||||||
localTerminationState = Optional.empty();
|
|
||||||
if (s == SignalType.CANCEL) {
|
|
||||||
localTerminationSink.emitError(new CancelledChannelException(), EmitFailureHandler.FAIL_FAST);
|
|
||||||
} else {
|
|
||||||
localTerminationSink.emitEmpty(EmitFailureHandler.FAIL_FAST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote connection ended with status {}, emitted termination complete", this.printStatus(), s);
|
|
||||||
} else {
|
} else {
|
||||||
remoteCount--;
|
remoteCount--;
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote connection ended with status {}, but at least one remote is still online", this.printStatus(), s);
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote connection ended with status {}, but at least one remote is still online", this.printStatus(), s);
|
||||||
@ -122,17 +98,39 @@ public class ConsumerConnection<T> {
|
|||||||
})
|
})
|
||||||
.map(element -> new Timestamped<>(System.currentTimeMillis(), element));
|
.map(element -> new Timestamped<>(System.currentTimeMillis(), element));
|
||||||
}
|
}
|
||||||
})).doFinally(s -> {
|
})).doOnError(this::onRemoteLastError).doFinally(this::onLastFinally);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onLastFinally(SignalType s) {
|
||||||
|
if (remoteCount > 0 && localTerminationState == null) {
|
||||||
|
assert connectedState;
|
||||||
|
var ex = new CancelledChannelException();
|
||||||
|
localTerminationState = Optional.of(ex);
|
||||||
if (s == SignalType.CANCEL) {
|
if (s == SignalType.CANCEL) {
|
||||||
synchronized (ConsumerConnection.this) {
|
localTerminationSink.emitError(ex, EmitFailureHandler.FAIL_FAST);
|
||||||
local = null;
|
} else {
|
||||||
var ex = new InterruptedException();
|
localTerminationSink.emitEmpty(EmitFailureHandler.FAIL_FAST);
|
||||||
localTerminationState = Optional.of(ex);
|
}
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Local is cancelled", this.printStatus());
|
}
|
||||||
localTerminationSink.emitError(ex, EmitFailureHandler.busyLooping(Duration.ofMillis(100)));
|
reset();
|
||||||
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote connection ended with status {}, emitted termination complete", this.printStatus(), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onRemoteLastError(Throwable ex) {
|
||||||
|
if (remoteCount > 0 && localTerminationState == null) {
|
||||||
|
localTerminationState = Optional.of(ex);
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("%s Local connection ended with failure".formatted(this.printStatus()), ex);
|
||||||
|
}
|
||||||
|
if (remoteCount <= 1) {
|
||||||
|
var sink = localTerminationSink;
|
||||||
|
reset();
|
||||||
|
sink.emitError(ex, EmitFailureHandler.FAIL_FAST);
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("%s Local connection ended with failure, emitted termination failure".formatted(this.printStatus()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Mono<Void> connectRemote() {
|
public synchronized Mono<Void> connectRemote() {
|
||||||
@ -154,7 +152,7 @@ public class ConsumerConnection<T> {
|
|||||||
if (connectedState) {
|
if (connectedState) {
|
||||||
if (localTerminationState == null) {
|
if (localTerminationState == null) {
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection is still marked as open but not terminated, interrupting it", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection is still marked as open but not terminated, interrupting it", this.printStatus());
|
||||||
var ex = new InterruptedException();
|
var ex = new InterruptedException("Interrupted this connection because a new one is being prepared");
|
||||||
localTerminationState = Optional.of(ex);
|
localTerminationState = Optional.of(ex);
|
||||||
localTerminationSink.emitError(ex, EmitFailureHandler.busyLooping(Duration.ofMillis(100)));
|
localTerminationSink.emitError(ex, EmitFailureHandler.busyLooping(Duration.ofMillis(100)));
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection has been interrupted", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection has been interrupted", this.printStatus());
|
||||||
@ -167,7 +165,7 @@ public class ConsumerConnection<T> {
|
|||||||
}
|
}
|
||||||
local = null;
|
local = null;
|
||||||
remoteCount = 0;
|
remoteCount = 0;
|
||||||
remotes.emitComplete(EmitFailureHandler.FAIL_FAST);
|
remotes.tryEmitComplete();
|
||||||
remotes = Sinks.many().replay().all();
|
remotes = Sinks.many().replay().all();
|
||||||
connectedState = false;
|
connectedState = false;
|
||||||
connectedSink = Sinks.empty();
|
connectedSink = Sinks.empty();
|
||||||
@ -179,7 +177,7 @@ public class ConsumerConnection<T> {
|
|||||||
public synchronized void registerRemote(Flux<Payload> remote) {
|
public synchronized void registerRemote(Flux<Payload> remote) {
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote is trying to register", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote is trying to register", this.printStatus());
|
||||||
this.remoteCount++;
|
this.remoteCount++;
|
||||||
this.remotes.emitNext(remote, EmitFailureHandler.FAIL_FAST);
|
this.remotes.tryEmitNext(remote);
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote registered", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote registered", this.printStatus());
|
||||||
onChanged();
|
onChanged();
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,12 @@ public class ProducerConnection<T> {
|
|||||||
return remoteTerminationSink.asMono().publishOn(Schedulers.parallel());
|
return remoteTerminationSink.asMono().publishOn(Schedulers.parallel());
|
||||||
}
|
}
|
||||||
})).doFinally(s -> {
|
})).doFinally(s -> {
|
||||||
if (s == SignalType.CANCEL) {
|
if (s == SignalType.ON_ERROR || s == SignalType.CANCEL) {
|
||||||
synchronized (ProducerConnection.this) {
|
synchronized (ProducerConnection.this) {
|
||||||
|
if (connectedState) {
|
||||||
|
connectedState = false;
|
||||||
|
connectedSink = Sinks.empty();
|
||||||
|
}
|
||||||
local = null;
|
local = null;
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Local is cancelled", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} Local is cancelled", this.printStatus());
|
||||||
}
|
}
|
||||||
@ -126,7 +130,7 @@ public class ProducerConnection<T> {
|
|||||||
if (connectedState) {
|
if (connectedState) {
|
||||||
if (remoteTerminationState == null) {
|
if (remoteTerminationState == null) {
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection is still marked as open but not terminated, interrupting it", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection is still marked as open but not terminated, interrupting it", this.printStatus());
|
||||||
var ex = new InterruptedException();
|
var ex = new InterruptedException("Interrupted this connection because a new one is being prepared");
|
||||||
remoteTerminationState = Optional.of(ex);
|
remoteTerminationState = Optional.of(ex);
|
||||||
remoteTerminationSink.emitError(ex, EmitFailureHandler.busyLooping(Duration.ofMillis(100)));
|
remoteTerminationSink.emitError(ex, EmitFailureHandler.busyLooping(Duration.ofMillis(100)));
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection has been interrupted", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} The previous connection has been interrupted", this.printStatus());
|
||||||
@ -148,10 +152,6 @@ public class ProducerConnection<T> {
|
|||||||
|
|
||||||
public synchronized void registerRemote() {
|
public synchronized void registerRemote() {
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote is trying to register", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote is trying to register", this.printStatus());
|
||||||
if (this.remoteCount > 0) {
|
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote was already registered", this.printStatus());
|
|
||||||
throw new IllegalStateException("Remote is already registered");
|
|
||||||
}
|
|
||||||
this.remoteCount++;
|
this.remoteCount++;
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("{} Remote registered", this.printStatus());
|
if (LOG.isDebugEnabled()) LOG.debug("{} Remote registered", this.printStatus());
|
||||||
onChanged();
|
onChanged();
|
||||||
|
@ -22,6 +22,8 @@ public class RSocketUtils {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
|
} finally {
|
||||||
|
payload.release();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,9 @@ import it.tdlight.reactiveapi.EventConsumer;
|
|||||||
import it.tdlight.reactiveapi.EventProducer;
|
import it.tdlight.reactiveapi.EventProducer;
|
||||||
import it.tdlight.reactiveapi.RSocketParameters;
|
import it.tdlight.reactiveapi.RSocketParameters;
|
||||||
import it.tdlight.reactiveapi.Timestamped;
|
import it.tdlight.reactiveapi.Timestamped;
|
||||||
import it.tdlight.reactiveapi.rsocket.CancelledChannelException;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
@ -25,6 +22,7 @@ import org.junit.jupiter.api.AfterEach;
|
|||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.function.Executable;
|
||||||
import reactor.core.Exceptions;
|
import reactor.core.Exceptions;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
@ -449,7 +447,7 @@ public abstract class TestChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailTwoSubscribers() {
|
public void testTwoSubscribers() {
|
||||||
var dataFlux = Flux.fromIterable(data).publish().autoConnect();
|
var dataFlux = Flux.fromIterable(data).publish().autoConnect();
|
||||||
var exRef1 = new AtomicReference<Throwable>();
|
var exRef1 = new AtomicReference<Throwable>();
|
||||||
var exRef2 = new AtomicReference<Throwable>();
|
var exRef2 = new AtomicReference<Throwable>();
|
||||||
@ -459,35 +457,43 @@ public abstract class TestChannel {
|
|||||||
.retryWhen(Retry.fixedDelay(5, Duration.ofSeconds(1)))
|
.retryWhen(Retry.fixedDelay(5, Duration.ofSeconds(1)))
|
||||||
.subscribe(n -> {}, ex -> exRef1.set(ex));
|
.subscribe(n -> {}, ex -> exRef1.set(ex));
|
||||||
|
|
||||||
Assertions.assertThrows(IllegalStateException.class, () -> {
|
var exe = new Executable() {
|
||||||
try {
|
@Override
|
||||||
Mono
|
public void execute() throws Throwable {
|
||||||
.when(consumer
|
try {
|
||||||
.consumeMessages()
|
Mono
|
||||||
.limitRate(1)
|
.when(consumer
|
||||||
.map(Timestamped::data)
|
.consumeMessages()
|
||||||
.map(Integer::parseUnsignedInt)
|
.limitRate(1)
|
||||||
.log("consumer-1", Level.INFO)
|
.map(Timestamped::data)
|
||||||
.doOnError(ex -> exRef2.set(ex)),
|
.map(Integer::parseUnsignedInt)
|
||||||
consumer
|
.log("consumer-1", Level.INFO)
|
||||||
.consumeMessages()
|
.doOnError(ex -> exRef2.set(ex)),
|
||||||
.limitRate(1)
|
consumer
|
||||||
.map(Timestamped::data)
|
.consumeMessages()
|
||||||
.map(Integer::parseUnsignedInt)
|
.limitRate(1)
|
||||||
.log("consumer-2", Level.INFO)
|
.map(Timestamped::data)
|
||||||
.onErrorResume(io.rsocket.exceptions.ApplicationErrorException.class,
|
.map(Integer::parseUnsignedInt)
|
||||||
ex -> Mono.error(new IllegalStateException(ex))
|
.log("consumer-2", Level.INFO)
|
||||||
)
|
.onErrorResume(io.rsocket.exceptions.ApplicationErrorException.class,
|
||||||
)
|
ex -> Mono.error(new IllegalStateException(ex))
|
||||||
.block();
|
)
|
||||||
Assertions.assertNull(exRef1.get());
|
)
|
||||||
Assertions.assertNull(exRef2.get());
|
.block();
|
||||||
} catch (RuntimeException ex) {
|
Assertions.assertNull(exRef1.get());
|
||||||
throw Exceptions.unwrap(ex);
|
Assertions.assertNull(exRef2.get());
|
||||||
} finally {
|
} catch (RuntimeException ex) {
|
||||||
eventProducer.dispose();
|
throw Exceptions.unwrap(ex);
|
||||||
|
} finally {
|
||||||
|
eventProducer.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
if (isConsumerClient()) {
|
||||||
|
Assertions.assertDoesNotThrow(exe);
|
||||||
|
} else {
|
||||||
|
Assertions.assertThrows(IllegalStateException.class, exe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user