Skip to content

Commit

Permalink
fix: add support for Hikari pool metrics [DHIS2-15419] (#15623)
Browse files Browse the repository at this point in the history
* fix: add support for Hikari pool metrics

(cherry picked from commit 94e33c8)
  • Loading branch information
netroms committed Sep 4, 2024
1 parent 4f5e58d commit 19248a9
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 75 deletions.
4 changes: 4 additions & 0 deletions dhis-2/dhis-support/dhis-support-system/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>

<!-- application monitoring -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,20 @@

import static org.hisp.dhis.external.conf.ConfigurationKey.MONITORING_DBPOOL_ENABLED;

import com.google.common.collect.Lists;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zaxxer.hikari.HikariDataSource;
import io.micrometer.core.instrument.MeterRegistry;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.hisp.dhis.external.conf.ConfigurationKey;
import org.hisp.dhis.monitoring.metrics.jdbc.C3p0MetadataProvider;
import org.hisp.dhis.monitoring.metrics.jdbc.DataSourcePoolMetadataProvider;
import org.hisp.dhis.monitoring.metrics.jdbc.DataSourcePoolMetrics;
import org.hisp.dhis.monitoring.metrics.jdbc.HikariMetadataProvider;
import org.hisp.dhis.monitoring.metrics.jdbc.PoolMetadataProvider;
import org.hisp.dhis.monitoring.metrics.jdbc.PoolMetrics;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
Expand All @@ -59,10 +61,10 @@ static class DataSourcePoolMetadataMetricsConfiguration {

private final MeterRegistry registry;

private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
private final Collection<PoolMetadataProvider> metadataProviders;

DataSourcePoolMetadataMetricsConfiguration(
MeterRegistry registry, Collection<DataSourcePoolMetadataProvider> metadataProviders) {
MeterRegistry registry, Collection<PoolMetadataProvider> metadataProviders) {
this.registry = registry;
this.metadataProviders = metadataProviders;
}
Expand All @@ -74,8 +76,7 @@ public void bindDataSourcesToRegistry(Map<String, DataSource> dataSources) {

private void bindDataSourceToRegistry(String beanName, DataSource dataSource) {
String dataSourceName = getDataSourceName(beanName);
new DataSourcePoolMetrics(
dataSource, this.metadataProviders, dataSourceName, Collections.emptyList())
new PoolMetrics(dataSource, this.metadataProviders, dataSourceName, Collections.emptyList())
.bindTo(this.registry);
}

Expand All @@ -95,11 +96,18 @@ private String getDataSourceName(String beanName) {
}

@Bean
public Collection<DataSourcePoolMetadataProvider> dataSourceMetadataProvider() {
DataSourcePoolMetadataProvider provider =
dataSource -> new C3p0MetadataProvider((ComboPooledDataSource) dataSource);

return Lists.newArrayList(provider);
public Collection<PoolMetadataProvider> dataSourceMetadataProvider() {
return List.of(
dataSource -> {
if (dataSource instanceof ComboPooledDataSource comboPooledDataSource) {
return new C3p0MetadataProvider(comboPooledDataSource);
} else if (dataSource instanceof HikariDataSource hikariDataSource) {
return new HikariMetadataProvider(hikariDataSource);
} else {
throw new IllegalArgumentException(
"Unsupported DataSource type: " + dataSource.getClass().getName());
}
});
}

static class DataSourcePoolMetricsEnabledCondition extends MetricsEnabler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@
import javax.sql.DataSource;

/**
* A base {@link DataSourcePoolMetadata} implementation.
* A base {@link PoolMetadata} implementation.
*
* @param <T> the data source type
* @author Stephane Nicoll
* @since 2.0.0
*/
public abstract class AbstractDataSourcePoolMetadata<T extends DataSource>
implements DataSourcePoolMetadata {
public abstract class AbstractPoolMetadata<T extends DataSource> implements PoolMetadata {

private final T dataSource;

Expand All @@ -46,7 +45,7 @@ public abstract class AbstractDataSourcePoolMetadata<T extends DataSource>
*
* @param dataSource the data source
*/
protected AbstractDataSourcePoolMetadata(T dataSource) {
protected AbstractPoolMetadata(T dataSource) {
this.dataSource = dataSource;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* @author Luciano Fiandesio
*/
@Slf4j
public class C3p0MetadataProvider extends AbstractDataSourcePoolMetadata<ComboPooledDataSource> {
public class C3p0MetadataProvider extends AbstractPoolMetadata<ComboPooledDataSource> {
/**
* Create an instance with the data source to use.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,32 @@
import javax.sql.DataSource;

/**
* A {@link DataSourcePoolMetadataProvider} implementation that returns the first {@link
* DataSourcePoolMetadata} that is found by one of its delegate.
* A {@link PoolMetadataProvider} implementation that returns the first {@link PoolMetadata} that is
* found by one of its delegate.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class CompositeDataSourcePoolMetadataProvider implements DataSourcePoolMetadataProvider {
private final List<DataSourcePoolMetadataProvider> providers;
public class CompositePoolMetadataProvider implements PoolMetadataProvider {
private final List<PoolMetadataProvider> providers;

/**
* Create a {@link CompositeDataSourcePoolMetadataProvider} instance with an initial collection of
* delegates to use.
* Create a {@link CompositePoolMetadataProvider} instance with an initial collection of delegates
* to use.
*
* @param providers the data source pool metadata providers
*/
public CompositeDataSourcePoolMetadataProvider(
Collection<? extends DataSourcePoolMetadataProvider> providers) {
public CompositePoolMetadataProvider(Collection<? extends PoolMetadataProvider> providers) {
this.providers =
(providers != null)
? Collections.unmodifiableList(new ArrayList<>(providers))
: Collections.emptyList();
}

@Override
public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourcePoolMetadata metadata = provider.getDataSourcePoolMetadata(dataSource);
public PoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (PoolMetadataProvider provider : this.providers) {
PoolMetadata metadata = provider.getDataSourcePoolMetadata(dataSource);
if (metadata != null) {
return metadata;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2004-2022, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.monitoring.metrics.jdbc;

import com.zaxxer.hikari.HikariDataSource;

/**
* @author Morten Svanæs
*/
public class HikariMetadataProvider extends AbstractPoolMetadata<HikariDataSource> {

private HikariPoolMetadataAccessor hikariDataSourcePoolMetadata;

/**
* Create an instance with the data source to use.
*
* @param dataSource the data source
*/
public HikariMetadataProvider(HikariDataSource dataSource) {
super(dataSource);
this.hikariDataSourcePoolMetadata = new HikariPoolMetadataAccessor(getDataSource());
}

@Override
public Integer getActive() {
return hikariDataSourcePoolMetadata.getActive();
}

@Override
public Integer getMax() {
return hikariDataSourcePoolMetadata.getMax();
}

@Override
public Integer getMin() {
return hikariDataSourcePoolMetadata.getMin();
}

@Override
public String getValidationQuery() {
return "";
}

@Override
public Boolean getDefaultAutoCommit() {
return hikariDataSourcePoolMetadata.getDefaultAutoCommit();
}

@Override
public Integer getIdle() {
return hikariDataSourcePoolMetadata.getIdle();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2004-2022, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.monitoring.metrics.jdbc;

import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import org.springframework.beans.DirectFieldAccessor;

/**
* @author Morten Svanæs
*/
public class HikariPoolMetadataAccessor extends AbstractPoolMetadata<HikariDataSource> {

public HikariPoolMetadataAccessor(HikariDataSource dataSource) {
super(dataSource);
}

@Override
public Integer getActive() {
try {
return getHikariPool().getActiveConnections();
} catch (Exception ex) {
return null;
}
}

@Override
public Integer getIdle() {
try {
return getHikariPool().getIdleConnections();
} catch (Exception ex) {
return null;
}
}

private HikariPool getHikariPool() {
return (HikariPool) new DirectFieldAccessor(getDataSource()).getPropertyValue("pool");
}

@Override
public Integer getMax() {
return getDataSource().getMaximumPoolSize();
}

@Override
public Integer getMin() {
return getDataSource().getMinimumIdle();
}

@Override
public String getValidationQuery() {
return getDataSource().getConnectionTestQuery();
}

@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().isAutoCommit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* @author Stephane Nicoll
* @since 2.0.0
*/
public interface DataSourcePoolMetadata {
public interface PoolMetadata {

/**
* Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not limited).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
* @author Luciano Fiandesio
*/
@FunctionalInterface
public interface DataSourcePoolMetadataProvider {
public interface PoolMetadataProvider {
/**
* Return the {@link DataSourcePoolMetadata} instance able to manage the specified {@link
* DataSource} or {@code null} if the given data source could not be handled.
* Return the {@link PoolMetadata} instance able to manage the specified {@link DataSource} or
* {@code null} if the given data source could not be handled.
*
* @param dataSource the data source.
* @return the data source pool metadata.
*/
DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
PoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,29 @@
import javax.sql.DataSource;

/**
* A {@link DataSourcePoolMetadataProvider} implementation that returns the first {@link
* DataSourcePoolMetadata} that is found by one of its delegate.
* A {@link PoolMetadataProvider} implementation that returns the first {@link PoolMetadata} that is
* found by one of its delegate.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public class DataSourcePoolMetadataProviders implements DataSourcePoolMetadataProvider {
public class PoolMetadataProviders implements PoolMetadataProvider {

private final List<DataSourcePoolMetadataProvider> providers;
private final List<PoolMetadataProvider> providers;

/**
* Create a {@link DataSourcePoolMetadataProviders} instance with an initial collection of
* delegates to use.
* Create a {@link PoolMetadataProviders} instance with an initial collection of delegates to use.
*
* @param providers the data source pool metadata providers
*/
public DataSourcePoolMetadataProviders(
Collection<? extends DataSourcePoolMetadataProvider> providers) {
public PoolMetadataProviders(Collection<? extends PoolMetadataProvider> providers) {
this.providers = (providers == null ? Collections.emptyList() : new ArrayList<>(providers));
}

@Override
public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourcePoolMetadata metadata = provider.getDataSourcePoolMetadata(dataSource);
public PoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (PoolMetadataProvider provider : this.providers) {
PoolMetadata metadata = provider.getDataSourcePoolMetadata(dataSource);
if (metadata != null) {
return metadata;
}
Expand Down
Loading

0 comments on commit 19248a9

Please sign in to comment.