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 timestamp.
70// Reads at a timestamp are guaranteed to see a consistent prefix of the global
71// transaction history: they observe modifications done by all transactions with a
72// commit timestamp less than or equal to the read timestamp, and observe none of the
73// modifications done by transactions with a larger commit timestamp. They will block
74// until all conflicting transactions that may be assigned commit timestamps less
75// than or equal to the read timestamp have finished.
76//
77// The timestamp can either be expressed as an absolute Cloud Spanner commit
78// timestamp or a staleness relative to the current time.
79//
80// These modes do not require a "negotiation phase" to pick a timestamp. As a
81// result, they execute slightly faster than the equivalent boundedly stale
82// concurrency modes. On the other hand, boundedly stale reads usually return
83// fresher results.
84//
85// Use ReadTimestamp and ExactStaleness to create a bound of this type.
86//
87// Bounded staleness
88//
89// Bounded staleness modes allow Cloud Spanner to pick the read timestamp, subject to
90// a user-provided staleness bound. Cloud Spanner chooses the newest timestamp within
91// the staleness bound that allows execution of the reads at the closest
92// available replica without blocking.
93//
94// All rows yielded are consistent with each other: if any part of the read
95// observes a transaction, all parts of the read see the transaction. Boundedly
96// stale reads are not repeatable: two stale reads, even if they use the same
97// staleness bound, can execute at different timestamps and thus return
98// inconsistent results.
99//
100// Boundedly stale reads execute in two phases. The first phase negotiates a
101// timestamp among all replicas needed to serve the read. In the second phase,
102// reads are executed at the negotiated timestamp.
103//
104// As a result of this two-phase execution, bounded staleness reads are usually
105// a little slower than comparable exact staleness reads. However, they are
106// typically able to return fresher results, and are more likely to execute at
107// the closest replica.
108//
109// Because the timestamp negotiation requires up-front knowledge of which rows
110// will be read, it can only be used with single-use reads and single-use
111// read-only transactions.
112//
113// Use MinReadTimestamp and MaxStaleness to create a bound of this type.
114//
115// Old read timestamps and garbage collection
116//
117// Cloud Spanner continuously garbage collects deleted and overwritten data in the
118// background to reclaim storage space. This process is known as "version
119// GC". By default, version GC reclaims versions after they are four hours
120// old. Because of this, Cloud Spanner cannot perform reads at read timestamps more
121// than four hours in the past. This restriction also applies to in-progress
122// reads and/or SQL queries whose timestamps become too old while
123// executing. Reads and SQL queries with too-old read timestamps fail with the
124// error ErrorCode.FAILED_PRECONDITION.
125type TimestampBound struct {
126	mode timestampBoundType
127	d    time.Duration
128	t    time.Time
129}
130
131// StrongRead returns a TimestampBound that will perform reads and queries at a
132// timestamp where all previously committed transactions are visible.
133func StrongRead() TimestampBound {
134	return TimestampBound{mode: strong}
135}
136
137// ExactStaleness returns a TimestampBound that will perform reads and queries
138// at an exact staleness.
139func ExactStaleness(d time.Duration) TimestampBound {
140	return TimestampBound{
141		mode: exactStaleness,
142		d:    d,
143	}
144}
145
146// MaxStaleness returns a TimestampBound that will perform reads and queries at
147// a time chosen to be at most "d" stale.
148func MaxStaleness(d time.Duration) TimestampBound {
149	return TimestampBound{
150		mode: maxStaleness,
151		d:    d,
152	}
153}
154
155// MinReadTimestamp returns a TimestampBound that bound that will perform reads
156// and queries at a time chosen to be at least "t".
157func MinReadTimestamp(t time.Time) TimestampBound {
158	return TimestampBound{
159		mode: minReadTimestamp,
160		t:    t,
161	}
162}
163
164// ReadTimestamp returns a TimestampBound that will peform reads and queries at
165// the given time.
166func ReadTimestamp(t time.Time) TimestampBound {
167	return TimestampBound{
168		mode: readTimestamp,
169		t:    t,
170	}
171}
172
173func (tb TimestampBound) String() string {
174	switch tb.mode {
175	case strong:
176		return fmt.Sprintf("(strong)")
177	case exactStaleness:
178		return fmt.Sprintf("(exactStaleness: %s)", tb.d)
179	case maxStaleness:
180		return fmt.Sprintf("(maxStaleness: %s)", tb.d)
181	case minReadTimestamp:
182		return fmt.Sprintf("(minReadTimestamp: %s)", tb.t)
183	case readTimestamp:
184		return fmt.Sprintf("(readTimestamp: %s)", tb.t)
185	default:
186		return fmt.Sprintf("{mode=%v, d=%v, t=%v}", tb.mode, tb.d, tb.t)
187	}
188}
189
190// durationProto takes a time.Duration and converts it into pdb.Duration for
191// calling gRPC APIs.
192func durationProto(d time.Duration) *pbd.Duration {
193	n := d.Nanoseconds()
194	return &pbd.Duration{
195		Seconds: n / int64(time.Second),
196		Nanos:   int32(n % int64(time.Second)),
197	}
198}
199
200// timestampProto takes a time.Time and converts it into pbt.Timestamp for calling
201// gRPC APIs.
202func timestampProto(t time.Time) *pbt.Timestamp {
203	return &pbt.Timestamp{
204		Seconds: t.Unix(),
205		Nanos:   int32(t.Nanosecond()),
206	}
207}
208
209// buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a sppb.TransactionOptions_ReadOnly
210// transaction option, which is then used in transactional reads.
211func buildTransactionOptionsReadOnly(tb TimestampBound, returnReadTimestamp bool) *sppb.TransactionOptions_ReadOnly {
212	pb := &sppb.TransactionOptions_ReadOnly{
213		ReturnReadTimestamp: returnReadTimestamp,
214	}
215	switch tb.mode {
216	case strong:
217		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_Strong{
218			Strong: true,
219		}
220	case exactStaleness:
221		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ExactStaleness{
222			ExactStaleness: durationProto(tb.d),
223		}
224	case maxStaleness:
225		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MaxStaleness{
226			MaxStaleness: durationProto(tb.d),
227		}
228	case minReadTimestamp:
229		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{
230			MinReadTimestamp: timestampProto(tb.t),
231		}
232	case readTimestamp:
233		pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ReadTimestamp{
234			ReadTimestamp: timestampProto(tb.t),
235		}
236	default:
237		panic(fmt.Sprintf("buildTransactionOptionsReadOnly(%v,%v)", tb, returnReadTimestamp))
238	}
239	return pb
240}
241