tdlib-session-container/src/main/java/it/tdlight/reactiveapi/DynamicAtomixReactiveApiClient.java

116 lines
3.6 KiB
Java
Raw Normal View History

2022-01-07 23:54:18 +01:00
package it.tdlight.reactiveapi;
import it.tdlight.reactiveapi.Event.ClientBoundEvent;
import java.time.Duration;
2022-03-19 00:06:30 +01:00
import java.util.concurrent.CancellationException;
2022-01-22 17:45:56 +01:00
import java.util.concurrent.atomic.AtomicReference;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
2022-01-07 23:54:18 +01:00
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
2022-01-21 22:25:47 +01:00
public class DynamicAtomixReactiveApiClient extends BaseAtomixReactiveApiClient implements AutoCloseable {
2022-01-07 23:54:18 +01:00
2022-01-22 17:45:56 +01:00
private static final Logger LOG = LoggerFactory.getLogger(DynamicAtomixReactiveApiClient.class);
private record CurrentLiveId(long sinceTimestamp, long liveId) implements Comparable<CurrentLiveId> {
@Override
public int compareTo(@NotNull DynamicAtomixReactiveApiClient.CurrentLiveId o) {
return Long.compare(this.sinceTimestamp, o.sinceTimestamp);
}
}
2022-01-07 23:54:18 +01:00
private final ReactiveApi api;
2022-01-22 17:45:56 +01:00
private final AtomicReference<Disposable> clientBoundEventsSubscription = new AtomicReference<>(null);
2022-01-07 23:54:18 +01:00
private final long userId;
2022-01-22 17:45:56 +01:00
private final Flux<TimestampedClientBoundEvent> clientBoundEvents;
private final Flux<CurrentLiveId> liveIdChange;
2022-01-07 23:54:18 +01:00
2022-01-14 00:58:35 +01:00
private volatile boolean closed;
2022-01-13 16:19:10 +01:00
DynamicAtomixReactiveApiClient(AtomixReactiveApi api, KafkaConsumer kafkaConsumer, long userId, String subGroupId) {
2022-01-21 22:25:47 +01:00
super(api.getAtomix(), userId);
2022-01-07 23:54:18 +01:00
this.api = api;
this.userId = userId;
2022-01-22 17:45:56 +01:00
var clientBoundEvents = kafkaConsumer
.consumeMessages(subGroupId, userId)
2022-01-14 00:58:35 +01:00
.takeWhile(n -> !closed)
2022-01-22 17:45:56 +01:00
.publish()
2022-03-21 01:08:12 +01:00
.autoConnect(3, clientBoundEventsSubscription::set)
.onErrorResume(CancellationException.class, ex -> {
if ("Disconnected".equals(ex.getMessage())) {
LOG.debug("Disconnected client {}", userId, ex);
return Mono.empty();
} else {
return Mono.error(ex);
}
});
2022-01-07 23:54:18 +01:00
2022-01-22 17:45:56 +01:00
var firstLiveId = clientBoundEvents
.take(1, true)
.singleOrEmpty()
.map(e -> new CurrentLiveId(e.timestamp(), e.event().liveId()));
var sampledLiveIds = clientBoundEvents
.skip(1)
2022-01-07 23:54:18 +01:00
.sample(Duration.ofSeconds(1))
2022-01-22 17:45:56 +01:00
.map(e -> new CurrentLiveId(e.timestamp(), e.event().liveId()));
var startupLiveId = api
.resolveUserLiveId(userId)
.doOnError(ex -> LOG.error("Failed to resolve live id of user {}", userId, ex))
.onErrorResume(ex -> Mono.empty())
.map(liveId -> new CurrentLiveId(System.currentTimeMillis(), liveId));
liveIdChange = startupLiveId
.concatWith(Flux.merge(firstLiveId, sampledLiveIds))
.scan((prev, next) -> {
if (next.compareTo(prev) > 0) {
LOG.trace("Replaced id {} with id {}", prev, next);
return next;
} else {
return prev;
}
})
.distinctUntilChanged(CurrentLiveId::liveId);
// minimum 3 subscribers:
// - firstClientBoundEvent
// - sampledClientBoundEvents
// - clientBoundEvents
this.clientBoundEvents = clientBoundEvents;
2022-01-07 23:54:18 +01:00
2022-01-22 17:45:56 +01:00
super.initialize();
2022-01-07 23:54:18 +01:00
}
@Override
public Flux<ClientBoundEvent> clientBoundEvents() {
2022-01-22 17:45:56 +01:00
return clientBoundEvents.doFirst(() -> {
if (this.clientBoundEventsSubscription.get() != null) {
throw new UnsupportedOperationException("Already subscribed");
}
}).map(TimestampedClientBoundEvent::event);
2022-01-07 23:54:18 +01:00
}
2022-01-21 22:25:47 +01:00
@Override
2022-01-22 17:45:56 +01:00
protected Flux<Long> liveIdChange() {
return liveIdChange.map(CurrentLiveId::liveId);
2022-01-07 23:54:18 +01:00
}
public void close() {
2022-01-14 00:58:35 +01:00
this.closed = true;
2022-01-22 17:45:56 +01:00
var clientBoundEventsSubscription = this.clientBoundEventsSubscription.get();
2022-03-19 00:06:30 +01:00
if (clientBoundEventsSubscription != null && !clientBoundEventsSubscription.isDisposed()) {
try {
clientBoundEventsSubscription.dispose();
} catch (CancellationException ignored) {
LOG.debug("Reactive api client for user {} has been cancelled", userId);
}
2022-01-22 17:45:56 +01:00
}
super.close();
2022-01-07 23:54:18 +01:00
}
}