Allow RedisConnectionFactories to be initialized as part of the context lifecycle.

Lettuce and Jedis connection factories now can be configured to initialize early during afterPropertiesSet or configured whether the component should be auto-started by the container.

By default, connection factories auto-startup early.

Closes #2866
Original Pull Request: #2868
This commit is contained in:
Mark Paluch
2024-03-13 15:05:14 +01:00
committed by Christoph Strobl
parent 779a0126e5
commit 7a94beb467
4 changed files with 180 additions and 45 deletions

View File

@@ -78,8 +78,9 @@ import org.springframework.util.ObjectUtils;
* This connection factory implements {@link InitializingBean} and {@link SmartLifecycle} for flexible lifecycle
* control. It must be {@link #afterPropertiesSet() initialized} and {@link #start() started} before you can obtain a
* connection. {@link #afterPropertiesSet() Initialization} {@link SmartLifecycle#start() starts} this bean
* {@link #isAutoStartup() by default}. You can {@link SmartLifecycle#stop()} and {@link SmartLifecycle#start() restart}
* this connection factory if needed.
* {@link #isEarlyStartup() early} by default. You can {@link SmartLifecycle#stop()} and {@link SmartLifecycle#start()
* restart} this connection factory if needed. Disabling {@link #isEarlyStartup() early startup} leaves lifecycle
* management to the container refresh if {@link #isAutoStartup() auto-startup} is enabled.
* <p>
* Note that {@link JedisConnection} and its {@link JedisClusterConnection clustered variant} are not Thread-safe and
* instances should not be shared across threads. Refer to the
@@ -103,9 +104,10 @@ public class JedisConnectionFactory
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
JedisExceptionConverter.INSTANCE);
private boolean convertPipelineAndTxResults = true;
private int phase = 0; // in between min and max values
private boolean autoStartup = true;
private boolean earlyStartup = true;
private boolean convertPipelineAndTxResults = true;
private final AtomicReference<State> state = new AtomicReference<>(State.CREATED);
@@ -571,6 +573,70 @@ public class JedisConnectionFactory
return RedisConfiguration.isClusterConfiguration(configuration) ? (RedisClusterConfiguration) configuration : null;
}
@Override
public int getPhase() {
return this.phase;
}
/**
* Specify the lifecycle phase for pausing and resuming this executor. The default is {@code 0}.
*
* @since 3.2
* @see SmartLifecycle#getPhase()
*/
public void setPhase(int phase) {
this.phase = phase;
}
/**
* @since 3.3
*/
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
/**
* Configure if this Lifecycle connection factory should get started automatically by the container at the time that
* the containing ApplicationContext gets refreshed.
* <p>
* This connection factory defaults to early auto-startup during {@link #afterPropertiesSet()} and can potentially
* create Redis connections early on in the lifecycle. See {@link #setEarlyStartup(boolean)} for delaying connection
* creation to the ApplicationContext refresh if auto-startup is enabled.
*
* @param autoStartup {@literal true} to automatically {@link #start()} the connection factory; {@literal false}
* otherwise.
* @since 3.3
* @see #setEarlyStartup(boolean)
* @see #start()
*/
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
/**
* @return whether to {@link #start()} the component during {@link #afterPropertiesSet()}.
* @since 3.3
*/
public boolean isEarlyStartup() {
return this.earlyStartup;
}
/**
* Configure if this InitializingBean's component Lifecycle should get started early by {@link #afterPropertiesSet()}
* at the time that the bean is initialized. The component defaults to auto-startup.
* <p>
* This method is related to {@link #setAutoStartup(boolean) auto-startup} and can be used to delay Redis client
* startup until the ApplicationContext refresh. Disabling early startup does not disable auto-startup.
*
* @param earlyStartup {@literal true} to early {@link #start()} the component; {@literal false} otherwise.
* @since 3.3
* @see #setAutoStartup(boolean)
*/
public void setEarlyStartup(boolean earlyStartup) {
this.earlyStartup = earlyStartup;
}
/**
* Specifies if pipelined results should be converted to the expected data type. If {@code false}, results of
* {@link JedisConnection#closePipeline()} and {@link JedisConnection#exec()} will be of the type returned by the
@@ -616,7 +682,7 @@ public class JedisConnectionFactory
this.clientConfig = createClientConfig(getDatabase(), getRedisUsername(), getRedisPassword());
if (isAutoStartup()) {
if (isEarlyStartup()) {
start();
}
}
@@ -724,21 +790,6 @@ public class JedisConnectionFactory
}
}
@Override
public int getPhase() {
return this.phase;
}
/**
* Specify the lifecycle phase for pausing and resuming this executor. The default is {@code 0}.
*
* @since 3.2
* @see SmartLifecycle#getPhase()
*/
public void setPhase(int phase) {
this.phase = phase;
}
@Override
public boolean isRunning() {
return State.STARTED.equals(this.state.get());

View File

@@ -100,8 +100,9 @@ import org.springframework.util.StringUtils;
* This connection factory implements {@link InitializingBean} and {@link SmartLifecycle} for flexible lifecycle
* control. It must be {@link #afterPropertiesSet() initialized} and {@link #start() started} before you can obtain a
* connection. {@link #afterPropertiesSet() Initialization} {@link SmartLifecycle#start() starts} this bean
* {@link #isAutoStartup() by default}. You can {@link SmartLifecycle#stop()} and {@link SmartLifecycle#start() restart}
* this connection factory if needed.
* {@link #isEarlyStartup() early} by default. You can {@link SmartLifecycle#stop()} and {@link SmartLifecycle#start()
* restart} this connection factory if needed. Disabling {@link #isEarlyStartup() early startup} leaves lifecycle
* management to the container refresh if {@link #isAutoStartup() auto-startup} is enabled.
*
* @author Costin Leau
* @author Jennifer Hickey
@@ -121,13 +122,14 @@ public class LettuceConnectionFactory implements RedisConnectionFactory, Reactiv
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
LettuceExceptionConverter.INSTANCE);
private int phase = 0; // in between min and max values
private boolean autoStartup = true;
private boolean earlyStartup = true;
private boolean convertPipelineAndTxResults = true;
private boolean eagerInitialization = false;
private boolean shareNativeConnection = true;
private boolean validateConnection = false;
private int phase = 0; // in between min and max values
private @Nullable AbstractRedisClient client;
private final AtomicReference<State> state = new AtomicReference<>(State.CREATED);
@@ -556,12 +558,13 @@ public class LettuceConnectionFactory implements RedisConnectionFactory, Reactiv
/**
* Indicates {@link #setShareNativeConnection(boolean) shared connections} should be eagerly initialized. Eager
* initialization requires a running Redis instance during application startup to allow early validation of connection
* factory configuration. Eager initialization also prevents blocking connect while using reactive API and is
* recommended for reactive API usage.
* initialization requires a running Redis instance during {@link #start() startup} to allow early validation of
* connection factory configuration. Eager initialization also prevents blocking connect while using reactive API and
* is recommended for reactive API usage.
*
* @return {@link true} if the shared connection is initialized upon {@link #afterPropertiesSet()}.
* @return {@link true} if the shared connection is initialized upon {@link #start()}.
* @since 2.2
* @see #start()
*/
public boolean getEagerInitialization() {
return this.eagerInitialization;
@@ -795,6 +798,70 @@ public class LettuceConnectionFactory implements RedisConnectionFactory, Reactiv
return isClusterAware() ? (RedisClusterConfiguration) this.configuration : null;
}
@Override
public int getPhase() {
return this.phase;
}
/**
* Specify the lifecycle phase for pausing and resuming this executor. The default is {@code 0}.
*
* @since 3.2
* @see SmartLifecycle#getPhase()
*/
public void setPhase(int phase) {
this.phase = phase;
}
/**
* @since 3.3
*/
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
/**
* Configure if this Lifecycle connection factory should get started automatically by the container at the time that
* the containing ApplicationContext gets refreshed.
* <p>
* This connection factory defaults to early auto-startup during {@link #afterPropertiesSet()} and can potentially
* create Redis connections early on in the lifecycle. See {@link #setEarlyStartup(boolean)} for delaying connection
* creation to the ApplicationContext refresh if auto-startup is enabled.
*
* @param autoStartup {@literal true} to automatically {@link #start()} the connection factory; {@literal false}
* otherwise.
* @since 3.3
* @see #setEarlyStartup(boolean)
* @see #start()
*/
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
/**
* @return whether to {@link #start()} the component during {@link #afterPropertiesSet()}.
* @since 3.3
*/
public boolean isEarlyStartup() {
return this.earlyStartup;
}
/**
* Configure if this InitializingBean's component Lifecycle should get started early by {@link #afterPropertiesSet()}
* at the time that the bean is initialized. The component defaults to auto-startup.
* <p>
* This method is related to {@link #setAutoStartup(boolean) auto-startup} and can be used to delay Redis client
* startup until the ApplicationContext refresh. Disabling early startup does not disable auto-startup.
*
* @param earlyStartup {@literal true} to early {@link #start()} the component; {@literal false} otherwise.
* @since 3.3
* @see #setAutoStartup(boolean)
*/
public void setEarlyStartup(boolean earlyStartup) {
this.earlyStartup = earlyStartup;
}
/**
* Specifies if pipelined results should be converted to the expected data type. If {@code false}, results of
* {@link LettuceConnection#closePipeline()} and {LettuceConnection#exec()} will be of the type returned by the
@@ -924,21 +991,6 @@ public class LettuceConnectionFactory implements RedisConnectionFactory, Reactiv
state.set(State.STOPPED);
}
@Override
public int getPhase() {
return this.phase;
}
/**
* Specify the lifecycle phase for pausing and resuming this executor. The default is {@code 0}.
*
* @since 3.2
* @see SmartLifecycle#getPhase()
*/
public void setPhase(int phase) {
this.phase = phase;
}
@Override
public boolean isRunning() {
return State.STARTED.equals(this.state.get());
@@ -947,7 +999,7 @@ public class LettuceConnectionFactory implements RedisConnectionFactory, Reactiv
@Override
public void afterPropertiesSet() {
if (isAutoStartup()) {
if (isEarlyStartup()) {
start();
}
}