33
33
34
34
final class ConnectionUtils {
35
35
36
+ static final ConnectionSettings .AffinityStrategy PREFER_LEADER_FOR_PUBLISHING_STRATEGY =
37
+ new PreferLeaderForPublishingAffinityStrategy ();
38
+
36
39
private static final Logger LOGGER = LoggerFactory .getLogger (ConnectionUtils .class );
37
40
38
41
private ConnectionUtils () {}
39
42
40
43
static AmqpConnection .NativeConnectionWrapper enforceAffinity (
41
44
Function <List <Address >, AmqpConnection .NativeConnectionWrapper > connectionFactory ,
42
45
AmqpManagement management ,
43
- ConnectionAffinity affinity ,
44
- AffinityCache affinityCache ) {
46
+ AffinityContext context ,
47
+ AffinityCache affinityCache ,
48
+ ConnectionSettings .AffinityStrategy strategy ) {
45
49
// TODO add retry for sensitive operations in affinity mechanism
46
- if (affinity == null ) {
50
+ if (context == null ) {
47
51
// no affinity asked, we create a connection and return it
48
52
return connectionFactory .apply (null );
49
53
}
@@ -52,13 +56,13 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
52
56
int attemptCount = 0 ;
53
57
boolean queueInfoRefreshed = false ;
54
58
List <String > nodesWithAffinity = null ;
55
- Management .QueueInfo info = affinityCache .queueInfo (affinity .queue ());
59
+ Management .QueueInfo info = affinityCache .queueInfo (context .queue ());
56
60
while (pickedConnection == null ) {
57
61
attemptCount ++;
58
62
AmqpConnection .NativeConnectionWrapper connectionWrapper = null ;
59
63
if (info == null ) {
60
64
connectionWrapper = connectionFactory .apply (null );
61
- info = lookUpQueueInfo (management , affinity , affinityCache );
65
+ info = lookUpQueueInfo (management , context , affinityCache );
62
66
queueInfoRefreshed = true ;
63
67
}
64
68
if (info == null ) {
@@ -72,7 +76,7 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
72
76
info .leader (),
73
77
info .replicas ());
74
78
if (nodesWithAffinity == null ) {
75
- nodesWithAffinity = findAffinity ( affinity , info );
79
+ nodesWithAffinity = strategy . nodesWithAffinity ( context , info );
76
80
}
77
81
if (connectionWrapper == null ) {
78
82
List <Address > addressHints =
@@ -82,19 +86,19 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
82
86
.collect (Collectors .toList ());
83
87
connectionWrapper = connectionFactory .apply (addressHints );
84
88
}
85
- LOGGER .debug ("Nodes matching affinity {}: {}." , affinity , nodesWithAffinity );
89
+ LOGGER .debug ("Nodes matching affinity {}: {}." , context , nodesWithAffinity );
86
90
LOGGER .debug ("Currently connected to node {}." , connectionWrapper .nodename ());
87
91
affinityCache .nodenameToAddress (connectionWrapper .nodename (), connectionWrapper .address ());
88
92
if (nodesWithAffinity .contains (connectionWrapper .nodename ())) {
89
93
if (!queueInfoRefreshed ) {
90
94
LOGGER .debug (
91
95
"Found affinity, but refreshing queue information to check affinity is still valid." );
92
- info = lookUpQueueInfo (management , affinity , affinityCache );
96
+ info = lookUpQueueInfo (management , context , affinityCache );
93
97
if (info == null ) {
94
- LOGGER .debug ("Could not look up info for queue '{}'" , affinity .queue ());
98
+ LOGGER .debug ("Could not look up info for queue '{}'" , context .queue ());
95
99
pickedConnection = connectionWrapper ;
96
100
} else {
97
- nodesWithAffinity = findAffinity ( affinity , info );
101
+ nodesWithAffinity = strategy . nodesWithAffinity ( context , info );
98
102
queueInfoRefreshed = true ;
99
103
if (nodesWithAffinity .contains (connectionWrapper .nodename ())) {
100
104
pickedConnection = connectionWrapper ;
@@ -113,16 +117,16 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
113
117
} else if (attemptCount == 5 ) {
114
118
LOGGER .debug (
115
119
"Could not find affinity {} after {} attempt(s), using last connection." ,
116
- affinity ,
120
+ context ,
117
121
attemptCount );
118
122
pickedConnection = connectionWrapper ;
119
123
} else {
120
124
LOGGER .debug (
121
- "Affinity {} not found with node {}." , affinity , connectionWrapper .nodename ());
125
+ "Affinity {} not found with node {}." , context , connectionWrapper .nodename ());
122
126
if (!queueInfoRefreshed ) {
123
- info = lookUpQueueInfo (management , affinity , affinityCache );
127
+ info = lookUpQueueInfo (management , context , affinityCache );
124
128
if (info != null ) {
125
- nodesWithAffinity = findAffinity ( affinity , info );
129
+ nodesWithAffinity = strategy . nodesWithAffinity ( context , info );
126
130
queueInfoRefreshed = true ;
127
131
}
128
132
}
@@ -132,13 +136,13 @@ static AmqpConnection.NativeConnectionWrapper enforceAffinity(
132
136
}
133
137
return pickedConnection ;
134
138
} catch (RuntimeException e ) {
135
- LOGGER .warn ("Cannot enforce affinity {} of error when looking up queue" , affinity , e );
139
+ LOGGER .warn ("Cannot enforce affinity {} of error when looking up queue" , context , e );
136
140
throw e ;
137
141
}
138
142
}
139
143
140
144
private static Management .QueueInfo lookUpQueueInfo (
141
- AmqpManagement management , ConnectionAffinity affinity , AffinityCache cache ) {
145
+ AmqpManagement management , AffinityContext affinity , AffinityCache cache ) {
142
146
Management .QueueInfo info = null ;
143
147
management .init ();
144
148
try {
@@ -185,57 +189,17 @@ Address nodenameToAddress(String name) {
185
189
}
186
190
}
187
191
188
- static List <String > findAffinity (ConnectionAffinity affinity , Management .QueueInfo info ) {
189
- ConnectionSettings .Affinity .Operation operation = affinity .operation ();
190
- String leader = info .leader ();
191
- List <String > replicas = info .replicas () == null ? Collections .emptyList () : info .replicas ();
192
- List <String > nodesWithAffinity ;
193
- LOGGER .debug (
194
- "Trying to find affinity {} with leader = {}, replicas = {}" , affinity , leader , replicas );
195
- if (info .type () == Management .QueueType .QUORUM || info .type () == Management .QueueType .STREAM ) {
196
- // we may choose between leader and replicas
197
- if (operation == ConnectionSettings .Affinity .Operation .PUBLISH ) {
198
- if (leader == null || leader .isBlank ()) {
199
- nodesWithAffinity = Collections .emptyList ();
200
- } else {
201
- nodesWithAffinity = List .of (leader );
202
- }
203
- } else if (operation == ConnectionSettings .Affinity .Operation .CONSUME ) {
204
- List <String > followers =
205
- replicas .stream ()
206
- .filter (Objects ::nonNull )
207
- .filter (r -> !r .equals (leader ))
208
- .collect (Collectors .toList ());
209
- if (!followers .isEmpty ()) {
210
- nodesWithAffinity = List .copyOf (followers );
211
- } else if (leader != null && !leader .isBlank ()) {
212
- nodesWithAffinity = List .of (leader );
213
- } else {
214
- nodesWithAffinity = Collections .emptyList ();
215
- }
216
- } else {
217
- // we don't care about the operation, we just return a replica
218
- nodesWithAffinity = List .copyOf (replicas );
219
- }
220
- } else {
221
- // classic queue, leader and replica are the same
222
- nodesWithAffinity = List .copyOf (replicas );
223
- }
224
- LOGGER .debug ("Nodes with affinity: {}" , nodesWithAffinity );
225
- return nodesWithAffinity ;
226
- }
227
-
228
- static class ConnectionAffinity {
192
+ static class AffinityContext implements ConnectionSettings .AffinityContext {
229
193
230
194
private final String queue ;
231
195
private final ConnectionSettings .Affinity .Operation operation ;
232
196
233
- ConnectionAffinity (String queue , ConnectionSettings .Affinity .Operation operation ) {
197
+ AffinityContext (String queue , ConnectionSettings .Affinity .Operation operation ) {
234
198
this .queue = queue ;
235
199
this .operation = operation ;
236
200
}
237
201
238
- String queue () {
202
+ public String queue () {
239
203
return this .queue ;
240
204
}
241
205
@@ -252,7 +216,7 @@ public String toString() {
252
216
public boolean equals (Object o ) {
253
217
if (this == o ) return true ;
254
218
if (o == null || getClass () != o .getClass ()) return false ;
255
- ConnectionAffinity that = (ConnectionAffinity ) o ;
219
+ AffinityContext that = (AffinityContext ) o ;
256
220
return Objects .equals (queue , that .queue ) && operation == that .operation ;
257
221
}
258
222
@@ -261,4 +225,51 @@ public int hashCode() {
261
225
return Objects .hash (queue , operation );
262
226
}
263
227
}
228
+
229
+ static class PreferLeaderForPublishingAffinityStrategy
230
+ implements ConnectionSettings .AffinityStrategy {
231
+
232
+ @ Override
233
+ public List <String > nodesWithAffinity (
234
+ ConnectionSettings .AffinityContext context , Management .QueueInfo info ) {
235
+ ConnectionSettings .Affinity .Operation operation = context .operation ();
236
+ String leader = info .leader ();
237
+ List <String > replicas = info .replicas () == null ? Collections .emptyList () : info .replicas ();
238
+ List <String > nodesWithAffinity ;
239
+ LOGGER .debug (
240
+ "Trying to find affinity {} with leader = {}, replicas = {}" , context , leader , replicas );
241
+ if (info .type () == Management .QueueType .QUORUM
242
+ || info .type () == Management .QueueType .STREAM ) {
243
+ // we may choose between leader and replicas
244
+ if (operation == ConnectionSettings .Affinity .Operation .PUBLISH ) {
245
+ if (leader == null || leader .isBlank ()) {
246
+ nodesWithAffinity = Collections .emptyList ();
247
+ } else {
248
+ nodesWithAffinity = List .of (leader );
249
+ }
250
+ } else if (operation == ConnectionSettings .Affinity .Operation .CONSUME ) {
251
+ List <String > followers =
252
+ replicas .stream ()
253
+ .filter (Objects ::nonNull )
254
+ .filter (r -> !r .equals (leader ))
255
+ .collect (Collectors .toList ());
256
+ if (!followers .isEmpty ()) {
257
+ nodesWithAffinity = List .copyOf (followers );
258
+ } else if (leader != null && !leader .isBlank ()) {
259
+ nodesWithAffinity = List .of (leader );
260
+ } else {
261
+ nodesWithAffinity = Collections .emptyList ();
262
+ }
263
+ } else {
264
+ // we don't care about the operation, we just return a replica
265
+ nodesWithAffinity = List .copyOf (replicas );
266
+ }
267
+ } else {
268
+ // classic queue, leader and replica are the same
269
+ nodesWithAffinity = List .copyOf (replicas );
270
+ }
271
+ LOGGER .debug ("Nodes with affinity: {}" , nodesWithAffinity );
272
+ return nodesWithAffinity ;
273
+ }
274
+ }
264
275
}
0 commit comments