| /* |
| 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" |
| |
| pbd "github.com/golang/protobuf/ptypes/duration" |
| pbt "github.com/golang/protobuf/ptypes/timestamp" |
| sppb "google.golang.org/genproto/googleapis/spanner/v1" |
| ) |
| |
| // 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 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 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 fmt.Sprintf("(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 |
| } |