Skip to content

Commit cdbaf18

Browse files
garyrussellartembilan
authored andcommitted
GH-3199: TCP FailoverClientCF fail back default
Resolves #3199 - change the default behavior to not fail back until the current connection fails - reduce method complexity (Sonar) - 5.3 only
1 parent c6cf261 commit cdbaf18

File tree

2 files changed

+31
-21
lines changed

2 files changed

+31
-21
lines changed

spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/FailoverClientConnectionFactory.java

+24-16
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,17 @@
4242
*/
4343
public class FailoverClientConnectionFactory extends AbstractClientConnectionFactory {
4444

45-
private static final long DEFAULT_REFRESH_SHARED_INTERVAL = 0L;
45+
private static final long DEFAULT_REFRESH_SHARED_INTERVAL = Long.MAX_VALUE;
4646

4747
private final List<AbstractClientConnectionFactory> factories;
4848

4949
private final boolean cachingDelegates;
5050

5151
private long refreshSharedInterval = DEFAULT_REFRESH_SHARED_INTERVAL;
5252

53-
private boolean closeOnRefresh;
53+
private boolean closeOnRefresh = true;
54+
55+
private boolean failBack;
5456

5557
private volatile long creationTime;
5658

@@ -70,10 +72,9 @@ public FailoverClientConnectionFactory(List<AbstractClientConnectionFactory> fac
7072
/**
7173
* When using a shared connection {@link #setSingleUse(boolean) singleUse} is false,
7274
* specify how long to wait before trying to fail back to start from the beginning of
73-
* the factory list. Default is 0 for backwards compatibility to always try to get a
74-
* connection to the primary server. If you don't want to fail back until the current
75-
* connection is closed, set this to {@link Long#MAX_VALUE}.
76-
* Cannot be changed when using {@link CachingClientConnectionFactory} delegates.
75+
* the factory list. Default is {@link Long#MAX_VALUE} - meaning only fail back when
76+
* the current connection fails. Cannot be changed when using
77+
* {@link CachingClientConnectionFactory} delegates.
7778
* @param refreshSharedInterval the interval in milliseconds.
7879
* @since 4.3.22
7980
* @see #setSingleUse(boolean)
@@ -83,6 +84,7 @@ public void setRefreshSharedInterval(long refreshSharedInterval) {
8384
Assert.isTrue(!this.cachingDelegates,
8485
"'refreshSharedInterval' cannot be changed when using 'CachingClientConnectionFactory` delegates");
8586
this.refreshSharedInterval = refreshSharedInterval;
87+
this.failBack = refreshSharedInterval != Long.MAX_VALUE;
8688
}
8789

8890
/**
@@ -149,7 +151,7 @@ public void registerSender(TcpSender sender) {
149151
protected TcpConnectionSupport obtainConnection() throws InterruptedException {
150152
FailoverTcpConnection sharedConnection = (FailoverTcpConnection) getTheConnection();
151153
boolean shared = !isSingleUse() && !this.cachingDelegates;
152-
boolean refreshShared = shared
154+
boolean refreshShared = this.failBack && shared
153155
&& sharedConnection != null
154156
&& System.currentTimeMillis() > this.creationTime + this.refreshSharedInterval;
155157
if (sharedConnection != null && sharedConnection.isOpen() && !refreshShared) {
@@ -162,20 +164,26 @@ protected TcpConnectionSupport obtainConnection() throws InterruptedException {
162164
}
163165
failoverTcpConnection.incrementEpoch();
164166
if (shared) {
165-
this.creationTime = System.currentTimeMillis();
166-
/*
167-
* We may have simply wrapped the same connection in a new wrapper; don't close.
168-
*/
169-
if (refreshShared && this.closeOnRefresh
170-
&& !sharedConnection.delegate.equals(failoverTcpConnection.delegate)
171-
&& sharedConnection.isOpen()) {
172-
sharedConnection.close();
173-
}
167+
closeRefreshedIfNecessary(sharedConnection, refreshShared, failoverTcpConnection);
174168
setTheConnection(failoverTcpConnection);
175169
}
176170
return failoverTcpConnection;
177171
}
178172

173+
private void closeRefreshedIfNecessary(FailoverTcpConnection sharedConnection, boolean refreshShared,
174+
FailoverTcpConnection failoverTcpConnection) {
175+
176+
this.creationTime = System.currentTimeMillis();
177+
/*
178+
* We may have simply wrapped the same connection in a new wrapper; don't close.
179+
*/
180+
if (refreshShared && this.closeOnRefresh
181+
&& !sharedConnection.delegate.equals(failoverTcpConnection.delegate)
182+
&& sharedConnection.isOpen()) {
183+
sharedConnection.close();
184+
}
185+
}
186+
179187
@Override
180188
public void start() {
181189
for (AbstractClientConnectionFactory factory : this.factories) {

src/reference/asciidoc/ip.adoc

+7-5
Original file line numberDiff line numberDiff line change
@@ -536,23 +536,25 @@ The following example shows how to configure a failover client connection factor
536536

537537
NOTE: When using the failover connection factory, the `singleUse` property must be consistent between the factory itself and the list of factories it is configured to use.
538538

539-
The connection factory has two properties when used with a shared connection (`singleUse=false`):
539+
The connection factory has two properties related to failing back, when used with a shared connection (`singleUse=false`):
540540

541541
* `refreshSharedInterval`
542542
* `closeOnRefresh`
543543

544-
These are `0` and `false` to retain the same behavior that existed before the properties were added.
545-
546544
Consider the following scenario based on the above configuration:
547545
Let's say `clientFactory1` cannot establish a connection but `clientFactory2` can.
548-
Each time the `failCF` `getConnection()` method is called, we will again attempt to connect using `clientFactory1`; if successful, the "old" connection will remain open and may be reused in future if the first factory fails once more.
546+
When the `failCF` `getConnection()` method is called after the `refreshSharedInterval` has passed, we will again attempt to connect using `clientFactory1`; if successful, the connection to `clientFactory2` will be closed.
547+
If `closeOnRefresh` is `false`, the "old" connection will remain open and may be reused in future if the first factory fails once more.
549548

550-
Set `refreshSharedInterval` to only attempt to reconnect with the first factory after that time has expired; set it to `Long.MAX_VALUE` if you only want to fail back to the first factory when the current connection fails.
549+
Set `refreshSharedInterval` to only attempt to reconnect with the first factory after that time has expired; setting it to `Long.MAX_VALUE` (default) if you only want to fail back to the first factory when the current connection fails.
551550

552551
Set `closeOnRefresh` to close the "old" connection after a refresh actually creates a new connection.
553552

554553
IMPORTANT: These properties do not apply if any of the delegate factories is a `CachingClientConnectionFactory` because the connection caching is handled there; in that case the list of connection factories will always be consulted to get a connection.
555554

555+
Starting with version 5.3, these default to `Long.MAX_VALUE` and `true` so the factory only attempts to fail back when the current connection fails.
556+
To revert to the default behavior of previous versions, set them to `0` and `false`.
557+
556558
[[tcp-affinity-cf]]
557559
==== TCP Thread Affinity Connection Factory
558560

0 commit comments

Comments
 (0)