Skip to content

Commit 05eb4a3

Browse files
injectivesshakuzenmeistermeiermichael-simons
authored
Micrometer metrics (#1137) (#1173)
* [WIP] Micrometer metrics instrumentation This is a work-in-progress attempt at instrumenting the driver with Micrometer as an optional alternative metrics implementation to the existing internal one. * Bring it to life. * Update Micrometer metrics This update includes general improvements, refactorings and tests. * Make MetricsAdapter a supported, public api. - Set scope of micrometer to provided - Rename MetricsProvider to MetricsAdapter (it does not provide metrics, the driver does. It adapts the drivers metrics to something external) - Make it a public interface - Remove dependencies to other internal interfaces and classes (such as BoltServerAddress and the pool itself via ServerAddress and intsuppliers) - Reduce visibility of internal classes as much as possible - Make sure that adapters are not serialized - Apply the clock to the internal adapter (an oversight from the previous iteration) * Saving a file before commiting seems to be relevant. * Not messing up the file while saven even more so. * Make interfaces internal again, provide an enum to select the provider, use global registry by default. This change keeps the existing serialization feature of config correct. It will work for everyone just using micrometer as is with the global registry. if there is a need to select the registriy, we can work on that later. * Move micrometer-core optional tag to dependency declaration * Update Config javadoc and remove imports of internal metrics classes * Update withMetricsEnabled(boolean) implementation * Enable ConfigBuilder.withMetricsAdapter documentation * Update driver/src/main/java/org/neo4j/driver/ConnectionPoolMetrics.java Co-authored-by: Gerrit Meier <[email protected]> * Update ConnectionPoolMetrics documentation * Update MetricsAdapter documentation * Update formatting in ConfigTest * Update wording * Updated wording * Formatting * Update driver/src/main/java/org/neo4j/driver/internal/metrics/DevNullMetricsListener.java Co-authored-by: Gerrit Meier <[email protected]> * Update driver/src/main/java/org/neo4j/driver/internal/metrics/MicrometerConnectionPoolMetrics.java Co-authored-by: Gerrit Meier <[email protected]> * Fix compilation error * Update failing test * Fix GetConnectionPoolMetrics access * Delete redundant counters based on review feedback Co-authored-by: Tommy Ludwig <[email protected]> Co-authored-by: Gerrit Meier <[email protected]> Co-authored-by: Michael Simons <[email protected]> Co-authored-by: Tommy Ludwig <[email protected]> Co-authored-by: Gerrit Meier <[email protected]> Co-authored-by: Michael Simons <[email protected]>
1 parent 3edf0e2 commit 05eb4a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1861
-398
lines changed

bundle/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
</dependency>
3333

3434
<!-- Optional and / or provided dependencies, as they are not transitive to the original driver they must be declared again. -->
35+
<dependency>
36+
<groupId>io.micrometer</groupId>
37+
<artifactId>micrometer-core</artifactId>
38+
<optional>true</optional>
39+
</dependency>
3540
<dependency>
3641
<groupId>org.slf4j</groupId>
3742
<artifactId>slf4j-api</artifactId>

driver/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
</dependency>
3838

3939
<!-- Optional and / or provided dependencies -->
40+
<dependency>
41+
<groupId>io.micrometer</groupId>
42+
<artifactId>micrometer-core</artifactId>
43+
<optional>true</optional>
44+
</dependency>
4045
<dependency>
4146
<groupId>org.slf4j</groupId>
4247
<artifactId>slf4j-api</artifactId>

driver/src/main/java/org/neo4j/driver/Config.java

+43-8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil;
3737
import org.neo4j.driver.internal.retry.RetrySettings;
3838
import org.neo4j.driver.net.ServerAddressResolver;
39+
import org.neo4j.driver.util.Experimental;
3940
import org.neo4j.driver.util.Immutable;
4041

4142
import static java.lang.String.format;
@@ -96,9 +97,9 @@ public class Config implements Serializable
9697
private final RetrySettings retrySettings;
9798
private final ServerAddressResolver resolver;
9899

99-
private final boolean isMetricsEnabled;
100100
private final int eventLoopThreads;
101101
private final String userAgent;
102+
private final MetricsAdapter metricsAdapter;
102103

103104
private Config( ConfigBuilder builder )
104105
{
@@ -122,7 +123,7 @@ private Config( ConfigBuilder builder )
122123
this.fetchSize = builder.fetchSize;
123124

124125
this.eventLoopThreads = builder.eventLoopThreads;
125-
this.isMetricsEnabled = builder.isMetricsEnabled;
126+
this.metricsAdapter = builder.metricsAdapter;
126127
}
127128

128129
/**
@@ -260,7 +261,12 @@ public int eventLoopThreads()
260261
*/
261262
public boolean isMetricsEnabled()
262263
{
263-
return isMetricsEnabled;
264+
return this.metricsAdapter != MetricsAdapter.DEV_NULL;
265+
}
266+
267+
public MetricsAdapter metricsAdapter()
268+
{
269+
return this.metricsAdapter;
264270
}
265271

266272
/**
@@ -290,7 +296,7 @@ public static class ConfigBuilder
290296
private int connectionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis( 30 );
291297
private RetrySettings retrySettings = RetrySettings.DEFAULT;
292298
private ServerAddressResolver resolver;
293-
private boolean isMetricsEnabled = false;
299+
private MetricsAdapter metricsAdapter = MetricsAdapter.DEV_NULL;
294300
private long fetchSize = FetchSizeUtil.DEFAULT_FETCH_SIZE;
295301
private int eventLoopThreads = 0;
296302

@@ -703,13 +709,13 @@ public ConfigBuilder withResolver( ServerAddressResolver resolver )
703709
}
704710

705711
/**
706-
* Enable driver metrics. The metrics can be obtained afterwards via {@link Driver#metrics()}.
712+
* Enable driver metrics backed by internal basic implementation. The metrics can be obtained afterwards via {@link Driver#metrics()}.
713+
*
707714
* @return this builder.
708715
*/
709716
public ConfigBuilder withDriverMetrics()
710717
{
711-
this.isMetricsEnabled = true;
712-
return this;
718+
return withMetricsEnabled( true );
713719
}
714720

715721
/**
@@ -718,7 +724,36 @@ public ConfigBuilder withDriverMetrics()
718724
*/
719725
public ConfigBuilder withoutDriverMetrics()
720726
{
721-
this.isMetricsEnabled = false;
727+
return withMetricsEnabled( false );
728+
}
729+
730+
private ConfigBuilder withMetricsEnabled( boolean enabled )
731+
{
732+
if ( !enabled )
733+
{
734+
withMetricsAdapter( MetricsAdapter.DEV_NULL );
735+
}
736+
else if ( this.metricsAdapter == null || this.metricsAdapter == MetricsAdapter.DEV_NULL )
737+
{
738+
withMetricsAdapter( MetricsAdapter.DEFAULT );
739+
}
740+
return this;
741+
}
742+
743+
/**
744+
* Enable driver metrics with given {@link MetricsAdapter}.
745+
* <p>
746+
* {@link MetricsAdapter#MICROMETER} enables implementation based on <a href="https://micrometer.io">Micrometer</a>. The metrics can be obtained
747+
* afterwards via Micrometer means and {@link Driver#metrics()}. Micrometer must be on classpath when using this option.
748+
* <p>
749+
*
750+
* @param metricsAdapter the metrics adapter to use. Use {@link MetricsAdapter#DEV_NULL} to disable metrics.
751+
* @return this builder.
752+
*/
753+
@Experimental
754+
public ConfigBuilder withMetricsAdapter( MetricsAdapter metricsAdapter )
755+
{
756+
this.metricsAdapter = Objects.requireNonNull( metricsAdapter, "metricsAdapter" );
722757
return this;
723758
}
724759

driver/src/main/java/org/neo4j/driver/ConnectionPoolMetrics.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@
3030
public interface ConnectionPoolMetrics
3131
{
3232
/**
33-
* An unique id that identifies this pool metrics.
34-
* @return An unique name
33+
* A unique id that identifies this pool metrics.
34+
*
35+
* @return A unique name
3536
*/
3637
String id();
3738

@@ -57,9 +58,9 @@ public interface ConnectionPoolMetrics
5758
int creating();
5859

5960
/**
60-
* A counter to record how many connections have been successfully created with this pool since the pool is created.
61+
* A counter to record how many connections have been successfully created with this pool since the pool was created.
6162
* This number increases every time when a connection is successfully created.
62-
* @return The amount of connections have ever been created by this pool.
63+
* @return The amount of connections that have ever been created by this pool.
6364
*/
6465
long created();
6566

driver/src/main/java/org/neo4j/driver/Driver.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -148,19 +148,17 @@ public interface Driver extends AutoCloseable
148148

149149
/**
150150
* Returns the driver metrics if metrics reporting is enabled via {@link Config.ConfigBuilder#withDriverMetrics()}.
151-
* Otherwise a {@link ClientException} will be thrown.
151+
* Otherwise, a {@link ClientException} will be thrown.
152152
* @return the driver metrics if enabled.
153153
* @throws ClientException if the driver metrics reporting is not enabled.
154154
*/
155-
@Experimental
156155
Metrics metrics();
157156

158157
/**
159158
* Returns true if the driver metrics reporting is enabled via {@link Config.ConfigBuilder#withDriverMetrics()}, otherwise false.
160159
*
161160
* @return true if the metrics reporting is enabled.
162161
*/
163-
@Experimental
164162
boolean isMetricsEnabled();
165163

166164
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver;
20+
21+
/**
22+
* Defines which metrics consumer to use: Should metrics be consumed and exposed via driver's default consumer or provided with one of the external facades.
23+
*/
24+
public enum MetricsAdapter
25+
{
26+
/**
27+
* Disables metrics.
28+
*/
29+
DEV_NULL,
30+
31+
/**
32+
* Consumes and publishes metrics via the driver itself.
33+
*/
34+
DEFAULT,
35+
36+
/**
37+
* Consumes and publishes metrics via Micrometer. Ensure that Micrometer is on classpath when using this option.
38+
*/
39+
MICROMETER
40+
}

driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java

+17-7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.neo4j.driver.Driver;
3232
import org.neo4j.driver.Logger;
3333
import org.neo4j.driver.Logging;
34+
import org.neo4j.driver.MetricsAdapter;
3435
import org.neo4j.driver.internal.async.connection.BootstrapFactory;
3536
import org.neo4j.driver.internal.async.connection.ChannelConnector;
3637
import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl;
@@ -42,8 +43,10 @@
4243
import org.neo4j.driver.internal.cluster.loadbalancing.LoadBalancer;
4344
import org.neo4j.driver.internal.cluster.loadbalancing.LoadBalancingStrategy;
4445
import org.neo4j.driver.internal.logging.NettyLogging;
46+
import org.neo4j.driver.internal.metrics.DevNullMetricsProvider;
4547
import org.neo4j.driver.internal.metrics.InternalMetricsProvider;
4648
import org.neo4j.driver.internal.metrics.MetricsProvider;
49+
import org.neo4j.driver.internal.metrics.MicrometerMetricsProvider;
4750
import org.neo4j.driver.internal.retry.ExponentialBackoffRetryLogic;
4851
import org.neo4j.driver.internal.retry.RetryLogic;
4952
import org.neo4j.driver.internal.retry.RetrySettings;
@@ -56,7 +59,6 @@
5659

5760
import static org.neo4j.driver.internal.Scheme.isRoutingScheme;
5861
import static org.neo4j.driver.internal.cluster.IdentityResolver.IDENTITY_RESOLVER;
59-
import static org.neo4j.driver.internal.metrics.MetricsProvider.METRICS_DISABLED_PROVIDER;
6062
import static org.neo4j.driver.internal.util.ErrorUtil.addSuppressed;
6163

6264
public class DriverFactory
@@ -94,7 +96,7 @@ public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings r
9496
EventExecutorGroup eventExecutorGroup = bootstrap.config().group();
9597
RetryLogic retryLogic = createRetryLogic( retrySettings, eventExecutorGroup, config.logging() );
9698

97-
MetricsProvider metricsProvider = createDriverMetrics( config, createClock() );
99+
MetricsProvider metricsProvider = getOrCreateMetricsProvider( config, createClock() );
98100
ConnectionPool connectionPool = createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config,
99101
ownsEventLoopGroup, newRoutingSettings.routingContext() );
100102

@@ -114,16 +116,24 @@ protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan
114116
return new ConnectionPoolImpl( connector, bootstrap, poolSettings, metricsProvider.metricsListener(), config.logging(), clock, ownsEventLoopGroup );
115117
}
116118

117-
protected static MetricsProvider createDriverMetrics( Config config, Clock clock )
119+
protected static MetricsProvider getOrCreateMetricsProvider( Config config, Clock clock )
118120
{
119-
if( config.isMetricsEnabled() )
121+
MetricsAdapter metricsAdapter = config.metricsAdapter();
122+
// This can actually only happen when someone mocks the config
123+
if ( metricsAdapter == null )
120124
{
121-
return new InternalMetricsProvider( clock, config.logging() );
125+
metricsAdapter = config.isMetricsEnabled() ? MetricsAdapter.DEFAULT : MetricsAdapter.DEV_NULL;
122126
}
123-
else
127+
switch ( metricsAdapter )
124128
{
125-
return METRICS_DISABLED_PROVIDER;
129+
case DEV_NULL:
130+
return DevNullMetricsProvider.INSTANCE;
131+
case DEFAULT:
132+
return new InternalMetricsProvider( clock, config.logging() );
133+
case MICROMETER:
134+
return MicrometerMetricsProvider.forGlobalRegistry();
126135
}
136+
throw new IllegalStateException( "Unknown or unsupported MetricsAdapter: " + metricsAdapter );
127137
}
128138

129139
protected ChannelConnector createConnector( ConnectionSettings settings, SecurityPlan securityPlan,

driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
import org.neo4j.driver.Logger;
2626
import org.neo4j.driver.Logging;
2727
import org.neo4j.driver.Metrics;
28+
import org.neo4j.driver.internal.metrics.MetricsProvider;
2829
import org.neo4j.driver.Session;
2930
import org.neo4j.driver.SessionConfig;
3031
import org.neo4j.driver.async.AsyncSession;
3132
import org.neo4j.driver.internal.async.InternalAsyncSession;
3233
import org.neo4j.driver.internal.async.NetworkSession;
33-
import org.neo4j.driver.internal.metrics.MetricsProvider;
34+
import org.neo4j.driver.internal.metrics.DevNullMetricsProvider;
3435
import org.neo4j.driver.internal.reactive.InternalRxSession;
3536
import org.neo4j.driver.internal.security.SecurityPlan;
3637
import org.neo4j.driver.internal.types.InternalTypeSystem;
@@ -102,7 +103,7 @@ public Metrics metrics()
102103
@Override
103104
public boolean isMetricsEnabled()
104105
{
105-
return metricsProvider.isMetricsEnabled();
106+
return metricsProvider != DevNullMetricsProvider.INSTANCE;
106107
}
107108

108109
@Override

driver/src/main/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImpl.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.neo4j.driver.internal.spi.ConnectionPool;
4848
import org.neo4j.driver.internal.util.Clock;
4949
import org.neo4j.driver.internal.util.Futures;
50+
import org.neo4j.driver.net.ServerAddress;
5051

5152
import static java.lang.String.format;
5253
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setAuthorizationStateListener;
@@ -159,13 +160,13 @@ public void retainAll( Set<BoltServerAddress> addressesToRetain )
159160
}
160161

161162
@Override
162-
public int inUseConnections( BoltServerAddress address )
163+
public int inUseConnections( ServerAddress address )
163164
{
164165
return nettyChannelTracker.inUseChannelCount( address );
165166
}
166167

167168
@Override
168-
public int idleConnections( BoltServerAddress address )
169+
public int idleConnections( ServerAddress address )
169170
{
170171
return nettyChannelTracker.idleChannelCount( address );
171172
}
@@ -284,8 +285,8 @@ private ExtendedChannelPool getOrCreatePool( BoltServerAddress address )
284285
if ( pool == null )
285286
{
286287
pool = newPool( address );
287-
// before the connection pool is added I can add the metrics for the pool.
288-
metricsListener.putPoolMetrics( pool.id(), address, this );
288+
// before the connection pool is added I can register the metrics for the pool.
289+
metricsListener.registerPoolMetrics( pool.id(), address, () -> this.inUseConnections( address ), () -> this.idleConnections( address ) );
289290
addressToPool.put( address, pool );
290291
}
291292
return pool;

driver/src/main/java/org/neo4j/driver/internal/async/pool/NettyChannelTracker.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.neo4j.driver.internal.messaging.BoltProtocol;
3838
import org.neo4j.driver.internal.metrics.ListenerEvent;
3939
import org.neo4j.driver.internal.metrics.MetricsListener;
40+
import org.neo4j.driver.net.ServerAddress;
4041

4142
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.poolId;
4243
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAddress;
@@ -46,8 +47,8 @@ public class NettyChannelTracker implements ChannelPoolHandler
4647
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
4748
private final Lock read = lock.readLock();
4849
private final Lock write = lock.writeLock();
49-
private final Map<BoltServerAddress,Integer> addressToInUseChannelCount = new HashMap<>();
50-
private final Map<BoltServerAddress,Integer> addressToIdleChannelCount = new HashMap<>();
50+
private final Map<ServerAddress,Integer> addressToInUseChannelCount = new HashMap<>();
51+
private final Map<ServerAddress,Integer> addressToIdleChannelCount = new HashMap<>();
5152
private final Logger log;
5253
private final MetricsListener metricsListener;
5354
private final ChannelFutureListener closeListener = future -> channelClosed( future.channel() );
@@ -152,12 +153,12 @@ public void channelClosed( Channel channel )
152153
metricsListener.afterClosed( poolId( channel ) );
153154
}
154155

155-
public int inUseChannelCount( BoltServerAddress address )
156+
public int inUseChannelCount( ServerAddress address )
156157
{
157158
return retrieveInReadLock( () -> addressToInUseChannelCount.getOrDefault( address, 0 ) );
158159
}
159160

160-
public int idleChannelCount( BoltServerAddress address )
161+
public int idleChannelCount( ServerAddress address )
161162
{
162163
return retrieveInReadLock( () -> addressToIdleChannelCount.getOrDefault( address, 0 ) );
163164
}
@@ -213,9 +214,9 @@ private void decrementIdle( Channel channel )
213214
addressToIdleChannelCount.put( address, count - 1 );
214215
}
215216

216-
private void increment( Channel channel, Map<BoltServerAddress,Integer> countMap )
217+
private void increment( Channel channel, Map<ServerAddress,Integer> countMap )
217218
{
218-
BoltServerAddress address = serverAddress( channel );
219+
ServerAddress address = serverAddress( channel );
219220
Integer count = countMap.computeIfAbsent( address, k -> 0 );
220221
countMap.put( address, count + 1 );
221222
}

0 commit comments

Comments
 (0)