Skip to content

Commit

Permalink
Add Jedis pool metrics binder (#756)
Browse files Browse the repository at this point in the history

Signed-off-by: Paolo Di Tommaso <[email protected]>
Signed-off-by: munishchouhan <[email protected]>
Co-authored-by: Munish Chouhan <[email protected]>
Co-authored-by: munishchouhan <[email protected]>
  • Loading branch information
3 people authored Dec 4, 2024
1 parent 3fd5643 commit a6b2833
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 3 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ dependencies {
implementation 'com.coveo:spillway:3.0.0'

// monitoring
implementation 'io.micronaut.micrometer:micronaut-micrometer-core'
implementation 'io.micronaut.micrometer:micronaut-micrometer-registry-prometheus'
// Also required to enable endpoint
implementation 'io.micronaut:micronaut-management'
Expand Down
60 changes: 60 additions & 0 deletions src/main/groovy/io/seqera/wave/redis/JedisPoolMetricsBinder.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Wave, containers provisioning service
* Copyright (c) 2023-2024, Seqera Labs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package io.seqera.wave.redis

import groovy.transform.CompileStatic;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import redis.clients.jedis.JedisPool;

/**
* Implements {@link MeterBinder} for {@link redis.clients.jedis.JedisPool}
*
* @author Paolo Di Tommaso <[email protected]>
*/
@CompileStatic
class JedisPoolMetricsBinder implements MeterBinder {

private final JedisPool pool

JedisPoolMetricsBinder(JedisPool pool) {
this.pool = pool;
}

@Override
void bindTo(MeterRegistry registry) {
registry.gauge("jedis.pool.active", pool, JedisPool::getNumActive);
registry.gauge("jedis.pool.idle", pool, JedisPool::getNumIdle);
registry.gauge("jedis.pool.waiters", pool, JedisPool::getNumWaiters);

// Connection lifecycle metrics
registry.gauge("jedis.pool.created", pool, JedisPool::getCreatedCount);
registry.gauge("jedis.pool.destroyed", pool, JedisPool::getDestroyedCount);

// Borrow/Return statistics
registry.gauge("jedis.pool.borrowed", pool, JedisPool::getBorrowedCount);
registry.gauge("jedis.pool.returned", pool, JedisPool::getReturnedCount);

// Additional metrics (resets, evictions, etc.)
registry.gauge("jedis.pool.max.borrow.wait.millis", pool, (p)-> p.maxBorrowWaitDuration.toMillis() as double)
registry.gauge("jedis.pool.mean.borrow.wait.millis", pool, (p)-> p.meanBorrowWaitDuration.toMillis() as double)
registry.gauge("jedis.pool.mean.active.millis", pool, (p)-> p.meanActiveDuration.toMillis() as double)
registry.gauge("jedis.pool.mean.idle.millis", pool, (p)-> p.meanIdleDuration.toMillis() as double)
}
}
11 changes: 10 additions & 1 deletion src/main/groovy/io/seqera/wave/redis/RedisFactory.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ package io.seqera.wave.redis

import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import io.micrometer.core.instrument.MeterRegistry
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Requires
import io.micronaut.context.annotation.Value
import io.micronaut.core.annotation.Nullable
import jakarta.inject.Inject
import jakarta.inject.Singleton
import redis.clients.jedis.DefaultJedisClientConfig
import redis.clients.jedis.JedisClientConfig
Expand All @@ -42,6 +44,9 @@ import redis.clients.jedis.util.JedisURIHelper
@CompileStatic
class RedisFactory {

@Inject
private MeterRegistry meterRegistry

@Singleton
JedisPool createRedisPool(
@Value('${redis.uri}') String connection,
Expand All @@ -62,7 +67,11 @@ class RedisFactory {
// client config
final clientConfig = clientConfig(uri, password, timeout)
// create the jedis pool
return new JedisPool(config, JedisURIHelper.getHostAndPort(uri), clientConfig)
final result = new JedisPool(config, JedisURIHelper.getHostAndPort(uri), clientConfig)
// Instrument the internal pool
new JedisPoolMetricsBinder(result).bindTo(meterRegistry);
// final return the jedis pool
return result
}

protected JedisClientConfig clientConfig(URI uri, String password, int timeout) {
Expand Down
5 changes: 3 additions & 2 deletions src/test/groovy/io/seqera/wave/redis/RedisFactoryTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package io.seqera.wave.redis

import spock.lang.Specification

import io.micrometer.core.instrument.MeterRegistry
import redis.clients.jedis.exceptions.InvalidURIException
/**
*
Expand All @@ -28,7 +29,7 @@ import redis.clients.jedis.exceptions.InvalidURIException
class RedisFactoryTest extends Specification {
def 'should create redis pool with valid URI'() {
given:
def factory = new RedisFactory()
def factory = new RedisFactory(meterRegistry: Mock(MeterRegistry))

when:
def pool = factory.createRedisPool(URI_STRING, MIN_IDLE, MAX_IDLE, MAX_TOTAL, TIMEOUT, 'password')
Expand All @@ -44,7 +45,7 @@ class RedisFactoryTest extends Specification {

def 'should throw exception for invalid URI'() {
given:
def factory = new RedisFactory()
def factory = new RedisFactory(meterRegistry: Mock(MeterRegistry))

when:
factory.createRedisPool(URI_STRING, MIN_IDLE, MAX_IDLE, MAX_TOTAL, TIMEOUT, null)
Expand Down

0 comments on commit a6b2833

Please sign in to comment.