Skip to content
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

Configurable MaxLifeTime Variance #2035

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/java/com/zaxxer/hikari/HikariConfig.java
Expand Up @@ -53,6 +53,7 @@ public class HikariConfig implements HikariConfigMXBean
private static final long SOFT_TIMEOUT_FLOOR = Long.getLong("com.zaxxer.hikari.timeoutMs.floor", 250L);
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
private static final double DEFAULT_MAX_LIFETIME_VARIANCE = 2.5;
private static final long DEFAULT_KEEPALIVE_TIME = 0L;
private static final int DEFAULT_POOL_SIZE = 10;

Expand All @@ -66,6 +67,7 @@ public class HikariConfig implements HikariConfigMXBean
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile double maxLifetimeVariance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally really try to avoid floating point numbers as much as I can, as they cause a lot of trouble. ( What Every Computer Scientist Should Know About Floating-Point Arithmetic )

I understand why you went for them: you need to be able to represent a value like 2.5%, and it has a decimal point, which makes them not representable by integers.

I have a suggestion. How about you use per mille to represent the variance instead of percent. That way, you could change your type to int, which is a lot more friendly than double, and you'll be able to represent 2.5% as 25‰. If you think that we'll need more precision than that, you can change the type to make it more precise. Like "per million" for example. Though IMO "per mille" is enough.

Please let me know your thoughts.

private volatile int maxPoolSize;
private volatile int minIdle;
private volatile String username;
Expand Down Expand Up @@ -119,6 +121,7 @@ public HikariConfig()
minIdle = -1;
maxPoolSize = -1;
maxLifetime = MAX_LIFETIME;
maxLifetimeVariance = DEFAULT_MAX_LIFETIME_VARIANCE;
connectionTimeout = CONNECTION_TIMEOUT;
validationTimeout = VALIDATION_TIMEOUT;
idleTimeout = IDLE_TIMEOUT;
Expand Down Expand Up @@ -229,6 +232,17 @@ public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
this.leakDetectionThreshold = leakDetectionThresholdMs;
}

/** {@inheritDoc} */
@Override
public double getMaxLifetimeVariance()
{
return maxLifetimeVariance;
}

/** {@inheritDoc} */
@Override
public void setMaxLifetimeVariance(double maxLifetimeVariance) { this.maxLifetimeVariance = maxLifetimeVariance; }

/** {@inheritDoc} */
@Override
public long getMaxLifetime()
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/zaxxer/hikari/HikariConfigMXBean.java
Expand Up @@ -93,6 +93,24 @@ public interface HikariConfigMXBean
*/
void setLeakDetectionThreshold(long leakDetectionThresholdMs);

/**
* This property controls the percentage of a connections maximum lifetime that will be used as variance/jitter.
* This percentage will be a maximum and random variances up to this percentage will be subtracted from the defined
* maxLifeTime value.
*
* @return the maximum percentage of a connections maxLifeTime to be utilized as a variance
*/
double getMaxLifetimeVariance();

/**
* This property controls the percentage of a connections maximum lifetime that will be used as variance/jitter.
* This percentage will be a maximum and random variances up to this percentage will be subtracted from the defined
* maxLifeTime value.
*
* @param maxLifetimeVariance the maximum percentage of maxLifeTime that should be used for variance
*/
void setMaxLifetimeVariance(double maxLifetimeVariance);

/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/com/zaxxer/hikari/pool/HikariPool.java
Expand Up @@ -452,7 +452,7 @@ int[] getPoolStateCounts()
// ***********************************************************************

/**
* Creating new poolEntry. If maxLifetime is configured, create a future End-of-life task with 2.5% variance from
* Creating new poolEntry. If maxLifetime is configured, create a future End-of-life task with configurable variance from
* the maxLifetime time to ensure there is no massive die-off of Connections in the pool.
*/
private PoolEntry createPoolEntry()
Expand All @@ -462,8 +462,7 @@ private PoolEntry createPoolEntry()

final var maxLifetime = config.getMaxLifetime();
if (maxLifetime > 0) {
// variance up to 2.5% of the maxlifetime
final var variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;
final var variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( Math.round(maxLifetime * (config.getMaxLifetimeVariance() / 100)) ) : 0;
final var lifetime = maxLifetime - variance;
poolEntry.setFutureEol(houseKeepingExecutorService.schedule(new MaxLifetimeTask(poolEntry), lifetime, MILLISECONDS));
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/zaxxer/hikari/util/PropertyElf.java
Expand Up @@ -138,6 +138,9 @@ else if (paramClass == long.class) {
else if (paramClass == short.class) {
writeMethod.invoke(target, Short.parseShort(propValue.toString()));
}
else if (paramClass == double.class) {
writeMethod.invoke(target, Double.parseDouble(propValue.toString()));
}
else if (paramClass == boolean.class || paramClass == Boolean.class) {
writeMethod.invoke(target, Boolean.parseBoolean(propValue.toString()));
}
Expand Down