1/*
2Copyright 2017 Google LLC
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package spanner
18
19import (
20	"fmt"
21	"time"
22
23	pbd "github.com/golang/protobuf/ptypes/duration"
24	pbt "github.com/golang/protobuf/ptypes/timestamp"
25	sppb "google.golang.org/genproto/googleapis/spanner/v1"
26)
27
28// timestampBoundType specifies the timestamp bound mode.
29type timestampBoundType int
30
31const (
32	strong           timestampBoundType = iota // strong reads
33	exactStaleness                             // read with exact staleness
34	maxStaleness                               // read with max staleness
35	minReadTimestamp                           // read with min freshness
36	readTimestamp                              // read data at exact timestamp
37)
38
39// TimestampBound defines how Cloud Spanner will choose a timestamp for a single
40// read/query or read-only transaction.
41//
42// There are three types of timestamp bound: strong, bounded staleness and exact
43// staleness. Strong is the default.
44//
45// If the Cloud Spanner database to be read is geographically distributed, stale
46// read-only transactions can execute more quickly than strong or read-write
47// transactions, because they are able to execute far from the leader replica.
48//
49// Each type of timestamp bound is discussed in detail below.  A TimestampBound
50// can be specified when creating transactions, see the documentation of
51// spanner.Client for an example.
52//
53// Strong reads
54//
55// Strong reads are guaranteed to see the effects of all transactions that have
56// committed before the start of the read. Furthermore, all rows yielded by a
57// single read are consistent with each other: if any part of the read
58// observes a transaction, all parts of the read see the transaction.
59//
60// Strong reads are not repeatable: two consecutive strong read-only
61// transactions might return inconsistent results if there are concurrent
62// writes. If consistency across reads is required, the reads should be
63// executed within a transaction or at an exact read timestamp.
64//
65// Use StrongRead to create a bound of this type.
66//
67// Exact staleness
68//
69// An exact staleness timestamp bound executes reads at a user-specified
70// timestamp. Reads at a timestamp are guaranteed to see a consistent prefix of
71// the global transaction history: they observe modifications done by all
72// transactions with a commit timestamp less than or equal to the read
73// timestamp, and observe none of the modifications done by transactions with a
74// larger commit timestamp. They will block until all conflicting transactions
75// that may be assigned commit timestamps less than or equal to the read
76// timestamp have finished.
77//
78// The timestamp can either be expressed as an absolute Cloud Spanner commit
79// timestamp or a staleness relative to the current time.
80//
81// These modes do not require a "negotiation phase" to pick a timestamp. As a
82// result, they execute slightly faster than the equivalent boundedly stale
83// concurrency modes. On the other hand, boundedly stale reads usually return
84// fresher results.
85//
86// Use ReadTimestamp and ExactStaleness to create a bound of this type.
87//
88// Bounded staleness
89//
90// Bounded staleness modes allow Cloud Spanner to pick the read timestamp,
91// subject to a user-provided staleness bound. Cloud Spanner chooses the newest
92// timestamp within the staleness bound that allows execution of the reads at
93// the closest available replica without blocking.
94//
95// All rows yielded are consistent with each other: if any part of the read
96// observes a transaction, all parts of the read see the transaction. Boundedly
97// stale reads are not repeatable: two stale reads, even if they use the same
98// staleness bound, can execute at different timestamps and thus return
99// inconsistent results.
100//
101// Boundedly stale reads execute in two phases. The first phase negotiates a
102// timestamp among all replicas needed to serve the read. In the second phase,
103// reads are executed at the negotiated timestamp.
104//
105// As a result of this two-phase execution, bounded staleness reads are usually
106// a little slower than comparable exact staleness reads. However, they are
107// typically able to return fresher results, and are more likely to execute at
108// the closest replica.
109//
110// Because the timestamp negotiation requires up-front knowledge of which rows
111// will be read, it can only be used with single-use reads and single-use
112// read-only transactions.
113//
114// Use MinReadTimestamp and MaxStaleness to create a bound of this type.
115//
116// Old read timestamps and garbage collection
117//
118// Cloud Spanner continuously garbage collects deleted and overwritten data in
119// the background to reclaim storage space. This process is known as "version
120// GC". By default, version GC reclaims versions after they are one hour old.
121// Because of this, Cloud Spanner cannot perform reads at read timestamps more
122// than one hour in the past. This restriction also applies to in-progress reads
123// and/or SQL queries whose timestamps become too old while executing. Reads and
124// SQL queries with too-old read timestamps fail with the error
125// ErrorCode.FAILED_PRECONDITION.
126type TimestampBound struct {
127	mode timestampBoundType
128	d    time.Duration
129	t    time.Time
130}
131
132// StrongRead returns a TimestampBound that will perform reads and queries at a
133// timestamp where all previously committed transactions are visible.
134func StrongRead() TimestampBound {
135	return TimestampBound{mode: strong}
136}
137
138// ExactStaleness returns a TimestampBound that will perform reads and queries
139// at an exact staleness.
140func ExactStaleness(d time.Duration) TimestampBound {
141	return TimestampBound{
142		mode: exactStaleness,
143		d:    d,
144	}
145}
146
147// MaxStaleness returns a TimestampBound that will perform reads and queries at
148// a time chosen to be at most "d" stale.
149func MaxStaleness(d time.Duration) TimestampBound {
150	return TimestampBound{
151		mode: maxStaleness,
152		d:    d,
153	}
154}
155
156// MinReadTimestamp returns a TimestampBound that bound that will perform reads
157// and queries at a time chosen to be at least "t".
158func MinReadTimestamp(t time.Time) TimestampBound {
159	return TimestampBound{
160		mode: minReadTimestamp,
161		t:    t,
162	}
163}
164
165// ReadTimestamp returns a TimestampBound that will peform reads and queries at
166// the given time.
167func ReadTimestamp(t time.Time) TimestampBound {
168	return TimestampBound{
169		mode: readTimestamp,
170		t:    t,
171	}
172}
173
174func (tb TimestampBound) String() string {
175	switch tb.mode {
176	case strong:
177		return fmt.Sprintf("(strong)")
178	case exactStaleness:
179		return fmt.Sprintf("(exactStaleness: %s)", tb.d)
180	case maxStaleness:
181		return fmt.Sprintf("(maxStaleness: %s)", tb.d)
182	case minReadTimestamp:
183		return fmt.Sprintf("(minReadTimestamp: %s)", tb.t)
184	case readTimestamp:
185		return fmt.Sprintf("(readTimestamp: %s)", tb.t)
186	default:
187		return fmt.Sprintf("{mode=%v, d=%v, t=%v}", tb.mode, tb.d, tb.t)
188	}
189}
190
191// durationProto takes a time.Duration and converts it into pdb.Duration for
192// calling gRPC APIs.
193func durationProto(d time.Duration) *pbd.Duration {
194	n := d.Nanoseconds()
195	return &pbd.Duration{
196		Seconds: n / int64(time.Second),
197		Nanos:   int32(n % int64(time.Second)),
198	}
199}
200
201// timestampProto takes a time.Time and converts it into pbt.Timestamp for
202// calling gRPC APIs.
203func timestampProto(t time.Time) *pbt.Timestamp {
204	return &pbt.Timestamp{
205		Seconds: t.Unix(),
206		Nanos:   int32(t.Nanosecond()),
207	}
208}
209
210// buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a
211// sppb.TransactionOptions_ReadOnly transaction option, which is then used in
212// transactional reads.
213func buildTransactionOptionsReadOnly(tb TimestampBound, returnReadTimestamp bool) *sppb.TransactionOptions_ReadOnly {
214	pb := &sppb.TransactionOptions_ReadOnly{
215		ReturnReadTimestamp: returnReadTimestamp,
216	}
217	switch tb.mode {
218	case strong:
219		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_Strong{
220			Strong: true,
221		}
222	case exactStaleness:
223		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ExactStaleness{
224			ExactStaleness: durationProto(tb.d),
225		}
226	case maxStaleness:
227		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MaxStaleness{
228			MaxStaleness: durationProto(tb.d),
229		}
230	case minReadTimestamp:
231		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{
232			MinReadTimestamp: timestampProto(tb.t),
233		}
234	case readTimestamp:
235		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ReadTimestamp{
236			ReadTimestamp: timestampProto(tb.t),
237		}
238	default:
239		panic(fmt.Sprintf("buildTransactionOptionsReadOnly(%v,%v)", tb, returnReadTimestamp))
240	}
241	return pb
242}
243