Skip to content

Add support for GQL-status objects #1555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions driver/clirr-ignored-differences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -603,4 +603,28 @@
<method>org.neo4j.driver.ExecutableQuery withAuthToken(org.neo4j.driver.AuthToken)</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/ResultSummary</className>
<differenceType>7012</differenceType>
<method>java.util.Set gqlStatusObjects()</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/Notification</className>
<differenceType>7012</differenceType>
<method>java.util.Optional inputPosition()</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/Notification</className>
<differenceType>7012</differenceType>
<method>java.util.Optional classification()</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/Notification</className>
<differenceType>7012</differenceType>
<method>java.util.Optional rawClassification()</method>
</difference>

</differences>
98 changes: 88 additions & 10 deletions driver/src/main/java/org/neo4j/driver/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING;
import static org.neo4j.driver.internal.util.DriverInfoUtil.driverVersion;

import io.netty.channel.EventLoop;
import java.io.File;
import java.io.Serial;
import java.io.Serializable;
Expand All @@ -28,9 +29,14 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
import org.neo4j.driver.internal.InternalNotificationConfig;
import org.neo4j.driver.internal.SecuritySettings;
import org.neo4j.driver.internal.async.pool.PoolSettings;
import org.neo4j.driver.internal.cluster.RoutingSettings;
Expand All @@ -39,6 +45,8 @@
import org.neo4j.driver.net.ServerAddressResolver;
import org.neo4j.driver.util.Experimental;
import org.neo4j.driver.util.Immutable;
import org.neo4j.driver.util.Preview;
import org.neo4j.driver.util.Resource;

/**
* A configuration class to config driver properties.
Expand Down Expand Up @@ -223,6 +231,7 @@ public int connectionTimeoutMillis() {

/**
* Returns the maximum connection pool size.
*
* @return the maximum size
*/
public int maxConnectionPoolSize() {
Expand All @@ -231,6 +240,7 @@ public int maxConnectionPoolSize() {

/**
* Returns the connection acquisition timeout in milliseconds.
*
* @return the acquisition timeout
*/
public long connectionAcquisitionTimeoutMillis() {
Expand Down Expand Up @@ -296,6 +306,7 @@ public long maxTransactionRetryTimeMillis() {

/**
* Returns the fetch size.
*
* @return the fetch size
*/
public long fetchSize() {
Expand All @@ -304,6 +315,7 @@ public long fetchSize() {

/**
* Returns notification config.
*
* @return the notification config
* @since 5.7
*/
Expand All @@ -312,7 +324,35 @@ public NotificationConfig notificationConfig() {
}

/**
* Returns the number of {@link io.netty.channel.EventLoop} threads.
* Returns a minimum notification severity.
*
* @return an {@link Optional} of minimum {@link NotificationSeverity} or an empty {@link Optional} if it is not set
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public Optional<NotificationSeverity> minimumNotificationSeverity() {
return Optional.ofNullable(((InternalNotificationConfig) notificationConfig).minimumSeverity());
}

/**
* Returns a set of disabled notification classifications.
* @return the {@link Set} of disabled {@link NotificationClassification}
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public Set<NotificationClassification> disabledNotificationClassifications() {
var disabledCategories = ((InternalNotificationConfig) notificationConfig).disabledCategories();
return disabledCategories != null
? ((InternalNotificationConfig) notificationConfig)
.disabledCategories().stream()
.map(NotificationClassification.class::cast)
.collect(Collectors.toUnmodifiableSet())
: Collections.emptySet();
}

/**
* Returns the number of {@link EventLoop} threads.
*
* @return the number of threads
*/
public int eventLoopThreads() {
Expand All @@ -328,6 +368,7 @@ public boolean isMetricsEnabled() {

/**
* Returns the {@link MetricsAdapter}.
*
* @return the metrics adapter
*/
public MetricsAdapter metricsAdapter() {
Expand Down Expand Up @@ -373,6 +414,7 @@ public static final class ConfigBuilder {
private MetricsAdapter metricsAdapter = MetricsAdapter.DEV_NULL;
private long fetchSize = FetchSizeUtil.DEFAULT_FETCH_SIZE;
private int eventLoopThreads = 0;

private NotificationConfig notificationConfig = NotificationConfig.defaultConfig();

private boolean telemetryDisabled = false;
Expand All @@ -399,7 +441,7 @@ public ConfigBuilder withLogging(Logging logging) {
* Enable logging of leaked sessions.
* <p>
* Each {@link Session session} is associated with a network connection and thus is a
* {@link org.neo4j.driver.util.Resource resource} that needs to be explicitly closed.
* {@link Resource resource} that needs to be explicitly closed.
* Unclosed sessions will result in socket leaks and could cause {@link OutOfMemoryError}s.
* <p>
* Session is considered to be leaked when it is finalized via {@link Object#finalize()} while not being
Expand Down Expand Up @@ -579,8 +621,8 @@ public ConfigBuilder withTrustStrategy(TrustStrategy trustStrategy) {
public ConfigBuilder withRoutingTablePurgeDelay(long delay, TimeUnit unit) {
var routingTablePurgeDelayMillis = unit.toMillis(delay);
if (routingTablePurgeDelayMillis < 0) {
throw new IllegalArgumentException(String.format(
"The routing table purge delay may not be smaller than 0, but was %d %s.", delay, unit));
throw new IllegalArgumentException(
format("The routing table purge delay may not be smaller than 0, but was %d %s.", delay, unit));
}
this.routingTablePurgeDelayMillis = routingTablePurgeDelayMillis;
return this;
Expand All @@ -591,11 +633,11 @@ public ConfigBuilder withRoutingTablePurgeDelay(long delay, TimeUnit unit) {
* This config is only valid when the driver is used with servers that support Bolt V4 (Server version 4.0 and later).
* <p>
* Bolt V4 enables pulling records in batches to allow client to take control of data population and apply back pressure to server.
* This config specifies the default fetch size for all query runs using {@link Session} and {@link org.neo4j.driver.async.AsyncSession}.
* This config specifies the default fetch size for all query runs using {@link Session} and {@link AsyncSession}.
* By default, the value is set to {@code 1000}.
* Use {@code -1} to disables back pressure and config client to pull all records at once after each run.
* <p>
* This config only applies to run results obtained via {@link Session} and {@link org.neo4j.driver.async.AsyncSession}.
* This config only applies to run results obtained via {@link Session} and {@link AsyncSession}.
* As with the reactive sessions the batch size is managed by the subscription requests instead.
*
* @param size the default record fetch size when pulling records in batches using Bolt V4.
Expand Down Expand Up @@ -627,11 +669,11 @@ public ConfigBuilder withConnectionTimeout(long value, TimeUnit unit) {
var connectionTimeoutMillis = unit.toMillis(value);
if (connectionTimeoutMillis < 0) {
throw new IllegalArgumentException(
String.format("The connection timeout may not be smaller than 0, but was %d %s.", value, unit));
format("The connection timeout may not be smaller than 0, but was %d %s.", value, unit));
}
var connectionTimeoutMillisInt = (int) connectionTimeoutMillis;
if (connectionTimeoutMillisInt != connectionTimeoutMillis) {
throw new IllegalArgumentException(String.format(
throw new IllegalArgumentException(format(
"The connection timeout must represent int value when converted to milliseconds %d.",
connectionTimeoutMillis));
}
Expand All @@ -655,7 +697,7 @@ public ConfigBuilder withMaxTransactionRetryTime(long value, TimeUnit unit) {
var maxRetryTimeMs = unit.toMillis(value);
if (maxRetryTimeMs < 0) {
throw new IllegalArgumentException(
String.format("The max retry time may not be smaller than 0, but was %d %s.", value, unit));
format("The max retry time may not be smaller than 0, but was %d %s.", value, unit));
}
this.maxTransactionRetryTimeMillis = maxRetryTimeMs;
return this;
Expand Down Expand Up @@ -732,7 +774,7 @@ public ConfigBuilder withMetricsAdapter(MetricsAdapter metricsAdapter) {
public ConfigBuilder withEventLoopThreads(int size) {
if (size < 1) {
throw new IllegalArgumentException(
String.format("The event loop thread may not be smaller than 1, but was %d.", size));
format("The event loop thread may not be smaller than 1, but was %d.", size));
}
this.eventLoopThreads = size;
return this;
Expand Down Expand Up @@ -768,6 +810,42 @@ public ConfigBuilder withNotificationConfig(NotificationConfig notificationConfi
return this;
}

/**
* Sets a minimum severity for notifications produced by the server.
*
* @param minimumNotificationSeverity the minimum notification severity
* @return this builder
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public ConfigBuilder withMinimumNotificationSeverity(NotificationSeverity minimumNotificationSeverity) {
if (minimumNotificationSeverity == null) {
notificationConfig = NotificationConfig.disableAllConfig();
} else {
notificationConfig = notificationConfig.enableMinimumSeverity(minimumNotificationSeverity);
}
return this;
}

/**
* Sets a set of disabled classifications for notifications produced by the server.
*
* @param disabledNotificationClassifications the set of disabled notification classifications
* @return this builder
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public ConfigBuilder withDisabledNotificationClassifications(
Set<NotificationClassification> disabledNotificationClassifications) {
var disabledCategories = disabledNotificationClassifications == null
? Collections.<NotificationCategory>emptySet()
: disabledNotificationClassifications.stream()
.map(NotificationCategory.class::cast)
.collect(Collectors.toSet());
notificationConfig = notificationConfig.disableCategories(disabledCategories);
return this;
}

/**
* Sets if telemetry is disabled on the driver side.
* <p>
Expand Down
20 changes: 9 additions & 11 deletions driver/src/main/java/org/neo4j/driver/NotificationCategory.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,48 @@
package org.neo4j.driver;

import java.io.Serializable;
import org.neo4j.driver.internal.InternalNotificationCategory;
import org.neo4j.driver.internal.InternalNotificationCategory.Type;

/**
* Notification category.
*
* @since 5.7
*/
public sealed interface NotificationCategory extends Serializable permits InternalNotificationCategory {
public sealed interface NotificationCategory extends Serializable permits NotificationClassification {
/**
* A hint category.
* <p>
* For instance, the given hint cannot be satisfied.
*/
NotificationCategory HINT = new InternalNotificationCategory(Type.HINT);
NotificationCategory HINT = NotificationClassification.HINT;

/**
* An unrecognized category.
* <p>
* For instance, the query or command mentions entities that are unknown to the system.
*/
NotificationCategory UNRECOGNIZED = new InternalNotificationCategory(Type.UNRECOGNIZED);
NotificationCategory UNRECOGNIZED = NotificationClassification.UNRECOGNIZED;

/**
* An unsupported category.
* <p>
* For instance, the query/command is trying to use features that are not supported by the current system or using
* features that are experimental and should not be used in production.
*/
NotificationCategory UNSUPPORTED = new InternalNotificationCategory(Type.UNSUPPORTED);
NotificationCategory UNSUPPORTED = NotificationClassification.UNSUPPORTED;

/**
* A performance category.
* <p>
* For instance, the query uses costly operations and might be slow.
*/
NotificationCategory PERFORMANCE = new InternalNotificationCategory(Type.PERFORMANCE);
NotificationCategory PERFORMANCE = NotificationClassification.PERFORMANCE;

/**
* A deprecation category.
* <p>
* For instance, the query/command use deprecated features that should be replaced.
*/
NotificationCategory DEPRECATION = new InternalNotificationCategory(Type.DEPRECATION);
NotificationCategory DEPRECATION = NotificationClassification.DEPRECATION;

/**
* A security category.
Expand All @@ -72,7 +70,7 @@ public sealed interface NotificationCategory extends Serializable permits Intern
*
* @since 5.14
*/
NotificationCategory SECURITY = new InternalNotificationCategory(Type.SECURITY);
NotificationCategory SECURITY = NotificationClassification.SECURITY;

/**
* A topology category.
Expand All @@ -84,12 +82,12 @@ public sealed interface NotificationCategory extends Serializable permits Intern
*
* @since 5.14
*/
NotificationCategory TOPOLOGY = new InternalNotificationCategory(Type.TOPOLOGY);
NotificationCategory TOPOLOGY = NotificationClassification.TOPOLOGY;

/**
* A generic category.
* <p>
* For instance, notifications that are not part of a more specific class.
*/
NotificationCategory GENERIC = new InternalNotificationCategory(Type.GENERIC);
NotificationCategory GENERIC = NotificationClassification.GENERIC;
}
Loading