Open pkgonan opened 1 year ago
It sounds a bit as if this would originate from Reactor Pool as R2DBC pool delegates all eviction and statistics functionality to Reactor Pool. Paging @pderop for further guidance.
@mp911de @pderop
Hi, How do I prevent mass extinction of a Connection Pool?
@pkgonan, ok, I'll check this tomorrow morning.
Hi, @pderop Have you checked?
BACKGROUND_EVICTION_INTERVAL causes all connections to be removed and recreated.
However, all connections are recreated even if BACKGROUND_EVICTION_INTERVAL is not used.
I was in trouble on another PR, I'm now starting to check.
Can you confirm which exact version of reactor-pool you are using ?
I have tried to reproduce your scenario using the following junit test (based on 1.0.1
release), but I cannot reproduce the problem. Can you try to modify it, maybe I have missed your exact use case ? If you can reproduce it, please create an issue on reactor-pool project.
here is my junit test, to test it, you can load the reactor-pool (main branch) into your IDE and then add this test, for example in the SimpleDequePoolInstrumentationTest.
@Test
void useCase() throws InterruptedException {
VirtualTimeScheduler vts = VirtualTimeScheduler.create();
Duration maxLifeTime = Duration.ofSeconds(10);
SimpleDequePool<String> pool = new SimpleDequePool<>(
PoolBuilder
.from(Mono.fromCallable(String::new))
.clock(SchedulerClock.of(vts))
.evictInBackground(Duration.ofMillis(1000), vts)
.evictionPredicate((s, metadata) -> {
return metadata.lifeTime() > maxLifeTime.toMillis();
})
.idleResourceReuseMruOrder()
.sizeBetween(20, 20)
.buildConfig());
// Acquire 19 resources
PooledRef<String>[] refs = IntStream.range(0, 19).mapToObj(i -> pool.acquire().block()).toArray(PooledRef[]::new);
assertThat(pool.metrics().allocatedSize()).isEqualTo(20); // one more resource is created during lazy warmup
assertThat(pool.metrics().idleSize()).isEqualTo(1);
// wait for 1 sec
vts.advanceTimeBy(Duration.ofMillis(1000));
// acquire a twentieth resource (there is one remaining idle one)
PooledRef<String> r20 = pool.acquire().block();
// invalidate r20
r20.invalidate().block();
// re-acquire a new fresh r20 resource
r20 = pool.acquire().block();
// at this point, the first nineteen resources lifetime is around 1 sec, and r20 is fresh.
assertThat(pool.metrics().allocatedSize()).isEqualTo(20);
assertThat(pool.metrics().idleSize()).isEqualTo(0);
// Now release all
Stream.of(refs).forEach(ref -> ref.release().block());
r20.release().block();
// Wait 10 more sec, meaning that the first nineteen resources lifetime will be 11 sec
vts.advanceTimeBy(Duration.ofMillis(10000));
// the first nineteen resources should have been evicted, only 1 resource should still be acquired (r20)
assertThat(pool.metrics().allocatedSize()).isEqualTo(1);
// wait 1 sec, after that, r20 should have been evicted
vts.advanceTimeBy(Duration.ofMillis(1000));
assertThat(pool.metrics().allocatedSize()).isEqualTo(0);
}
Mass destruction of R2DBC pools at once.
initial_size : 20 MAX_SIZE: 20 MAX_LIFE_TIME: 10 Seconds BACKGROUND_EVICTION_INTERVAL: 1 Seconds validation_depth: local validation_query: select 1
With the above configured, a show processlist; query on the database will automatically remove all connections without attempting to maintain 20 connections.
If we have a situation where 19 of the 20 connections have been open for 10 seconds and the remaining 1 connection has been open for 1 second, common sense would dictate that only 19 connections should be removed, but all 20 connections are removed.
Kotlin + Spring Boot Webflux + R2DBC + R2DBC Pool Configuration