Skip to content

Commit 92e7341

Browse files
bigmontzinjectives
andauthored
Introduce Api Telemetry (#1487)
* Introduce Api Telemetry * Add telemetry for session run and add configuration/hint * Fix typo * Do not send telmetry on retries * Add tests for messages, connection and protocol changes * add tests for telemetry work * Add tests for Unmanaged transactions * Update Config documentation * Refactoring * Fix docs paragraph * Set telemetry flag on connection upon creation * Refactoring * Remove redundant header * Exclude vulnerable dependency * Refactoring * Refactoring * Add config builder tests * Update documentation * Update documentation * Add NetworkSession tests --------- Co-authored-by: Dmitriy Tverdiakov <[email protected]>
1 parent f0f46ba commit 92e7341

File tree

69 files changed

+2273
-118
lines changed

Some content is hidden

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

69 files changed

+2273
-118
lines changed

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,13 @@ public final class Config implements Serializable {
148148
*/
149149
private final MetricsAdapter metricsAdapter;
150150

151+
/**
152+
* Specify if telemetry collection is disabled.
153+
* <p>
154+
* By default, the driver will send anonymous usage statistics to the server it connects to if the server requests those.
155+
*/
156+
private final boolean telemetryDisabled;
157+
151158
private Config(ConfigBuilder builder) {
152159
this.logging = builder.logging;
153160
this.logLeakedSessions = builder.logLeakedSessions;
@@ -169,6 +176,7 @@ private Config(ConfigBuilder builder) {
169176

170177
this.eventLoopThreads = builder.eventLoopThreads;
171178
this.metricsAdapter = builder.metricsAdapter;
179+
this.telemetryDisabled = builder.telemetryDisabled;
172180
}
173181

174182
/**
@@ -335,6 +343,18 @@ public String userAgent() {
335343
return userAgent;
336344
}
337345

346+
/**
347+
* Returns if the telemetry is disabled on the driver side.
348+
* <p>
349+
* The telemetry is collected only when it is enabled both the server and the driver.
350+
*
351+
* @return {@code true} if telemetry is disabled or {@code false} otherwise
352+
* @since 5.13
353+
*/
354+
public boolean isTelemetryDisabled() {
355+
return telemetryDisabled;
356+
}
357+
338358
/**
339359
* Used to build new config instances
340360
*/
@@ -357,6 +377,8 @@ public static final class ConfigBuilder {
357377
private int eventLoopThreads = 0;
358378
private NotificationConfig notificationConfig = NotificationConfig.defaultConfig();
359379

380+
private boolean telemetryDisabled = false;
381+
360382
private ConfigBuilder() {}
361383

362384
/**
@@ -748,6 +770,31 @@ public ConfigBuilder withNotificationConfig(NotificationConfig notificationConfi
748770
return this;
749771
}
750772

773+
/**
774+
* Sets if telemetry is disabled on the driver side.
775+
* <p>
776+
* By default, the driver sends anonymous telemetry data to the server it connects to if the server has
777+
* telemetry enabled. This can be explicitly disabled on the driver side by setting this setting to
778+
* {@code true}.
779+
* <p>
780+
* At present, the driver sends which API type is used, like:
781+
* <ul>
782+
* <li>Managed transaction ({@link Session#executeWrite(TransactionCallback)},
783+
* {@link Session#executeRead(TransactionCallback)} and similar options)</li>
784+
* <li>Unmanaged transaction ({@link Session#beginTransaction()} and similar options)</li>
785+
* <li>Autocommit transaction ({@link Session#run(Query)} and similar options)</li>
786+
* <li>Executable query ({@link Driver#executableQuery(String)} and similar options)</li>
787+
* </ul>
788+
*
789+
* @param telemetryDisabled {@code true} if telemetry is disabled or {@code false} otherwise
790+
* @return this builder
791+
* @since 5.13
792+
*/
793+
public ConfigBuilder withTelemetryDisabled(boolean telemetryDisabled) {
794+
this.telemetryDisabled = telemetryDisabled;
795+
return this;
796+
}
797+
751798
/**
752799
* Create a config instance from this builder.
753800
*

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ protected InternalDriver createRoutingDriver(
269269
*/
270270
protected InternalDriver createDriver(
271271
SecurityPlan securityPlan, SessionFactory sessionFactory, MetricsProvider metricsProvider, Config config) {
272-
return new InternalDriver(securityPlan, sessionFactory, metricsProvider, config.logging());
272+
return new InternalDriver(
273+
securityPlan, sessionFactory, metricsProvider, config.isTelemetryDisabled(), config.logging());
273274
}
274275

275276
/**

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,22 @@ public class InternalDriver implements Driver {
6565
private final SessionFactory sessionFactory;
6666
private final Logger log;
6767

68+
private final boolean telemetryDisabled;
69+
6870
private final AtomicBoolean closed = new AtomicBoolean(false);
6971
private final MetricsProvider metricsProvider;
7072

7173
InternalDriver(
7274
SecurityPlan securityPlan,
7375
SessionFactory sessionFactory,
7476
MetricsProvider metricsProvider,
77+
boolean telemetryDisabled,
7578
Logging logging) {
7679
this.securityPlan = securityPlan;
7780
this.sessionFactory = sessionFactory;
7881
this.metricsProvider = metricsProvider;
7982
this.log = logging.getLog(getClass());
83+
this.telemetryDisabled = telemetryDisabled;
8084
}
8185

8286
@Override
@@ -215,7 +219,7 @@ private static RuntimeException driverCloseException() {
215219

216220
public NetworkSession newSession(SessionConfig config, AuthToken overrideAuthToken) {
217221
assertOpen();
218-
var session = sessionFactory.newInstance(config, overrideAuthToken);
222+
var session = sessionFactory.newInstance(config, overrideAuthToken, telemetryDisabled);
219223
if (closed.get()) {
220224
// session does not immediately acquire connection, it is fine to just throw
221225
throw driverCloseException();

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.neo4j.driver.SessionConfig;
3333
import org.neo4j.driver.TransactionCallback;
3434
import org.neo4j.driver.TransactionConfig;
35+
import org.neo4j.driver.internal.telemetry.TelemetryApi;
3536

3637
public class InternalExecutableQuery implements ExecutableQuery {
3738
private final Driver driver;
@@ -81,7 +82,8 @@ public <A, R, T> T execute(Collector<Record, A, R> recordCollector, ResultFinish
8182
return resultFinisher.finish(result.keys(), finishedValue, summary);
8283
};
8384
var accessMode = config.routing().equals(RoutingControl.WRITE) ? AccessMode.WRITE : AccessMode.READ;
84-
return session.execute(accessMode, txCallback, TransactionConfig.empty(), false);
85+
return session.execute(
86+
accessMode, txCallback, TransactionConfig.empty(), TelemetryApi.EXECUTABLE_QUERY, false);
8587
}
8688
}
8789

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import org.neo4j.driver.exceptions.ClientException;
3535
import org.neo4j.driver.internal.async.NetworkSession;
3636
import org.neo4j.driver.internal.spi.Connection;
37+
import org.neo4j.driver.internal.telemetry.ApiTelemetryWork;
38+
import org.neo4j.driver.internal.telemetry.TelemetryApi;
3739
import org.neo4j.driver.internal.util.Futures;
3840

3941
public class InternalSession extends AbstractQueryRunner implements Session {
@@ -93,7 +95,7 @@ public Transaction beginTransaction(TransactionConfig config) {
9395

9496
public Transaction beginTransaction(TransactionConfig config, String txType) {
9597
var tx = Futures.blockingGet(
96-
session.beginTransactionAsync(config, txType),
98+
session.beginTransactionAsync(config, txType, new ApiTelemetryWork(TelemetryApi.UNMANAGED_TRANSACTION)),
9799
() -> terminateConnectionOnThreadInterrupt("Thread interrupted while starting a transaction"));
98100
return new InternalTransaction(tx);
99101
}
@@ -107,12 +109,12 @@ public <T> T readTransaction(TransactionWork<T> work) {
107109
@Override
108110
@Deprecated
109111
public <T> T readTransaction(TransactionWork<T> work, TransactionConfig config) {
110-
return transaction(AccessMode.READ, work, config, true);
112+
return transaction(AccessMode.READ, work, config, TelemetryApi.MANAGED_TRANSACTION, true);
111113
}
112114

113115
@Override
114116
public <T> T executeRead(TransactionCallback<T> callback, TransactionConfig config) {
115-
return execute(AccessMode.READ, callback, config, true);
117+
return execute(AccessMode.READ, callback, config, TelemetryApi.MANAGED_TRANSACTION, true);
116118
}
117119

118120
@Override
@@ -124,12 +126,12 @@ public <T> T writeTransaction(TransactionWork<T> work) {
124126
@Override
125127
@Deprecated
126128
public <T> T writeTransaction(TransactionWork<T> work, TransactionConfig config) {
127-
return transaction(AccessMode.WRITE, work, config, true);
129+
return transaction(AccessMode.WRITE, work, config, TelemetryApi.MANAGED_TRANSACTION, true);
128130
}
129131

130132
@Override
131133
public <T> T executeWrite(TransactionCallback<T> callback, TransactionConfig config) {
132-
return execute(AccessMode.WRITE, callback, config, true);
134+
return execute(AccessMode.WRITE, callback, config, TelemetryApi.MANAGED_TRANSACTION, true);
133135
}
134136

135137
@Override
@@ -151,21 +153,29 @@ public void reset() {
151153
() -> terminateConnectionOnThreadInterrupt("Thread interrupted while resetting the session"));
152154
}
153155

154-
<T> T execute(AccessMode accessMode, TransactionCallback<T> callback, TransactionConfig config, boolean flush) {
155-
return transaction(accessMode, tx -> callback.execute(new DelegatingTransactionContext(tx)), config, flush);
156+
<T> T execute(
157+
AccessMode accessMode,
158+
TransactionCallback<T> callback,
159+
TransactionConfig config,
160+
TelemetryApi telemetryApi,
161+
boolean flush) {
162+
return transaction(
163+
accessMode, tx -> callback.execute(new DelegatingTransactionContext(tx)), config, telemetryApi, flush);
156164
}
157165

158166
private <T> T transaction(
159167
AccessMode mode,
160168
@SuppressWarnings("deprecation") TransactionWork<T> work,
161169
TransactionConfig config,
170+
TelemetryApi telemetryApi,
162171
boolean flush) {
163172
// use different code path compared to async so that work is executed in the caller thread
164173
// caller thread will also be the one who sleeps between retries;
165174
// it is unsafe to execute retries in the event loop threads because this can cause a deadlock
166175
// event loop thread will bock and wait for itself to read some data
176+
var apiTelemetryWork = new ApiTelemetryWork(telemetryApi);
167177
return session.retryLogic().retry(() -> {
168-
try (var tx = beginTransaction(mode, config, flush)) {
178+
try (var tx = beginTransaction(mode, config, apiTelemetryWork, flush)) {
169179

170180
var result = work.execute(tx);
171181
if (result instanceof Result) {
@@ -182,9 +192,10 @@ private <T> T transaction(
182192
});
183193
}
184194

185-
private Transaction beginTransaction(AccessMode mode, TransactionConfig config, boolean flush) {
195+
private Transaction beginTransaction(
196+
AccessMode mode, TransactionConfig config, ApiTelemetryWork apiTelemetryWork, boolean flush) {
186197
var tx = Futures.blockingGet(
187-
session.beginTransactionAsync(mode, config, null, flush),
198+
session.beginTransactionAsync(mode, config, null, apiTelemetryWork, flush),
188199
() -> terminateConnectionOnThreadInterrupt("Thread interrupted while starting a transaction"));
189200
return new InternalTransaction(tx);
190201
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.neo4j.driver.internal.async.NetworkSession;
2525

2626
public interface SessionFactory {
27-
NetworkSession newInstance(SessionConfig sessionConfig, AuthToken overrideAuthToken);
27+
NetworkSession newInstance(SessionConfig sessionConfig, AuthToken overrideAuthToken, boolean telemetryDisabled);
2828

2929
CompletionStage<Void> verifyConnectivity();
3030

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public class SessionFactoryImpl implements SessionFactory {
5353
}
5454

5555
@Override
56-
public NetworkSession newInstance(SessionConfig sessionConfig, AuthToken overrideAuthToken) {
56+
public NetworkSession newInstance(
57+
SessionConfig sessionConfig, AuthToken overrideAuthToken, boolean telemetryDisabled) {
5758
return createSession(
5859
connectionProvider,
5960
retryLogic,
@@ -65,7 +66,8 @@ public NetworkSession newInstance(SessionConfig sessionConfig, AuthToken overrid
6566
logging,
6667
sessionConfig.bookmarkManager().orElse(NoOpBookmarkManager.INSTANCE),
6768
sessionConfig.notificationConfig(),
68-
overrideAuthToken);
69+
overrideAuthToken,
70+
telemetryDisabled);
6971
}
7072

7173
private Set<Bookmark> toDistinctSet(Iterable<Bookmark> bookmarks) {
@@ -142,7 +144,8 @@ private NetworkSession createSession(
142144
Logging logging,
143145
BookmarkManager bookmarkManager,
144146
NotificationConfig notificationConfig,
145-
AuthToken authToken) {
147+
AuthToken authToken,
148+
boolean telemetryDisabled) {
146149
Objects.requireNonNull(bookmarks, "bookmarks may not be null");
147150
Objects.requireNonNull(bookmarkManager, "bookmarkManager may not be null");
148151
return leakedSessionsLoggingEnabled
@@ -157,7 +160,8 @@ private NetworkSession createSession(
157160
logging,
158161
bookmarkManager,
159162
notificationConfig,
160-
authToken)
163+
authToken,
164+
telemetryDisabled)
161165
: new NetworkSession(
162166
connectionProvider,
163167
retryLogic,
@@ -169,6 +173,7 @@ private NetworkSession createSession(
169173
logging,
170174
bookmarkManager,
171175
notificationConfig,
172-
authToken);
176+
authToken,
177+
telemetryDisabled);
173178
}
174179
}

driver/src/main/java/org/neo4j/driver/internal/async/InternalAsyncSession.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import org.neo4j.driver.async.ResultCursor;
3939
import org.neo4j.driver.exceptions.ClientException;
4040
import org.neo4j.driver.internal.InternalBookmark;
41+
import org.neo4j.driver.internal.telemetry.ApiTelemetryWork;
42+
import org.neo4j.driver.internal.telemetry.TelemetryApi;
4143
import org.neo4j.driver.internal.util.Futures;
4244

4345
public class InternalAsyncSession extends AsyncAbstractQueryRunner implements AsyncSession {
@@ -80,7 +82,8 @@ public CompletionStage<AsyncTransaction> beginTransactionAsync() {
8082

8183
@Override
8284
public CompletionStage<AsyncTransaction> beginTransactionAsync(TransactionConfig config) {
83-
return session.beginTransactionAsync(config).thenApply(InternalAsyncTransaction::new);
85+
return session.beginTransactionAsync(config, new ApiTelemetryWork(TelemetryApi.UNMANAGED_TRANSACTION))
86+
.thenApply(InternalAsyncTransaction::new);
8487
}
8588

8689
@Override
@@ -136,9 +139,10 @@ private <T> CompletionStage<T> transactionAsync(
136139
AccessMode mode,
137140
@SuppressWarnings("deprecation") AsyncTransactionWork<CompletionStage<T>> work,
138141
TransactionConfig config) {
142+
var apiTelemetryWork = new ApiTelemetryWork(TelemetryApi.MANAGED_TRANSACTION);
139143
return session.retryLogic().retryAsync(() -> {
140144
var resultFuture = new CompletableFuture<T>();
141-
var txFuture = session.beginTransactionAsync(mode, config);
145+
var txFuture = session.beginTransactionAsync(mode, config, apiTelemetryWork);
142146

143147
txFuture.whenComplete((tx, completionError) -> {
144148
var error = Futures.completionExceptionCause(completionError);

driver/src/main/java/org/neo4j/driver/internal/async/LeakLoggingNetworkSession.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public LeakLoggingNetworkSession(
4848
Logging logging,
4949
BookmarkManager bookmarkManager,
5050
NotificationConfig notificationConfig,
51-
AuthToken overrideAuthToken) {
51+
AuthToken overrideAuthToken,
52+
boolean telemetryDisabled) {
5253
super(
5354
connectionProvider,
5455
retryLogic,
@@ -60,7 +61,8 @@ public LeakLoggingNetworkSession(
6061
logging,
6162
bookmarkManager,
6263
notificationConfig,
63-
overrideAuthToken);
64+
overrideAuthToken,
65+
telemetryDisabled);
6466
this.stackTrace = captureStackTrace();
6567
}
6668

driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public class NetworkConnection implements Connection {
6767
private final InboundMessageDispatcher messageDispatcher;
6868
private final String serverAgent;
6969
private final BoltServerAddress serverAddress;
70+
private final boolean telemetryEnabled;
7071
private final BoltProtocol protocol;
7172
private final ExtendedChannelPool channelPool;
7273
private final CompletableFuture<Void> releaseFuture;
@@ -92,6 +93,7 @@ public NetworkConnection(
9293
this.messageDispatcher = ChannelAttributes.messageDispatcher(channel);
9394
this.serverAgent = ChannelAttributes.serverAgent(channel);
9495
this.serverAddress = ChannelAttributes.serverAddress(channel);
96+
this.telemetryEnabled = ChannelAttributes.telemetryEnabled(channel);
9597
this.protocol = BoltProtocol.forChannel(channel);
9698
this.channelPool = channelPool;
9799
this.releaseFuture = new CompletableFuture<>();
@@ -136,6 +138,11 @@ public void writeAndFlush(Message message, ResponseHandler handler) {
136138
}
137139
}
138140

141+
@Override
142+
public boolean isTelemetryEnabled() {
143+
return telemetryEnabled;
144+
}
145+
139146
@Override
140147
public CompletionStage<Void> reset(Throwable throwable) {
141148
var result = new CompletableFuture<Void>();

0 commit comments

Comments
 (0)