Skip to content

Commit 8ba9c50

Browse files
committed
Add retry for affinity during recovery
1 parent c93c80f commit 8ba9c50

File tree

3 files changed

+50
-23
lines changed

3 files changed

+50
-23
lines changed

src/main/java/com/rabbitmq/client/amqp/impl/AmqpConnection.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ final class AmqpConnection extends ResourceBase implements Connection {
136136
this.management,
137137
this.affinity,
138138
this.environment.affinityCache(),
139-
this.affinityStrategy);
139+
this.affinityStrategy,
140+
ConnectionUtils.NO_RETRY_STRATEGY);
140141
this.sync(ncw);
141142
this.state(OPEN);
142143
this.environment.metricsCollector().openConnection();
@@ -404,7 +405,17 @@ private NativeConnectionWrapper recoverNativeConnection(
404405
this.management,
405406
this.affinity,
406407
this.environment.affinityCache(),
407-
this.affinityStrategy);
408+
this.affinityStrategy,
409+
new ConnectionUtils.RetryStrategy() {
410+
@Override
411+
public <T> T maybeRetry(Supplier<T> task) {
412+
return RetryUtils.callAndMaybeRetry(
413+
task::get,
414+
e -> true,
415+
recoveryConfiguration.backOffDelayPolicy(),
416+
"Connection affinity operation");
417+
}
418+
});
408419
return result;
409420
} catch (Exception ex) {
410421
LOGGER.info("Error while trying to recover connection", ex);

src/main/java/com/rabbitmq/client/amqp/impl/ConnectionUtils.java

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@
2727
import java.util.concurrent.ConcurrentHashMap;
2828
import java.util.concurrent.ConcurrentMap;
2929
import java.util.function.Function;
30+
import java.util.function.Supplier;
3031
import java.util.stream.Collectors;
3132
import org.slf4j.Logger;
3233
import org.slf4j.LoggerFactory;
3334

3435
final class ConnectionUtils {
3536

37+
static final RetryStrategy NO_RETRY_STRATEGY = Supplier::get;
38+
3639
static final ConnectionSettings.AffinityStrategy
3740
LEADER_FOR_PUBLISHING_FOLLOWERS_FOR_CONSUMING_STRATEGY =
3841
new LeaderForPublishingFollowersForConsumingStrategy();
@@ -50,11 +53,11 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
5053
AmqpManagement management,
5154
AffinityContext context,
5255
AffinityCache affinityCache,
53-
ConnectionSettings.AffinityStrategy strategy) {
54-
// TODO add retry for sensitive operations in affinity mechanism
56+
ConnectionSettings.AffinityStrategy strategy,
57+
RetryStrategy retryStrategy) {
5558
if (context == null) {
5659
// no affinity asked, we create a connection and return it
57-
return connectionFactory.apply(null);
60+
return retryStrategy.maybeRetry(() -> connectionFactory.apply(null));
5861
}
5962
try {
6063
AmqpConnection.NativeConnectionWrapper pickedConnection = null;
@@ -66,8 +69,8 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
6669
attemptCount++;
6770
AmqpConnection.NativeConnectionWrapper connectionWrapper = null;
6871
if (info == null) {
69-
connectionWrapper = connectionFactory.apply(null);
70-
info = lookUpQueueInfo(management, context, affinityCache);
72+
connectionWrapper = retryStrategy.maybeRetry(() -> connectionFactory.apply(null));
73+
info = lookUpQueueInfo(management, context, affinityCache, retryStrategy);
7174
queueInfoRefreshed = true;
7275
}
7376
if (info == null) {
@@ -89,7 +92,7 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
8992
.map(affinityCache::nodenameToAddress)
9093
.filter(Objects::nonNull)
9194
.collect(Collectors.toList());
92-
connectionWrapper = connectionFactory.apply(addressHints);
95+
connectionWrapper = retryStrategy.maybeRetry(() -> connectionFactory.apply(addressHints));
9396
}
9497
LOGGER.debug("Nodes matching affinity {}: {}.", context, nodesWithAffinity);
9598
LOGGER.debug("Currently connected to node {}.", connectionWrapper.nodename());
@@ -98,7 +101,7 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
98101
if (!queueInfoRefreshed) {
99102
LOGGER.debug(
100103
"Found affinity, but refreshing queue information to check affinity is still valid.");
101-
info = lookUpQueueInfo(management, context, affinityCache);
104+
info = lookUpQueueInfo(management, context, affinityCache, retryStrategy);
102105
if (info == null) {
103106
LOGGER.debug("Could not look up info for queue '{}'", context.queue());
104107
pickedConnection = connectionWrapper;
@@ -129,7 +132,7 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
129132
LOGGER.debug(
130133
"Affinity {} not found with node {}.", context, connectionWrapper.nodename());
131134
if (!queueInfoRefreshed) {
132-
info = lookUpQueueInfo(management, context, affinityCache);
135+
info = lookUpQueueInfo(management, context, affinityCache, retryStrategy);
133136
if (info != null) {
134137
nodesWithAffinity = strategy.nodesWithAffinity(context, info);
135138
queueInfoRefreshed = true;
@@ -147,18 +150,24 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
147150
}
148151

149152
private static Management.QueueInfo lookUpQueueInfo(
150-
AmqpManagement management, AffinityContext affinity, AffinityCache cache) {
151-
Management.QueueInfo info = null;
152-
management.init();
153-
try {
154-
info = management.queueInfo(affinity.queue());
155-
cache.queueInfo(info);
156-
} catch (AmqpException.AmqpEntityDoesNotExistException e) {
157-
LOGGER.debug("Queue '{}' does not exist.", affinity.queue());
158-
cache.clearQueueInfoEntry(affinity.queue());
159-
// we just return null, caller will have to return the last connection
160-
}
161-
return info;
153+
AmqpManagement management,
154+
AffinityContext affinity,
155+
AffinityCache cache,
156+
RetryStrategy retryStrategy) {
157+
return retryStrategy.maybeRetry(
158+
() -> {
159+
Management.QueueInfo info = null;
160+
management.init();
161+
try {
162+
info = management.queueInfo(affinity.queue());
163+
cache.queueInfo(info);
164+
} catch (AmqpException.AmqpEntityDoesNotExistException e) {
165+
LOGGER.debug("Queue '{}' does not exist.", affinity.queue());
166+
cache.clearQueueInfoEntry(affinity.queue());
167+
// we just return null, caller will have to return the last connection
168+
}
169+
return info;
170+
});
162171
}
163172

164173
// TODO clean affinity cache (LRU or size-based)
@@ -298,4 +307,10 @@ public List<String> nodesWithAffinity(
298307
return nodesWithAffinity;
299308
}
300309
}
310+
311+
@FunctionalInterface
312+
interface RetryStrategy {
313+
314+
<T> T maybeRetry(Supplier<T> task);
315+
}
301316
}

src/test/java/com/rabbitmq/client/amqp/impl/AmqpConnectionAffinityUnitTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ private static AmqpConnection.NativeConnectionWrapper enforceAffinity(
443443
management,
444444
context,
445445
affinityCache,
446-
ConnectionUtils.LEADER_FOR_PUBLISHING_FOLLOWERS_FOR_CONSUMING_STRATEGY);
446+
ConnectionUtils.LEADER_FOR_PUBLISHING_FOLLOWERS_FOR_CONSUMING_STRATEGY,
447+
ConnectionUtils.NO_RETRY_STRATEGY);
447448
}
448449
}

0 commit comments

Comments
 (0)