Skip to content

Commit

Permalink
fix #87 Allow to chose between LRU and MRU for idle resources
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbasle committed Aug 6, 2020
1 parent db9dd8f commit b023baf
Show file tree
Hide file tree
Showing 12 changed files with 1,193 additions and 1,673 deletions.
2 changes: 1 addition & 1 deletion src/main/java/reactor/pool/AbstractPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public int getMaxPendingAcquireSize() {

// == common methods to interact with idle/pending queues ==

abstract boolean elementOffer(POOLABLE element);
abstract boolean elementOffer(POOLABLE element); //used in tests

/**
* Note to implementors: stop the {@link Borrower} countdown by calling
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/reactor/pool/DefaultPoolConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class DefaultPoolConfig<POOLABLE> implements PoolConfig<POOLABLE> {
protected final Scheduler acquisitionScheduler;
protected final PoolMetricsRecorder metricsRecorder;
protected final Clock clock;
protected final boolean isIdleLRU;

public DefaultPoolConfig(Mono<POOLABLE> allocator,
AllocationStrategy allocationStrategy,
Expand All @@ -51,7 +52,8 @@ public DefaultPoolConfig(Mono<POOLABLE> allocator,
BiPredicate<POOLABLE, PooledRefMetadata> evictionPredicate,
Scheduler acquisitionScheduler,
PoolMetricsRecorder metricsRecorder,
Clock clock) {
Clock clock,
boolean isIdleLRU) {
this.allocator = allocator;
this.allocationStrategy = allocationStrategy;
this.maxPending = maxPending;
Expand All @@ -61,6 +63,7 @@ public DefaultPoolConfig(Mono<POOLABLE> allocator,
this.acquisitionScheduler = acquisitionScheduler;
this.metricsRecorder = metricsRecorder;
this.clock = clock;
this.isIdleLRU = isIdleLRU;
}

/**
Expand All @@ -81,6 +84,7 @@ protected DefaultPoolConfig(PoolConfig<POOLABLE> toCopy) {
this.acquisitionScheduler = toCopyDpc.acquisitionScheduler;
this.metricsRecorder = toCopyDpc.metricsRecorder;
this.clock = toCopyDpc.clock;
this.isIdleLRU = toCopyDpc.isIdleLRU;
}
else {
this.allocator = toCopy.allocator();
Expand All @@ -92,6 +96,7 @@ protected DefaultPoolConfig(PoolConfig<POOLABLE> toCopy) {
this.acquisitionScheduler = toCopy.acquisitionScheduler();
this.metricsRecorder = toCopy.metricsRecorder();
this.clock = toCopy.clock();
this.isIdleLRU = toCopy.reuseIdleResourcesInLruOrder();
}
}

Expand Down Expand Up @@ -139,4 +144,9 @@ public PoolMetricsRecorder metricsRecorder() {
public Clock clock() {
return this.clock;
}

@Override
public boolean reuseIdleResourcesInLruOrder() {
return isIdleLRU;
}
}
70 changes: 64 additions & 6 deletions src/main/java/reactor/pool/PoolBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public static <T> PoolBuilder<T, PoolConfig<T>> from(Publisher<? extends T> allo
Scheduler acquisitionScheduler = Schedulers.immediate();
Clock clock = Clock.systemUTC();
PoolMetricsRecorder metricsRecorder = NoOpPoolMetricsRecorder.INSTANCE;
boolean idleLruOrder = true;

PoolBuilder(Mono<T> allocator, Function<PoolConfig<T>, CONF> configModifier) {
this.allocator = allocator;
Expand All @@ -88,6 +89,7 @@ public static <T> PoolBuilder<T, PoolConfig<T>> from(Publisher<? extends T> allo
this.acquisitionScheduler = source.acquisitionScheduler;
this.metricsRecorder = source.metricsRecorder;
this.clock = source.clock;
this.idleLruOrder = source.idleLruOrder;
}

/**
Expand Down Expand Up @@ -285,12 +287,52 @@ public PoolBuilder<T, CONF> sizeUnbounded() {
return allocationStrategy(new AllocationStrategies.UnboundedAllocationStrategy());
}

/**
* Configure the pool so that if there are idle resources (ie pool is under-utilized),
* the next {@link Pool#acquire()} will get the <b>Least Recently Used</b> resource
* (LRU, ie. the resource that was released first among the current idle resources).
*
* @return this {@link Pool} builder
*/
public PoolBuilder<T, CONF> idleResourceReuseLruOrder() {
return idleResourceReuseOrder(true);
}

/**
* Configure the pool so that if there are idle resources (ie pool is under-utilized),
* the next {@link Pool#acquire()} will get the <b>Most Recently Used</b> resource
* (MRU, ie. the resource that was released last among the current idle resources).
*
* @return this {@link Pool} builder
*/
public PoolBuilder<T, CONF> idleResourceReuseMruOrder() {
return idleResourceReuseOrder(false);
}

/**
* Configure the order in which idle resources are used when the next {@link Pool#acquire()}
* is performed (while the pool is under-utilized). Allows to chose between
* the <b>Least Recently Used</b> order when {@code true} (LRU, ie. the resource that
* was released first among the current idle resources, the default) and
* <b>Most Recently Used</b> order (MRU, ie. the resource that was released last among
* the current idle resources).
*
* @param isLru {@code true} for LRU (the default) or {@code false} for MRU
* @return this {@link Pool} builder
* @see #idleResourceReuseLruOrder()
* @see #idleResourceReuseMruOrder()
*/
public PoolBuilder<T, CONF> idleResourceReuseOrder(boolean isLru) {
this.idleLruOrder = isLru;
return this;
}

/**
* Add implementation-specific configuration, changing the type of {@link PoolConfig}
* passed to the {@link Pool} factory in {@link #build(Function)}.
*
* @param configModifier {@link Function} to transform the type of {@link PoolConfig}
* create by this builder for the benefit of the pool factory, allowing for custom
* created by this builder for the benefit of the pool factory, allowing for custom
* implementations with custom configurations
* @param <CONF2> new type for the configuration
* @return a new PoolBuilder that now produces a different type of {@link PoolConfig}
Expand All @@ -299,26 +341,41 @@ public <CONF2 extends PoolConfig<T>> PoolBuilder<T, CONF2> extraConfiguration(Fu
return new PoolBuilder<>(this, this.configModifier.andThen(configModifier));
}

/**
* Construct a default reactor pool with the builder's configuration.
*
* @return an {@link InstrumentedPool}
*/
public InstrumentedPool<T> buildPool() {
return new SimpleDequePool<>(this.buildConfig(), true);
}

/**
* Build a LIFO flavor of {@link Pool}, that is to say a flavor where the last
* {@link Pool#acquire()} {@link Mono Mono} that was pending is served first
* whenever a resource becomes available.
* <p>
* This is different from the {@link #idleResourceReuseOrder(boolean) idle resource reuse order},
* which is used when resources ARE available at the instant the {@link Pool#acquire()} is attempted.
*
* @return a builder of {@link Pool} with LIFO pending acquire ordering
* @return a {@link Pool} with LIFO pending acquire ordering
*/
public InstrumentedPool<T> lifo() {
return build(SimpleLifoPool::new);
return new SimpleDequePool<>(this.buildConfig(), false);
}

/**
* Build the default flavor of {@link Pool}, which has FIFO semantics on pending
* {@link Pool#acquire()} {@link Mono Mono}, serving the oldest pending acquire first
* whenever a resource becomes available.
* <p>
* This is different from the {@link #idleResourceReuseOrder(boolean) idle resource reuse order},
* which is used when resources ARE available at the instant the {@link Pool#acquire()} is attempted.
*
* @return a builder of {@link Pool} with FIFO pending acquire ordering
* @return a {@link Pool} with FIFO pending acquire ordering
*/
public InstrumentedPool<T> fifo() {
return build(SimpleFifoPool::new);
return buildPool();
}

/**
Expand All @@ -345,7 +402,8 @@ CONF buildConfig() {
evictionPredicate,
acquisitionScheduler,
metricsRecorder,
clock);
clock,
idleLruOrder);

return this.configModifier.apply(baseConfig);
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/reactor/pool/PoolConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,15 @@ public interface PoolConfig<POOLABLE> {
*/
Clock clock();

/**
* The order in which idle (aka available) resources should be used when the pool was
* under-utilized and a new {@link Pool#acquire()} is performed. Returns {@code true}
* if LRU (Least-Recently Used, the resource that was released first is emitted) or
* {@code false} for MRU (Most-Recently Used, the resource that was released last is
* emitted).
*
* @return {@code true} for LRU, {@code false} for MRU
*/
boolean reuseIdleResourcesInLruOrder();

}
Loading

0 comments on commit b023baf

Please sign in to comment.