Skip to content

GH-3199: TCP FailoverClientCF fail back default #3206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@
*/
public class FailoverClientConnectionFactory extends AbstractClientConnectionFactory {

private static final long DEFAULT_REFRESH_SHARED_INTERVAL = 0L;
private static final long DEFAULT_REFRESH_SHARED_INTERVAL = Long.MAX_VALUE;

private final List<AbstractClientConnectionFactory> factories;

private final boolean cachingDelegates;

private long refreshSharedInterval = DEFAULT_REFRESH_SHARED_INTERVAL;

private boolean closeOnRefresh;
private boolean closeOnRefresh = true;

private boolean failBack;

private volatile long creationTime;

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

/**
Expand Down Expand Up @@ -148,7 +150,7 @@ public void registerSender(TcpSender sender) {
protected TcpConnectionSupport obtainConnection() throws InterruptedException {
FailoverTcpConnection sharedConnection = (FailoverTcpConnection) getTheConnection();
boolean shared = !isSingleUse() && !this.cachingDelegates;
boolean refreshShared = shared
boolean refreshShared = this.failBack && shared
&& sharedConnection != null
&& System.currentTimeMillis() > this.creationTime + this.refreshSharedInterval;
if (sharedConnection != null && sharedConnection.isOpen() && !refreshShared) {
Expand All @@ -161,20 +163,26 @@ protected TcpConnectionSupport obtainConnection() throws InterruptedException {
}
failoverTcpConnection.incrementEpoch();
if (shared) {
this.creationTime = System.currentTimeMillis();
/*
* We may have simply wrapped the same connection in a new wrapper; don't close.
*/
if (refreshShared && this.closeOnRefresh
&& !sharedConnection.delegate.equals(failoverTcpConnection.delegate)
&& sharedConnection.isOpen()) {
sharedConnection.close();
}
closeRefreshedIfNecessary(sharedConnection, refreshShared, failoverTcpConnection);
setTheConnection(failoverTcpConnection);
}
return failoverTcpConnection;
}

private void closeRefreshedIfNecessary(FailoverTcpConnection sharedConnection, boolean refreshShared,
FailoverTcpConnection failoverTcpConnection) {

this.creationTime = System.currentTimeMillis();
/*
* We may have simply wrapped the same connection in a new wrapper; don't close.
*/
if (refreshShared && this.closeOnRefresh
&& !sharedConnection.delegate.equals(failoverTcpConnection.delegate)
&& sharedConnection.isOpen()) {
sharedConnection.close();
}
}

@Override
public void start() {
for (AbstractClientConnectionFactory factory : this.factories) {
Expand Down
12 changes: 7 additions & 5 deletions src/reference/asciidoc/ip.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -536,23 +536,25 @@ The following example shows how to configure a failover client connection factor

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.

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

* `refreshSharedInterval`
* `closeOnRefresh`

These are `0` and `false` to retain the same behavior that existed before the properties were added.

Consider the following scenario based on the above configuration:
Let's say `clientFactory1` cannot establish a connection but `clientFactory2` can.
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.
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.
If `closeOnRefresh` is `false`, the "old" connection will remain open and may be reused in future if the first factory fails once more.

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.
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.

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

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.

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.
To revert to the default behavior of previous versions, set them to `0` and `false`.

[[tcp-affinity-cf]]
==== TCP Thread Affinity Connection Factory

Expand Down