Skip to content

Commit

Permalink
refactor: use java.time.Duration in Connection API (#3324)
Browse files Browse the repository at this point in the history
Use java.time.Duration internally in the Connection API, as that is
the value that is used in the public API of Connection. For example,
the `Connection#setMaxCommitDelay(java.time.Duration)` method uses
this type in the public API.

Refactoring the internal classes to also use this type makes it
easier to plug these properties into a generic framework for
connection state.
  • Loading branch information
olavloite authored and lqiu96 committed Sep 19, 2024
1 parent 004ad58 commit fa8d39a
Show file tree
Hide file tree
Showing 15 changed files with 13,314 additions and 8,991 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package com.google.cloud.spanner.connection;

import static com.google.cloud.spanner.connection.ReadOnlyStalenessUtil.parseTimeUnit;
import static com.google.cloud.spanner.connection.ReadOnlyStalenessUtil.toChronoUnit;

import com.google.cloud.spanner.ErrorCode;
import com.google.cloud.spanner.Options.RpcPriority;
import com.google.cloud.spanner.SpannerException;
Expand All @@ -27,15 +30,14 @@
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.protobuf.Duration;
import com.google.protobuf.util.Durations;
import com.google.spanner.v1.DirectedReadOptions;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -165,9 +167,16 @@ public Integer convert(String value) {

/** Converter from string to {@link Duration}. */
static class DurationConverter implements ClientSideStatementValueConverter<Duration> {
private final String resetValue;

private final Pattern allowedValues;

public DurationConverter(String allowedValues) {
this("NULL", allowedValues);
}

DurationConverter(String resetValue, String allowedValues) {
this.resetValue = Preconditions.checkNotNull(resetValue);
// Remove the parentheses from the beginning and end.
this.allowedValues =
Pattern.compile(
Expand All @@ -183,62 +192,35 @@ public Class<Duration> getParameterClass() {
public Duration convert(String value) {
Matcher matcher = allowedValues.matcher(value);
if (matcher.find()) {
if (matcher.group(0).equalsIgnoreCase("null")) {
return Durations.fromNanos(0L);
if (value.trim().equalsIgnoreCase(resetValue)) {
return Duration.ZERO;
} else {
Duration duration =
ReadOnlyStalenessUtil.createDuration(
Long.parseLong(matcher.group(1)),
ReadOnlyStalenessUtil.parseTimeUnit(matcher.group(2)));
if (duration.getSeconds() == 0L && duration.getNanos() == 0) {
try {
Duration duration;
if (matcher.group(1) != null && matcher.group(2) != null) {
ChronoUnit unit = toChronoUnit(parseTimeUnit(matcher.group(2)));
duration = Duration.of(Long.parseLong(matcher.group(1)), unit);
} else {
duration = Duration.ofMillis(Long.parseLong(value.trim()));
}
if (duration.isZero()) {
return null;
}
return duration;
} catch (NumberFormatException exception) {
// Converters should return null for invalid values.
return null;
}
return duration;
}
}
return null;
}
}

/** Converter from string to {@link Duration}. */
static class PgDurationConverter implements ClientSideStatementValueConverter<Duration> {
private final Pattern allowedValues;

static class PgDurationConverter extends DurationConverter {
public PgDurationConverter(String allowedValues) {
// Remove the parentheses from the beginning and end.
this.allowedValues =
Pattern.compile(
"(?is)\\A" + allowedValues.substring(1, allowedValues.length() - 1) + "\\z");
}

@Override
public Class<Duration> getParameterClass() {
return Duration.class;
}

@Override
public Duration convert(String value) {
Matcher matcher = allowedValues.matcher(value);
if (matcher.find()) {
Duration duration;
if (matcher.group(0).equalsIgnoreCase("default")) {
return Durations.fromNanos(0L);
} else if (matcher.group(2) == null) {
duration =
ReadOnlyStalenessUtil.createDuration(
Long.parseLong(matcher.group(0)), TimeUnit.MILLISECONDS);
} else {
duration =
ReadOnlyStalenessUtil.createDuration(
Long.parseLong(matcher.group(1)),
ReadOnlyStalenessUtil.parseTimeUnit(matcher.group(2)));
}
if (duration.getSeconds() == 0L && duration.getNanos() == 0) {
return null;
}
return duration;
}
return null;
super("DEFAULT", allowedValues);
}
}

Expand Down Expand Up @@ -288,7 +270,7 @@ public TimestampBound convert(String value) {
try {
return TimestampBound.ofExactStaleness(
Long.parseLong(matcher.group(groupIndex + 2)),
ReadOnlyStalenessUtil.parseTimeUnit(matcher.group(groupIndex + 3)));
parseTimeUnit(matcher.group(groupIndex + 3)));
} catch (IllegalArgumentException e) {
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.INVALID_ARGUMENT, e.getMessage());
Expand All @@ -297,7 +279,7 @@ public TimestampBound convert(String value) {
try {
return TimestampBound.ofMaxStaleness(
Long.parseLong(matcher.group(groupIndex + 2)),
ReadOnlyStalenessUtil.parseTimeUnit(matcher.group(groupIndex + 3)));
parseTimeUnit(matcher.group(groupIndex + 3)));
} catch (IllegalArgumentException e) {
throw SpannerExceptionFactory.newSpannerException(
ErrorCode.INVALID_ARGUMENT, e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.TimestampBound;
import com.google.cloud.spanner.connection.PgTransactionMode.IsolationLevel;
import com.google.protobuf.Duration;
import com.google.spanner.v1.DirectedReadOptions;
import java.time.Duration;

/**
* The Cloud Spanner JDBC driver supports a number of client side statements that are interpreted by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@
import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.protobuf.Duration;
import com.google.spanner.v1.DirectedReadOptions;
import com.google.spanner.v1.PlanNode;
import com.google.spanner.v1.QueryPlan;
import com.google.spanner.v1.RequestOptions;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -205,14 +205,19 @@ public StatementResult statementShowAutocommitDmlMode() {

@Override
public StatementResult statementSetStatementTimeout(Duration duration) {
if (duration.getSeconds() == 0L && duration.getNanos() == 0) {
if (duration == null || duration.isZero()) {
getConnection().clearStatementTimeout();
} else {
com.google.protobuf.Duration protoDuration =
com.google.protobuf.Duration.newBuilder()
.setSeconds(duration.getSeconds())
.setNanos(duration.getNano())
.build();
TimeUnit unit =
ReadOnlyStalenessUtil.getAppropriateTimeUnit(
new ReadOnlyStalenessUtil.DurationGetter(duration));
new ReadOnlyStalenessUtil.DurationGetter(protoDuration));
getConnection()
.setStatementTimeout(ReadOnlyStalenessUtil.durationToUnits(duration, unit), unit);
.setStatementTimeout(ReadOnlyStalenessUtil.durationToUnits(protoDuration, unit), unit);
}
return noResult(SET_STATEMENT_TIMEOUT);
}
Expand Down Expand Up @@ -343,11 +348,7 @@ public StatementResult statementShowReturnCommitStats() {

@Override
public StatementResult statementSetMaxCommitDelay(Duration duration) {
getConnection()
.setMaxCommitDelay(
duration == null || duration.equals(Duration.getDefaultInstance())
? null
: java.time.Duration.ofSeconds(duration.getSeconds(), duration.getNanos()));
getConnection().setMaxCommitDelay(duration == null || duration.isZero() ? null : duration);
return noResult(SET_MAX_COMMIT_DELAY);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.cloud.spanner.TimestampBound.Mode;
import com.google.protobuf.Duration;
import com.google.protobuf.util.Durations;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

/**
Expand Down Expand Up @@ -93,6 +94,31 @@ static TimeUnit parseTimeUnit(String unit) {
ErrorCode.INVALID_ARGUMENT, "Invalid option for time unit: " + unit);
}

/**
* Convert from {@link TimeUnit} to {@link ChronoUnit}. This code is copied from {@link
* TimeUnit#toChronoUnit()}, which is available in Java 9 and higher.
*/
static ChronoUnit toChronoUnit(TimeUnit timeUnit) {
switch (timeUnit) {
case NANOSECONDS:
return ChronoUnit.NANOS;
case MICROSECONDS:
return ChronoUnit.MICROS;
case MILLISECONDS:
return ChronoUnit.MILLIS;
case SECONDS:
return ChronoUnit.SECONDS;
case MINUTES:
return ChronoUnit.MINUTES;
case HOURS:
return ChronoUnit.HOURS;
case DAYS:
return ChronoUnit.DAYS;
default:
throw new IllegalArgumentException();
}
}

/**
* Internal interface that is used to generalize getting a time duration from Cloud Spanner
* read-only staleness settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,21 @@
"statementType": "SET_STATEMENT_TIMEOUT",
"regex": "(?is)\\A\\s*set\\s+statement_timeout\\s*(?:=)\\s*(.*)\\z",
"method": "statementSetStatementTimeout",
"exampleStatements": ["set statement_timeout=null", "set statement_timeout='1s'", "set statement_timeout='100ms'", "set statement_timeout='10000us'", "set statement_timeout='9223372036854775807ns'"],
"exampleStatements": [
"set statement_timeout=null",
"set statement_timeout = null ",
"set statement_timeout='1s'",
"set statement_timeout = '1s' ",
"set statement_timeout=100",
"set statement_timeout = 100 ",
"set statement_timeout='100ms'",
"set statement_timeout='10000us'",
"set statement_timeout='9223372036854775807ns'"
],
"setStatement": {
"propertyName": "STATEMENT_TIMEOUT",
"separator": "=",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|NULL)",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|\\d{1,19}|NULL)",
"converterName": "ClientSideStatementValueConverters$DurationConverter"
}
},
Expand Down Expand Up @@ -485,11 +495,23 @@
"statementType": "SET_MAX_COMMIT_DELAY",
"regex": "(?is)\\A\\s*set\\s+max_commit_delay\\s*(?:=)\\s*(.*)\\z",
"method": "statementSetMaxCommitDelay",
"exampleStatements": ["set max_commit_delay=null", "set max_commit_delay='1s'", "set max_commit_delay='100ms'", "set max_commit_delay='10000us'", "set max_commit_delay='9223372036854775807ns'"],
"exampleStatements": [
"set max_commit_delay=null",
"set max_commit_delay = null",
"set max_commit_delay = null ",
"set max_commit_delay=1000",
"set max_commit_delay = 1000",
"set max_commit_delay = 1000 ",
"set max_commit_delay='1s'",
"set max_commit_delay = '1s'",
"set max_commit_delay = '1s' ",
"set max_commit_delay='100ms'",
"set max_commit_delay='10000us'",
"set max_commit_delay='9223372036854775807ns'"],
"setStatement": {
"propertyName": "MAX_COMMIT_DELAY",
"separator": "=",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|NULL)",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|\\d{1,19}|NULL)",
"converterName": "ClientSideStatementValueConverters$DurationConverter"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,13 @@
"method": "statementSetStatementTimeout",
"exampleStatements": [
"set statement_timeout=default",
"set statement_timeout = default ",
"set statement_timeout = DEFAULT ",
"set statement_timeout='1s'",
"set statement_timeout = '1s' ",
"set statement_timeout='100ms'",
"set statement_timeout=100",
"set statement_timeout = 100 ",
"set statement_timeout='10000us'",
"set statement_timeout='9223372036854775807ns'",
"set statement_timeout to default",
Expand All @@ -436,7 +440,7 @@
"setStatement": {
"propertyName": "STATEMENT_TIMEOUT",
"separator": "(?:=|\\s+TO\\s+)",
"allowedValues": "(\\d{1,19}|'(\\d{1,19})(s|ms|us|ns)'|DEFAULT)",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|\\d{1,19}|DEFAULT)",
"converterName": "ClientSideStatementValueConverters$PgDurationConverter"
}
},
Expand Down Expand Up @@ -651,11 +655,23 @@
"statementType": "SET_MAX_COMMIT_DELAY",
"regex": "(?is)\\A\\s*set\\s+spanner\\.max_commit_delay(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z",
"method": "statementSetMaxCommitDelay",
"exampleStatements": ["set spanner.max_commit_delay=null", "set spanner.max_commit_delay='1s'", "set spanner.max_commit_delay='100ms'", "set spanner.max_commit_delay to '10000us'", "set spanner.max_commit_delay TO '9223372036854775807ns'"],
"exampleStatements": [
"set spanner.max_commit_delay=null",
"set spanner.max_commit_delay = NULL",
"set spanner.max_commit_delay = null ",
"set spanner.max_commit_delay='1s'",
"set spanner.max_commit_delay = '1s'",
"set spanner.max_commit_delay = '1s' ",
"set spanner.max_commit_delay=1000",
"set spanner.max_commit_delay = 1000",
"set spanner.max_commit_delay = 1000 ",
"set spanner.max_commit_delay='100ms'",
"set spanner.max_commit_delay to '10000us'",
"set spanner.max_commit_delay TO '9223372036854775807ns'"],
"setStatement": {
"propertyName": "SPANNER.MAX_COMMIT_DELAY",
"separator": "(?:=|\\s+TO\\s+)",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|NULL)",
"allowedValues": "('(\\d{1,19})(s|ms|us|ns)'|\\d{1,19}|NULL)",
"converterName": "ClientSideStatementValueConverters$DurationConverter"
}
},
Expand Down
Loading

0 comments on commit fa8d39a

Please sign in to comment.