Use timeouts

This commit is contained in:
Andrea Cavalli 2021-10-02 16:48:56 +02:00
parent 82050a5f0b
commit 714081a93c
12 changed files with 110 additions and 66 deletions

View File

@ -63,6 +63,11 @@
<artifactId>fastutil</artifactId>
<version>8.5.6</version>
</dependency>
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-java</artifactId>
<version>2.7.8.11</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
@ -154,7 +159,6 @@
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-java</artifactId>
<version>2.7.8.9</version>
</dependency>
<dependency>
<groupId>it.cavallium</groupId>

View File

@ -2,6 +2,7 @@ package it.tdlight.tdlibsession.td;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object;
import java.time.Duration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -11,7 +12,7 @@ public interface ReactorTelegramClient {
Flux<Object> receive();
Mono<TdApi.Object> send(TdApi.Function query);
Mono<TdApi.Object> send(TdApi.Function query, Duration timeout);
TdApi.Object execute(TdApi.Function query);
}

View File

@ -4,6 +4,7 @@ import it.tdlight.common.ReactiveItem;
import it.tdlight.common.ReactiveTelegramClient;
import it.tdlight.jni.TdApi;
import it.tdlight.utils.MonoUtils;
import java.time.Duration;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
@ -67,13 +68,14 @@ public class WrappedReactorTelegramClient implements ReactorTelegramClient {
/**
* Sends a request to the TDLib.
*
* @param query Object representing a query to the TDLib.
* @throws NullPointerException if query is null.
* @param query Object representing a query to the TDLib.
* @param timeout Response timeout.
* @return a publisher that will emit exactly one item, or an error
* @throws NullPointerException if query is null.
*/
@Override
public Mono<TdApi.Object> send(TdApi.Function query) {
return Mono.from(reactiveTelegramClient.send(query)).single();
public Mono<TdApi.Object> send(TdApi.Function query, Duration timeout) {
return Mono.from(reactiveTelegramClient.send(query, timeout)).single();
}
/**

View File

@ -3,6 +3,7 @@ package it.tdlight.tdlibsession.td.direct;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Function;
import it.tdlight.tdlibsession.td.TdResult;
import java.time.Duration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -22,9 +23,10 @@ public interface AsyncTdDirect {
* Should be called after receive.
*
* @param request Request to TDLib.
* @param timeout Response timeout.
* @param synchronous Execute synchronously.
* @return The request response or {@link it.tdlight.jni.TdApi.Error}.
*/
<T extends TdApi.Object> Mono<TdResult<T>> execute(Function request, boolean synchronous);
<T extends TdApi.Object> Mono<TdResult<T>> execute(Function request, Duration timeout, boolean synchronous);
}

View File

@ -39,7 +39,7 @@ public class AsyncTdDirectImpl implements AsyncTdDirect {
}
@Override
public <T extends TdApi.Object> Mono<TdResult<T>> execute(Function request, boolean synchronous) {
public <T extends TdApi.Object> Mono<TdResult<T>> execute(Function request, Duration timeout, boolean synchronous) {
if (synchronous) {
return Mono
.firstWithSignal(td.asMono(), Mono.empty())
@ -61,7 +61,7 @@ public class AsyncTdDirectImpl implements AsyncTdDirect {
if (td != null) {
return Mono
.fromRunnable(() -> logger.trace("Sending request to TDLib {}", request))
.then(td.send(request))
.then(td.send(request, timeout))
.single()
.<TdResult<T>>map(TdResult::of)
.doOnSuccess(s -> logger.trace("Sent request to TDLib {}", request));

View File

@ -25,6 +25,7 @@ import it.tdlight.jni.TdApi.UpdateConnectionState;
import it.tdlight.jni.TdApi.UpdateNewMessage;
import it.tdlight.jni.TdApi.User;
import it.tdlight.tdlibsession.td.ReactorTelegramClient;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -112,7 +113,7 @@ public class TestClient implements ReactorTelegramClient {
}
@Override
public Mono<TdApi.Object> send(Function query) {
public Mono<TdApi.Object> send(Function query, Duration timeout) {
return Mono.fromCallable(() -> {
TdApi.Object result = executeCommon(query);
if (result != null) {

View File

@ -39,6 +39,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Comparator;
import java.util.Objects;
import java.util.Set;
@ -57,6 +58,7 @@ import reactor.core.scheduler.Schedulers;
public class AsyncTdEasy {
private final Logger logger;
private static final Duration DEFAULT_TIMEOUT = Duration.ofMinutes(1);
private final Many<AuthorizationState> authState = Sinks.many().replay().latest();
private final Many<Boolean> requestedDefinitiveExit = Sinks.many().replay().latestOrDefault(false);
@ -181,14 +183,17 @@ public class AsyncTdEasy {
/**
* Sends request to TDLib.
* @param timeout Timeout duration.
* @return The response or {@link TdApi.Error}.
*/
public <T extends Object> Mono<TdResult<T>> send(TdApi.Function request) {
return td.<T>execute(request, false);
public <T extends Object> Mono<TdResult<T>> send(TdApi.Function request, Duration timeout) {
return td.execute(request, timeout, false);
}
private <T extends TdApi.Object> Mono<TdResult<T>> sendDirectly(TdApi.Function obj, boolean synchronous) {
return td.<T>execute(obj, synchronous);
private <T extends TdApi.Object> Mono<TdResult<T>> sendDirectly(TdApi.Function obj,
Duration timeout,
boolean synchronous) {
return td.execute(obj, timeout, synchronous);
}
/**
@ -196,7 +201,7 @@ public class AsyncTdEasy {
* @param i level
*/
public Mono<Void> setVerbosityLevel(int i) {
return thenOrFatalError(sendDirectly(new TdApi.SetLogVerbosityLevel(i), true));
return thenOrFatalError(sendDirectly(new TdApi.SetLogVerbosityLevel(i), DEFAULT_TIMEOUT, true));
}
/**
@ -204,7 +209,10 @@ public class AsyncTdEasy {
* @param name option name
*/
public Mono<Void> clearOption(String name) {
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueEmpty()), false));
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueEmpty()),
DEFAULT_TIMEOUT,
false
));
}
/**
@ -213,7 +221,10 @@ public class AsyncTdEasy {
* @param value option value
*/
public Mono<Void> setOptionString(String name, String value) {
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueString(value)), false));
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueString(value)),
DEFAULT_TIMEOUT,
false
));
}
/**
@ -222,7 +233,10 @@ public class AsyncTdEasy {
* @param value option value
*/
public Mono<Void> setOptionInteger(String name, long value) {
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueInteger(value)), false));
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueInteger(value)),
DEFAULT_TIMEOUT,
false
));
}
/**
@ -231,7 +245,10 @@ public class AsyncTdEasy {
* @param value option value
*/
public Mono<Void> setOptionBoolean(String name, boolean value) {
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueBoolean(value)), false));
return thenOrFatalError(sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueBoolean(value)),
DEFAULT_TIMEOUT,
false
));
}
/**
@ -240,7 +257,10 @@ public class AsyncTdEasy {
* @return The value or nothing
*/
public Mono<String> getOptionString(String name) {
return this.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), false).<OptionValue>flatMap(MonoUtils::orElseThrow).flatMap((TdApi.OptionValue value) -> {
return this
.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), DEFAULT_TIMEOUT, false)
.flatMap(MonoUtils::orElseThrow)
.flatMap((TdApi.OptionValue value) -> {
switch (value.getConstructor()) {
case OptionValueString.CONSTRUCTOR:
return Mono.just(((OptionValueString) value).value);
@ -259,17 +279,20 @@ public class AsyncTdEasy {
* @return The value or nothing
*/
public Mono<Long> getOptionInteger(String name) {
return this.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), false).<TdApi.OptionValue>flatMap(MonoUtils::orElseThrow).flatMap((TdApi.OptionValue value) -> {
switch (value.getConstructor()) {
case OptionValueInteger.CONSTRUCTOR:
return Mono.just(((OptionValueInteger) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException("The option " + name + " is of type "
+ value.getClass().getSimpleName()));
}
});
return this
.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), DEFAULT_TIMEOUT, false)
.flatMap(MonoUtils::orElseThrow)
.flatMap((TdApi.OptionValue value) -> {
switch (value.getConstructor()) {
case OptionValueInteger.CONSTRUCTOR:
return Mono.just(((OptionValueInteger) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException(
"The option " + name + " is of type " + value.getClass().getSimpleName()));
}
});
}
/**
@ -278,17 +301,20 @@ public class AsyncTdEasy {
* @return The value or nothing
*/
public Mono<Boolean> getOptionBoolean(String name) {
return this.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), false).<TdApi.OptionValue>flatMap(MonoUtils::orElseThrow).flatMap((TdApi.OptionValue value) -> {
switch (value.getConstructor()) {
case OptionValueBoolean.CONSTRUCTOR:
return Mono.just(((OptionValueBoolean) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException("The option " + name + " is of type "
+ value.getClass().getSimpleName()));
}
});
return this
.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), DEFAULT_TIMEOUT, false)
.flatMap(MonoUtils::orElseThrow)
.flatMap((TdApi.OptionValue value) -> {
switch (value.getConstructor()) {
case OptionValueBoolean.CONSTRUCTOR:
return Mono.just(((OptionValueBoolean) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException(
"The option " + name + " is of type " + value.getClass().getSimpleName()));
}
});
}
/**
@ -296,10 +322,11 @@ public class AsyncTdEasy {
* be called from any thread.
*
* @param request Request to the TDLib.
* @param timeout Timeout.
* @return The request response.
*/
public <T extends Object> Mono<TdResult<T>> execute(TdApi.Function request) {
return td.<T>execute(request, true);
public <T extends Object> Mono<TdResult<T>> execute(TdApi.Function request, Duration timeout) {
return td.execute(request, timeout, true);
}
/**
@ -322,7 +349,7 @@ public class AsyncTdEasy {
logger.debug("Setting requestedDefinitiveExit: true");
requestedDefinitiveExit.tryEmitNext(true);
})
.then(td.execute(new TdApi.Close(), false).doOnSubscribe(s -> {
.then(td.execute(new TdApi.Close(), DEFAULT_TIMEOUT, false).doOnSubscribe(s -> {
logger.debug("Sending TdApi.Close");
}))
.doOnNext(closeResponse -> logger.debug("TdApi.Close response is: \"{}\"",
@ -342,7 +369,7 @@ public class AsyncTdEasy {
}
private Mono<Update> catchErrors(Object obj) {
return Mono.<Update>fromCallable(() -> {
return Mono.fromCallable(() -> {
if (obj.getConstructor() == Error.CONSTRUCTOR) {
var error = (Error) obj;
@ -419,27 +446,27 @@ public class AsyncTdEasy {
parameters.enableStorageOptimizer = settings.enableStorageOptimizer;
parameters.ignoreFileNames = settings.ignoreFileNames;
return new SetTdlibParameters(parameters);
}).flatMap((SetTdlibParameters obj1) -> sendDirectly(obj1, false)));
}).flatMap((SetTdlibParameters obj1) -> sendDirectly(obj1, DEFAULT_TIMEOUT, false)));
case AuthorizationStateWaitEncryptionKey.CONSTRUCTOR:
return thenOrFatalError(sendDirectly(new CheckDatabaseEncryptionKey(), false))
return thenOrFatalError(sendDirectly(new CheckDatabaseEncryptionKey(), DEFAULT_TIMEOUT, false))
.onErrorResume((error) -> {
logger.error("Error while checking TDLib encryption key", error);
return sendDirectly(new TdApi.Close(), false).then();
return sendDirectly(new TdApi.Close(), DEFAULT_TIMEOUT, false).then();
});
case AuthorizationStateWaitPhoneNumber.CONSTRUCTOR:
return thenOrFatalError(Mono.from(this.settings.asFlux()).flatMap(settings -> {
if (settings.isPhoneNumberSet()) {
return sendDirectly(new SetAuthenticationPhoneNumber(String.valueOf(settings.getPhoneNumber()),
new PhoneNumberAuthenticationSettings(false, false, false)
), false);
), DEFAULT_TIMEOUT, false);
} else if (settings.isBotTokenSet()) {
return sendDirectly(new CheckAuthenticationBotToken(settings.getBotToken()), false);
return sendDirectly(new CheckAuthenticationBotToken(settings.getBotToken()), DEFAULT_TIMEOUT, false);
} else {
return Mono.error(new IllegalArgumentException("A bot is neither an user or a bot"));
}
})).onErrorResume((error) -> {
logger.error("Error while waiting for phone number", error);
return sendDirectly(new TdApi.Close(), false).then();
return sendDirectly(new TdApi.Close(), DEFAULT_TIMEOUT, false).then();
});
case AuthorizationStateWaitRegistration.CONSTRUCTOR:
var authorizationStateWaitRegistration = (AuthorizationStateWaitRegistration) obj;
@ -471,7 +498,7 @@ public class AsyncTdEasy {
.defaultIfEmpty("")
.doOnNext(lastName -> registerUser.lastName = lastName)
)
.then(sendDirectly(registerUser, false)));
.then(sendDirectly(registerUser, DEFAULT_TIMEOUT, false)));
});
case TdApi.AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR:
var authorizationStateWaitOtherDeviceConfirmation = (AuthorizationStateWaitOtherDeviceConfirmation) obj;
@ -495,7 +522,7 @@ public class AsyncTdEasy {
authorizationStateWaitCode.codeInfo.timeout,
authorizationStateWaitCode.codeInfo.type
)
).flatMap(code -> sendDirectly(new CheckAuthenticationCode(code), false)));
).flatMap(code -> sendDirectly(new CheckAuthenticationCode(code), DEFAULT_TIMEOUT, false)));
});
case AuthorizationStateWaitPassword.CONSTRUCTOR:
var authorizationStateWaitPassword = (AuthorizationStateWaitPassword) obj;
@ -505,7 +532,7 @@ public class AsyncTdEasy {
.flatMap(handler -> {
return MonoUtils.thenOrLogRepeatError(() -> handler.onParameterRequest(Parameter.ASK_PASSWORD,
new ParameterInfoPasswordHint(authorizationStateWaitPassword.passwordHint)
).flatMap(password -> sendDirectly(new CheckAuthenticationPassword(password), false)));
).flatMap(password -> sendDirectly(new CheckAuthenticationPassword(password), DEFAULT_TIMEOUT, false)));
});
case AuthorizationStateReady.CONSTRUCTOR: {
this.authState.tryEmitNext(new AuthorizationStateReady());

View File

@ -2,6 +2,7 @@ package it.tdlight.tdlibsession.td.middle;
import it.tdlight.jni.TdApi;
import it.tdlight.tdlibsession.td.TdResult;
import java.time.Duration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -20,7 +21,8 @@ public interface AsyncTdMiddle {
* Sends request to TDLib. May be called from any thread.
*
* @param request Request to TDLib.
* @param executeDirectly Execute the function synchronously.
* @param timeout Timeout.
* @param executeSync Execute the function synchronously.
*/
<T extends TdApi.Object> Mono<TdResult<T>> execute(TdApi.Function request, boolean executeDirectly);
<T extends TdApi.Object> Mono<TdResult<T>> execute(TdApi.Function request, Duration timeout, boolean executeSync);
}

View File

@ -343,8 +343,9 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
}
@Override
public <T extends TdApi.Object> Mono<TdResult<T>> execute(Function request, boolean executeDirectly) {
var req = new ExecuteObject(executeDirectly, request);
public <T extends TdApi.Object> Mono<TdResult<T>> execute(Function request, Duration timeout, boolean executeSync) {
var req = new ExecuteObject(executeSync, request);
var deliveryOptions = new DeliveryOptions(this.deliveryOptions).setSendTimeout(timeout.toMillis());
var crashMono = crash.asMono()
.doOnSuccess(s -> logger.debug("Failed request {} because the TDLib session was already crashed", request))

View File

@ -16,6 +16,7 @@ import it.tdlight.tdlibsession.td.direct.TelegramClientFactory;
import it.tdlight.tdlibsession.td.middle.AsyncTdMiddle;
import it.tdlight.tdlibsession.td.middle.TdClusterManager;
import it.tdlight.utils.MonoUtils;
import java.time.Duration;
import org.warp.commonutils.log.Logger;
import org.warp.commonutils.log.LoggerFactory;
import reactor.core.publisher.Flux;
@ -101,9 +102,11 @@ public class AsyncTdMiddleDirect extends AbstractVerticle implements AsyncTdMidd
}
@Override
public <T extends Object> Mono<TdResult<T>> execute(Function requestFunction, boolean executeDirectly) {
public <T extends Object> Mono<TdResult<T>> execute(Function requestFunction,
Duration timeout,
boolean executeDirectly) {
return td
.<T>execute(requestFunction, executeDirectly)
.<T>execute(requestFunction, timeout, executeDirectly)
.onErrorMap(error -> ResponseError.newResponseError(requestFunction, botAlias, error));
}
}

View File

@ -13,6 +13,7 @@ import it.tdlight.tdlibsession.td.middle.client.AsyncTdMiddleEventBusClient;
import it.tdlight.tdlibsession.td.middle.server.AsyncTdMiddleEventBusServer;
import it.tdlight.utils.MonoUtils;
import java.nio.file.Path;
import java.time.Duration;
import org.warp.commonutils.error.InitializationException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -79,7 +80,7 @@ public class AsyncTdMiddleLocal implements AsyncTdMiddle {
}
@Override
public <T extends Object> Mono<TdResult<T>> execute(Function request, boolean executeDirectly) {
return cli.asMono().single().flatMap(c -> c.execute(request, executeDirectly));
public <T extends Object> Mono<TdResult<T>> execute(Function request, Duration timeout, boolean executeDirectly) {
return cli.asMono().single().flatMap(c -> c.execute(request, timeout, executeDirectly));
}
}

View File

@ -149,9 +149,8 @@ public class AsyncTdMiddleEventBusServer extends AbstractVerticle {
logger.trace("Received execute request {}", request);
}
return td
.execute(request, body.isExecuteDirectly())
.execute(request, Duration.ofSeconds(60 + 30), body.isExecuteDirectly())
.single()
.timeout(Duration.ofSeconds(60 + 30))
.doOnSuccess(s -> logger.trace("Executed successfully. Request was {}", request))
.onErrorResume(ex -> Mono.fromRunnable(() -> msg.fail(500, ex.getLocalizedMessage())))
.flatMap(response -> Mono.fromCallable(() -> {
@ -389,7 +388,8 @@ public class AsyncTdMiddleEventBusServer extends AbstractVerticle {
if (printDefaultException) {
logger.warn("Undeploying after a fatal error in a served flux", ex);
}
return td.execute(new TdApi.Close(), false)
return td
.execute(new TdApi.Close(), Duration.ofDays(1), false)
.doOnError(ex2 -> logger.error("Unexpected error", ex2))
.doOnSuccess(s -> logger.debug("Emergency Close() signal has been sent successfully"))
.then(rxStop().as(MonoUtils::toMono));