Polishing.

Allow Jedis to exist pub/sub loop gracefully. Await unsubscribe. Reduce retry backoff to allow from broken connection recovery. Do not use pooling to avoid using broken connections.

See #2256
This commit is contained in:
Mark Paluch
2022-02-17 16:24:39 +01:00
parent bc07540b9a
commit d34c45bfe4
4 changed files with 76 additions and 28 deletions

View File

@@ -265,7 +265,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
}
try {
futureToAwait.get(getMaxSubscriptionRegistrationWaitingTime(), TimeUnit.SECONDS);
futureToAwait.get(getMaxSubscriptionRegistrationWaitingTime(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
@@ -391,15 +391,13 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
return true;
}
try {
listenFuture.join();
} catch (Exception e) {
// ignore, just await completion here.
}
awaitRegistrationTime(listenFuture);
if (this.state.compareAndSet(state, State.prepareUnsubscribe())) {
getRequiredSubscriber().cancel();
getRequiredSubscriber().unsubscribeAll();
awaitRegistrationTime(this.unsubscribeFuture);
this.state.set(State.notListening());
@@ -416,6 +414,16 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
}
}
private void awaitRegistrationTime(CompletableFuture<Void> future) {
try {
future.get(getMaxSubscriptionRegistrationWaitingTime(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException | TimeoutException e) {
// ignore
}
}
@Override
public boolean isRunning() {
return this.started.get();
@@ -876,7 +884,8 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
long recoveryInterval = backOffExecution.nextBackOff();
if (recoveryInterval != BackOffExecution.STOP) {
logger.error(String.format("Connection failure occurred: %s. Restarting subscription task after %s ms.", ex, recoveryInterval));
logger.error(String.format("Connection failure occurred: %s. Restarting subscription task after %s ms.", ex,
recoveryInterval), ex);
}
return recoveryInterval;
@@ -897,7 +906,9 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
return;
}
logger.error("SubscriptionTask aborted with exception:", ex);
if (isRunning()) { // log only if the container is still running to prevent close errors from logging
logger.error("SubscriptionTask aborted with exception:", ex);
}
future.completeExceptionally(ex);
}
@@ -1216,6 +1227,26 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
this.synchronizingMessageListener.addSynchronization(synchronizer);
}
public void unsubscribeAll() {
synchronized (localMonitor) {
RedisConnection connection = this.connection;
if (connection == null) {
return;
}
doUnsubscribe(connection);
}
}
void doUnsubscribe(RedisConnection connection) {
closeSubscription(connection);
closeConnection();
unsubscribeFuture.complete(null);
}
/**
* Cancel all subscriptions and close the connection.
*/
@@ -1228,26 +1259,34 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
return;
}
doCancel(connection);
}
}
void doCancel(RedisConnection connection) {
closeSubscription(connection);
closeConnection();
}
void closeSubscription(RedisConnection connection) {
if (logger.isTraceEnabled()) {
logger.trace("Cancelling Redis subscription...");
}
Subscription sub = connection.getSubscription();
if (sub != null) {
if (logger.isTraceEnabled()) {
logger.trace("Cancelling Redis subscription...");
logger.trace("Unsubscribing from all channels");
}
Subscription sub = connection.getSubscription();
if (sub != null) {
if (logger.isTraceEnabled()) {
logger.trace("Unsubscribing from all channels");
}
try {
sub.close();
} catch (Exception e) {
logger.warn("Unable to unsubscribe from subscriptions", e);
}
try {
sub.close();
} catch (Exception e) {
logger.warn("Unable to unsubscribe from subscriptions", e);
}
closeConnection();
}
}
@@ -1324,6 +1363,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
}
}
}
}
/**
@@ -1341,6 +1381,11 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
this.executor = executor;
}
@Override
void doUnsubscribe(RedisConnection connection) {
closeSubscription(connection); // connection will be closed after exiting the doSubscribe method
}
@Override
protected void eventuallyPerformSubscription(RedisConnection connection, BackOffExecution backOffExecution,
CompletableFuture<Void> subscriptionDone, Collection<byte[]> patterns, Collection<byte[]> channels) {
@@ -1369,6 +1414,8 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
try {
doSubscribe(connection, patterns, initiallySubscribeToChannels);
closeConnection();
unsubscribeFuture.complete(null);
} catch (Throwable t) {
handleSubscriptionException(subscriptionDone, backOffExecution, t);
}