All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.topicus.jdbc.shaded.com.google.cloud.spanner.TimestampBound Maven / Gradle / Ivy

/*
 * Copyright 2017 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package nl.topicus.jdbc.shaded.com.google.cloud.spanner;

import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkArgument;
import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkNotNull;
import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkState;

import nl.topicus.jdbc.shaded.com.google.cloud.Timestamp;
import nl.topicus.jdbc.shaded.com.google.protobuf.Duration;
import nl.topicus.jdbc.shaded.com.google.protobuf.util.Durations;
import nl.topicus.jdbc.shaded.com.google.spanner.v1.TransactionOptions;

import java.io.Serializable;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Defines how Cloud Spanner will choose a timestamp for a read-only transaction or a single
 * read/query.
 *
 * 

The types of timestamp bound are: * *

    *
  • Strong (the default). *
  • Bounded staleness. *
  • Exact staleness. *
* *

If the Cloud Spanner database to be read is geographically distributed, stale read-only * transactions can execute more quickly than strong or read-write transactions, because they are * able to execute far from the leader replica. * *

Each type of timestamp bound is discussed in detail below. * *

Strong reads

* *

Strong reads are guaranteed to see the effects of all transactions that have committed before * the start of the read. Furthermore, all rows yielded by a single read are consistent with each * other - if any part of the read observes a transaction, all parts of the read see the * transaction. * *

Strong reads are not repeatable: two consecutive strong read-only transactions might return * inconsistent results if there are concurrent writes. If consistency across reads is required, the * reads should be executed within a transaction or at an exact read timestamp. * *

Use {@link #strong()} to create a bound of this type. * *

Exact Staleness

* *

These timestamp bounds execute reads at a user-specified timestamp. Reads at a timestamp are * guaranteed to see a consistent prefix of the global transaction history: they observe * modifications done by all transactions with a commit timestamp less than or equal to the read * timestamp, and observe none of the modifications done by transactions with a larger commit * timestamp. They will block until all conflicting transactions that may be assigned commit * timestamps less than or equal to the read timestamp have finished. * *

The timestamp can either be expressed as an absolute Cloud Spanner commit timestamp or a * staleness relative to the current time. * *

These modes do not require a "negotiation phase" to pick a timestamp. As a result, they * execute slightly faster than the equivalent boundedly stale concurrency modes. On the other hand, * boundedly stale reads usually return fresher results. * *

Use {@link #ofReadTimestamp(Timestamp)} and {@link #ofExactStaleness(long, TimeUnit)} to * create a bound of this type. * *

Bounded Staleness

* *

Bounded staleness modes allow Cloud Spanner to pick the read timestamp, subject to a * user-provided staleness bound. Cloud Spanner chooses the newest timestamp within the staleness * bound that allows execution of the reads at the closest available replica without blocking. * *

All rows yielded are consistent with each other -- if any part of the read observes a * transaction, all parts of the read see the transaction. Boundedly stale reads are not repeatable: * two stale reads, even if they use the same staleness bound, can execute at different timestamps * and thus return inconsistent results. * *

Boundedly stale reads execute in two phases: the first phase negotiates a timestamp among all * replicas needed to serve the read. In the second phase, reads are executed at the negotiated * timestamp. * *

As a result of the two phase execution, bounded staleness reads are usually a little slower * than comparable exact staleness reads. However, they are typically able to return fresher * results, and are more likely to execute at the closest replica. * *

Because the timestamp negotiation requires up-front knowledge of which rows will be read, it * can only be used with single-use reads and single-use read-only transactions. * *

Use {@link #ofMinReadTimestamp(Timestamp)} and {@link #ofMaxStaleness(long, TimeUnit)} to * create a bound of this type. * *

Old Read Timestamps and Garbage Collection

* *

Cloud Spanner continuously garbage collects deleted and overwritten data in the background to * reclaim storage space. This process is known as "version GC". By default, version GC reclaims * versions after they are four hours old. Because of this, Cloud Spanner cannot perform reads at * read timestamps more than four hours in the past. This restriction also applies to in-progress * reads and/or SQL queries whose timestamp become too old while executing. Reads and SQL queries * with too-old read timestamps fail with the error {@link ErrorCode#FAILED_PRECONDITION}. * * @see Session#singleUse(TimestampBound) * @see Session#singleUseReadOnlyTransaction(TimestampBound) * @see Session#readOnlyTransaction(TimestampBound) */ public final class TimestampBound implements Serializable { private static final TimestampBound STRONG_BOUND = new TimestampBound(Mode.STRONG, null, null); private static final TransactionOptions.ReadOnly STRONG_PROTO = TransactionOptions.ReadOnly.newBuilder().setStrong(true).build(); private static final long serialVersionUID = 9194565742651275731L; private final Mode mode; private final Timestamp timestamp; private final Duration staleness; private TimestampBound(Mode mode, Timestamp timestamp, Duration staleness) { this.mode = mode; this.timestamp = timestamp; this.staleness = staleness; } /** * Returns a timestamp bound that will perform reads and queries at a timestamp where all * previously committed transactions are visible. */ public static TimestampBound strong() { return STRONG_BOUND; } /** * Returns a timestamp bound that will perform reads and queries at the given timestamp. Unlike * other modes, reads at a specific timestamp are repeatable; the same read at the same timestamp * always returns the same data. If the timestamp is in the future, the read will block until the * specified timestamp, modulo the read's deadline. * *

This mode is useful for large scale consistent reads such as mapreduces, or for coordinating * many reads against a consistent snapshot of the data. */ public static TimestampBound ofReadTimestamp(Timestamp timestamp) { return new TimestampBound(Mode.READ_TIMESTAMP, checkNotNull(timestamp), null); } /** * Returns a timestamp bound that will perform reads and queries at a timestamp chosen to be at * least {@code timestamp}. This is useful for requesting fresher data than some previous read, or * data that is fresh enough to observe the effects of some previously committed transaction whose * timestamp is known. * *

Note that this option can only be used in single-use reads and single-use read-only * transactions. */ public static TimestampBound ofMinReadTimestamp(Timestamp timestamp) { return new TimestampBound(Mode.MIN_READ_TIMESTAMP, checkNotNull(timestamp), null); } /** * Returns a timestamp bound that will perform reads and queries at an exact staleness. The * timestamp is chosen soon after the read is started. * *

Guarantees that all writes that have committed more than the specified number of seconds ago * are visible. Because Cloud Spanner chooses the exact timestamp, this mode works even if the * client's local clock is substantially skewed from Cloud Spanner commit timestamps. * *

Useful for reading at nearby replicas without the distributed timestamp negotiation overhead * of {@link #ofMaxStaleness(long, TimeUnit)}. */ public static TimestampBound ofExactStaleness(long num, TimeUnit units) { checkStaleness(num); return new TimestampBound(Mode.EXACT_STALENESS, null, createDuration(num, units)); } /** * Returns a timestamp bound that will perform reads and queries at a timestamp chosen to be at * most {@code num units} stale. This guarantees that all writes that have committed more than the * specified number of seconds ago are visible. Because Cloud Spanner chooses the exact timestamp, * this mode works even if the client's local clock is substantially skewed from Cloud Spanner * commit timestamps. * *

Useful for reading the freshest data available at a nearby replica, while bounding the * possible staleness if the local replica has fallen behind. * *

Note that this option can only be used in single-use reads and single-use read-only * transactions. */ public static TimestampBound ofMaxStaleness(long num, TimeUnit units) { checkStaleness(num); return new TimestampBound(Mode.MAX_STALENESS, null, createDuration(num, units)); } /** * The type of timestamp bound. See the class documentation of {@link TimestampBound} for a * detailed discussion of the various modes. */ public enum Mode { STRONG, READ_TIMESTAMP, MIN_READ_TIMESTAMP, EXACT_STALENESS, MAX_STALENESS, } public Mode getMode() { return mode; } /** * Returns the timestamp at which reads will be performed. * * @throws IllegalStateException if {@code mode() != Mode.EXACT_TIMESTAMP} * @see #ofReadTimestamp(Timestamp) */ public Timestamp getReadTimestamp() { checkMode(Mode.READ_TIMESTAMP); return timestamp; } /** * Returns the minimum timestamp at which reads will be performed. * * @throws IllegalStateException if {@code mode() != Mode.MIN_READ_TIMESTAMP} * @see #ofMinReadTimestamp(Timestamp) */ public Timestamp getMinReadTimestamp() { checkMode(Mode.MIN_READ_TIMESTAMP); return timestamp; } /** * Returns the exact staleness, in the units requested, at which reads will be performed. * * @throws IllegalStateException if {@code mode() != Mode.EXACT_STALENESS} * @see #ofExactStaleness(long, java.util.concurrent.TimeUnit) */ public long getExactStaleness(TimeUnit units) { checkMode(Mode.EXACT_STALENESS); return durationToUnits(staleness, units); } /** * Returns the maximum staleness, in the units requested, at which reads will be performed. * * @throws IllegalStateException if {@code mode() != Mode.MAX_STALENESS} * @see #ofMaxStaleness(long, java.util.concurrent.TimeUnit) */ public long getMaxStaleness(TimeUnit units) { checkMode(Mode.MAX_STALENESS); return durationToUnits(staleness, units); } StringBuilder toString(StringBuilder b) { // TODO(user): Convert all internal toString(StringBuilder) methods to return StringBuilder. switch (mode) { case STRONG: return b.append("strong"); case READ_TIMESTAMP: return b.append("exact_timestamp: ").append(timestamp.toString()); case MIN_READ_TIMESTAMP: return b.append("min_read_timestamp: ").append(timestamp.toString()); case EXACT_STALENESS: return b.append("exact_staleness: ").append(Durations.toString(staleness)); case MAX_STALENESS: return b.append("max_staleness: ").append(Durations.toString(staleness)); default: throw new AssertionError("Unexpected mode: " + mode); } } @Override public String toString() { return toString(new StringBuilder()).toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TimestampBound that = (TimestampBound) o; return mode == that.mode && Objects.equals(staleness, that.staleness) && Objects.equals(timestamp, that.timestamp); } @Override public int hashCode() { return Objects.hash(mode, timestamp, staleness); } TransactionOptions.ReadOnly toProto() { // TODO(user): Use full proto as the internal representation if we eliminate Timestamp. if (mode == Mode.STRONG) { return STRONG_PROTO; } TransactionOptions.ReadOnly.Builder builder = TransactionOptions.ReadOnly.newBuilder(); applyToBuilder(builder); return builder.build(); } TransactionOptions.ReadOnly.Builder applyToBuilder(TransactionOptions.ReadOnly.Builder builder) { switch (mode) { case STRONG: return builder.setStrong(true); case READ_TIMESTAMP: return builder.setReadTimestamp(timestamp.toProto()); case MIN_READ_TIMESTAMP: return builder.setMinReadTimestamp(timestamp.toProto()); case EXACT_STALENESS: return builder.setExactStaleness(staleness); case MAX_STALENESS: return builder.setMaxStaleness(staleness); default: throw new AssertionError("Unexpected mode: " + mode); } } private static void checkStaleness(double num) { checkArgument(num >= 0, "Staleness cannot be negative"); } private void checkMode(Mode requiredMode) { checkState(mode == requiredMode, "Invalid call for mode %s", mode); } private static Duration createDuration(long num, TimeUnit units) { switch (units) { case NANOSECONDS: return Durations.fromNanos(num); case MICROSECONDS: return Durations.fromMicros(num); default: return Durations.fromMillis(units.toMillis(num)); } } private static long durationToUnits(Duration duration, TimeUnit units) { // TODO(user): Handle overflow. switch (units) { case NANOSECONDS: return Durations.toNanos(duration); case MICROSECONDS: return Durations.toMicros(duration); default: return units.convert(Durations.toMillis(duration), TimeUnit.MILLISECONDS); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy