| /* | |
| 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 spanner | |
| import ( | |
| "fmt" | |
| "time" | |
| sppb "cloud.google.com/go/spanner/apiv1/spannerpb" | |
| pbd "google.golang.org/protobuf/types/known/durationpb" | |
| pbt "google.golang.org/protobuf/types/known/timestamppb" | |
| ) | |
| // timestampBoundType specifies the timestamp bound mode. | |
| type timestampBoundType int | |
| const ( | |
| strong timestampBoundType = iota // strong reads | |
| exactStaleness // read with exact staleness | |
| maxStaleness // read with max staleness | |
| minReadTimestamp // read with min freshness | |
| readTimestamp // read data at exact timestamp | |
| ) | |
| // TimestampBound defines how Cloud Spanner will choose a timestamp for a single | |
| // read/query or read-only transaction. | |
| // | |
| // There are three types of timestamp bound: strong, bounded staleness and exact | |
| // staleness. Strong is the default. | |
| // | |
| // 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. A TimestampBound | |
| // can be specified when creating transactions, see the documentation of | |
| // spanner.Client for an example. | |
| // | |
| // # 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 StrongRead to create a bound of this type. | |
| // | |
| // # Exact staleness | |
| // | |
| // An exact staleness timestamp bound executes 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 ReadTimestamp and ExactStaleness 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 this 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 MinReadTimestamp and MaxStaleness 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 one hour old. | |
| // Because of this, Cloud Spanner cannot perform reads at read timestamps more | |
| // than one hour in the past. This restriction also applies to in-progress reads | |
| // and/or SQL queries whose timestamps become too old while executing. Reads and | |
| // SQL queries with too-old read timestamps fail with the error | |
| // ErrorCode.FAILED_PRECONDITION. | |
| type TimestampBound struct { | |
| mode timestampBoundType | |
| d time.Duration | |
| t time.Time | |
| } | |
| // StrongRead returns a TimestampBound that will perform reads and queries at a | |
| // timestamp where all previously committed transactions are visible. | |
| func StrongRead() TimestampBound { | |
| return TimestampBound{mode: strong} | |
| } | |
| // ExactStaleness returns a TimestampBound that will perform reads and queries | |
| // at an exact staleness. | |
| func ExactStaleness(d time.Duration) TimestampBound { | |
| return TimestampBound{ | |
| mode: exactStaleness, | |
| d: d, | |
| } | |
| } | |
| // MaxStaleness returns a TimestampBound that will perform reads and queries at | |
| // a time chosen to be at most "d" stale. | |
| func MaxStaleness(d time.Duration) TimestampBound { | |
| return TimestampBound{ | |
| mode: maxStaleness, | |
| d: d, | |
| } | |
| } | |
| // MinReadTimestamp returns a TimestampBound that bound that will perform reads | |
| // and queries at a time chosen to be at least "t". | |
| func MinReadTimestamp(t time.Time) TimestampBound { | |
| return TimestampBound{ | |
| mode: minReadTimestamp, | |
| t: t, | |
| } | |
| } | |
| // ReadTimestamp returns a TimestampBound that will peform reads and queries at | |
| // the given time. | |
| func ReadTimestamp(t time.Time) TimestampBound { | |
| return TimestampBound{ | |
| mode: readTimestamp, | |
| t: t, | |
| } | |
| } | |
| func (tb TimestampBound) String() string { | |
| switch tb.mode { | |
| case strong: | |
| return "(strong)" | |
| case exactStaleness: | |
| return fmt.Sprintf("(exactStaleness: %s)", tb.d) | |
| case maxStaleness: | |
| return fmt.Sprintf("(maxStaleness: %s)", tb.d) | |
| case minReadTimestamp: | |
| return fmt.Sprintf("(minReadTimestamp: %s)", tb.t) | |
| case readTimestamp: | |
| return fmt.Sprintf("(readTimestamp: %s)", tb.t) | |
| default: | |
| return fmt.Sprintf("{mode=%v, d=%v, t=%v}", tb.mode, tb.d, tb.t) | |
| } | |
| } | |
| // durationProto takes a time.Duration and converts it into pdb.Duration for | |
| // calling gRPC APIs. | |
| func durationProto(d time.Duration) *pbd.Duration { | |
| n := d.Nanoseconds() | |
| return &pbd.Duration{ | |
| Seconds: n / int64(time.Second), | |
| Nanos: int32(n % int64(time.Second)), | |
| } | |
| } | |
| // timestampProto takes a time.Time and converts it into pbt.Timestamp for | |
| // calling gRPC APIs. | |
| func timestampProto(t time.Time) *pbt.Timestamp { | |
| return &pbt.Timestamp{ | |
| Seconds: t.Unix(), | |
| Nanos: int32(t.Nanosecond()), | |
| } | |
| } | |
| // buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a | |
| // sppb.TransactionOptions_ReadOnly transaction option, which is then used in | |
| // transactional reads. | |
| func buildTransactionOptionsReadOnly(tb TimestampBound, returnReadTimestamp bool) *sppb.TransactionOptions_ReadOnly { | |
| pb := &sppb.TransactionOptions_ReadOnly{ | |
| ReturnReadTimestamp: returnReadTimestamp, | |
| } | |
| switch tb.mode { | |
| case strong: | |
| pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_Strong{ | |
| Strong: true, | |
| } | |
| case exactStaleness: | |
| pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ExactStaleness{ | |
| ExactStaleness: durationProto(tb.d), | |
| } | |
| case maxStaleness: | |
| pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MaxStaleness{ | |
| MaxStaleness: durationProto(tb.d), | |
| } | |
| case minReadTimestamp: | |
| pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{ | |
| MinReadTimestamp: timestampProto(tb.t), | |
| } | |
| case readTimestamp: | |
| pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ReadTimestamp{ | |
| ReadTimestamp: timestampProto(tb.t), | |
| } | |
| default: | |
| panic(fmt.Sprintf("buildTransactionOptionsReadOnly(%v,%v)", tb, returnReadTimestamp)) | |
| } | |
| return pb | |
| } |