1/*
2 * transaction.go
3 *
4 * This source file is part of the FoundationDB open source project
5 *
6 * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21// FoundationDB Go API
22
23package fdb
24
25/*
26 #define FDB_API_VERSION 610
27 #include <foundationdb/fdb_c.h>
28*/
29import "C"
30
31// A ReadTransaction can asynchronously read from a FoundationDB
32// database. Transaction and Snapshot both satisfy the ReadTransaction
33// interface.
34//
35// All ReadTransactions satisfy the ReadTransactor interface and may be used
36// with read-only transactional functions.
37type ReadTransaction interface {
38	Get(key KeyConvertible) FutureByteSlice
39	GetKey(sel Selectable) FutureKey
40	GetRange(r Range, options RangeOptions) RangeResult
41	GetReadVersion() FutureInt64
42	GetDatabase() Database
43	Snapshot() Snapshot
44
45	ReadTransactor
46}
47
48// Transaction is a handle to a FoundationDB transaction. Transaction is a
49// lightweight object that may be efficiently copied, and is safe for concurrent
50// use by multiple goroutines.
51//
52// In FoundationDB, a transaction is a mutable snapshot of a database. All read
53// and write operations on a transaction see and modify an otherwise-unchanging
54// version of the database and only change the underlying database if and when
55// the transaction is committed. Read operations do see the effects of previous
56// write operations on the same transaction. Committing a transaction usually
57// succeeds in the absence of conflicts.
58//
59// Transactions group operations into a unit with the properties of atomicity,
60// isolation, and durability. Transactions also provide the ability to maintain
61// an applications invariants or integrity constraints, supporting the property
62// of consistency. Together these properties are known as ACID.
63//
64// Transactions are also causally consistent: once a transaction has been
65// successfully committed, all subsequently created transactions will see the
66// modifications made by it.
67type Transaction struct {
68	*transaction
69}
70
71type transaction struct {
72	ptr *C.FDBTransaction
73	db  Database
74}
75
76// TransactionOptions is a handle with which to set options that affect a
77// Transaction object. A TransactionOptions instance should be obtained with the
78// (Transaction).Options method.
79type TransactionOptions struct {
80	transaction *transaction
81}
82
83func (opt TransactionOptions) setOpt(code int, param []byte) error {
84	return setOpt(func(p *C.uint8_t, pl C.int) C.fdb_error_t {
85		return C.fdb_transaction_set_option(opt.transaction.ptr, C.FDBTransactionOption(code), p, pl)
86	}, param)
87}
88
89func (t *transaction) destroy() {
90	C.fdb_transaction_destroy(t.ptr)
91}
92
93// GetDatabase returns a handle to the database with which this transaction is
94// interacting.
95func (t Transaction) GetDatabase() Database {
96	return t.transaction.db
97}
98
99// Transact executes the caller-provided function, passing it the Transaction
100// receiver object.
101//
102// A panic of type Error during execution of the function will be recovered and
103// returned to the caller as an error, but Transact will not retry the function
104// or commit the Transaction after the caller-provided function completes.
105//
106// By satisfying the Transactor interface, Transaction may be passed to a
107// transactional function from another transactional function, allowing
108// composition. The outermost transactional function must have been provided a
109// Database, or else the transaction will never be committed.
110//
111// See the Transactor interface for an example of using Transact with
112// Transaction and Database objects.
113func (t Transaction) Transact(f func(Transaction) (interface{}, error)) (r interface{}, e error) {
114	defer panicToError(&e)
115
116	r, e = f(t)
117	return
118}
119
120// ReadTransact executes the caller-provided function, passing it the
121// Transaction receiver object (as a ReadTransaction).
122//
123// A panic of type Error during execution of the function will be recovered and
124// returned to the caller as an error, but ReadTransact will not retry the
125// function.
126//
127// By satisfying the ReadTransactor interface, Transaction may be passed to a
128// read-only transactional function from another (possibly read-only)
129// transactional function, allowing composition.
130//
131// See the ReadTransactor interface for an example of using ReadTransact with
132// Transaction, Snapshot and Database objects.
133func (t Transaction) ReadTransact(f func(ReadTransaction) (interface{}, error)) (r interface{}, e error) {
134	defer panicToError(&e)
135
136	r, e = f(t)
137	return
138}
139
140// Cancel cancels a transaction. All pending or future uses of the transaction
141// will encounter an error. The Transaction object may be reused after calling
142// (Transaction).Reset.
143//
144// Be careful if you are using (Transaction).Reset and (Transaction).Cancel
145// concurrently with the same transaction. Since they negate each others
146// effects, a race condition between these calls will leave the transaction in
147// an unknown state.
148//
149// If your program attempts to cancel a transaction after (Transaction).Commit
150// has been called but before it returns, unpredictable behavior will
151// result. While it is guaranteed that the transaction will eventually end up in
152// a cancelled state, the commit may or may not occur. Moreover, even if the
153// call to (Transaction).Commit appears to return a transaction_cancelled
154// error, the commit may have occurred or may occur in the future. This can make
155// it more difficult to reason about the order in which transactions occur.
156func (t Transaction) Cancel() {
157	C.fdb_transaction_cancel(t.ptr)
158}
159
160// (Infrequently used) SetReadVersion sets the database version that the transaction will read from
161// the database. The database cannot guarantee causal consistency if this method
162// is used (the transactions reads will be causally consistent only if the
163// provided read version has that property).
164func (t Transaction) SetReadVersion(version int64) {
165	C.fdb_transaction_set_read_version(t.ptr, C.int64_t(version))
166}
167
168// Snapshot returns a Snapshot object, suitable for performing snapshot
169// reads. Snapshot reads offer a more relaxed isolation level than
170// FoundationDB's default serializable isolation, reducing transaction conflicts
171// but making it harder to reason about concurrency.
172//
173// For more information on snapshot reads, see
174// https://apple.github.io/foundationdb/developer-guide.html#snapshot-reads.
175func (t Transaction) Snapshot() Snapshot {
176	return Snapshot{t.transaction}
177}
178
179// OnError determines whether an error returned by a Transaction method is
180// retryable. Waiting on the returned future will return the same error when
181// fatal, or return nil (after blocking the calling goroutine for a suitable
182// delay) for retryable errors.
183//
184// Typical code will not use OnError directly. (Database).Transact uses
185// OnError internally to implement a correct retry loop.
186func (t Transaction) OnError(e Error) FutureNil {
187	return &futureNil{newFuture(C.fdb_transaction_on_error(t.ptr, C.fdb_error_t(e.Code)))}
188}
189
190// Commit attempts to commit the modifications made in the transaction to the
191// database. Waiting on the returned future will block the calling goroutine
192// until the transaction has either been committed successfully or an error is
193// encountered. Any error should be passed to (Transaction).OnError to determine
194// if the error is retryable or not.
195//
196// As with other client/server databases, in some failure scenarios a client may
197// be unable to determine whether a transaction succeeded. For more information,
198// see
199// https://apple.github.io/foundationdb/developer-guide.html#transactions-with-unknown-results.
200func (t Transaction) Commit() FutureNil {
201	return &futureNil{newFuture(C.fdb_transaction_commit(t.ptr))}
202}
203
204// Watch creates a watch and returns a FutureNil that will become ready when the
205// watch reports a change to the value of the specified key.
206//
207// A watchs behavior is relative to the transaction that created it. A watch
208// will report a change in relation to the keys value as readable by that
209// transaction. The initial value used for comparison is either that of the
210// transactions read version or the value as modified by the transaction itself
211// prior to the creation of the watch. If the value changes and then changes
212// back to its initial value, the watch might not report the change.
213//
214// Until the transaction that created it has been committed, a watch will not
215// report changes made by other transactions. In contrast, a watch will
216// immediately report changes made by the transaction itself. Watches cannot be
217// created if the transaction has called SetReadYourWritesDisable on the
218// Transaction options, and an attempt to do so will return a watches_disabled
219// error.
220//
221// If the transaction used to create a watch encounters an error during commit,
222// then the watch will be set with that error. A transaction whose commit
223// result is unknown will set all of its watches with the commit_unknown_result
224// error. If an uncommitted transaction is reset or destroyed, then any watches
225// it created will be set with the transaction_cancelled error.
226//
227// By default, each database connection can have no more than 10,000 watches
228// that have not yet reported a change. When this number is exceeded, an attempt
229// to create a watch will return a too_many_watches error. This limit can be
230// changed using SetMaxWatches on the Database. Because a watch outlives the
231// transaction that creates it, any watch that is no longer needed should be
232// cancelled by calling (FutureNil).Cancel on its returned future.
233func (t Transaction) Watch(key KeyConvertible) FutureNil {
234	kb := key.FDBKey()
235	return &futureNil{newFuture(C.fdb_transaction_watch(t.ptr, byteSliceToPtr(kb), C.int(len(kb))))}
236}
237
238func (t *transaction) get(key []byte, snapshot int) FutureByteSlice {
239	return &futureByteSlice{future: newFuture(C.fdb_transaction_get(t.ptr, byteSliceToPtr(key), C.int(len(key)), C.fdb_bool_t(snapshot)))}
240}
241
242// Get returns the (future) value associated with the specified key. The read is
243// performed asynchronously and does not block the calling goroutine. The future
244// will become ready when the read is complete.
245func (t Transaction) Get(key KeyConvertible) FutureByteSlice {
246	return t.get(key.FDBKey(), 0)
247}
248
249func (t *transaction) doGetRange(r Range, options RangeOptions, snapshot bool, iteration int) futureKeyValueArray {
250	begin, end := r.FDBRangeKeySelectors()
251	bsel := begin.FDBKeySelector()
252	esel := end.FDBKeySelector()
253	bkey := bsel.Key.FDBKey()
254	ekey := esel.Key.FDBKey()
255
256	return futureKeyValueArray{newFuture(C.fdb_transaction_get_range(t.ptr, byteSliceToPtr(bkey), C.int(len(bkey)), C.fdb_bool_t(boolToInt(bsel.OrEqual)), C.int(bsel.Offset), byteSliceToPtr(ekey), C.int(len(ekey)), C.fdb_bool_t(boolToInt(esel.OrEqual)), C.int(esel.Offset), C.int(options.Limit), C.int(0), C.FDBStreamingMode(options.Mode-1), C.int(iteration), C.fdb_bool_t(boolToInt(snapshot)), C.fdb_bool_t(boolToInt(options.Reverse))))}
257}
258
259func (t *transaction) getRange(r Range, options RangeOptions, snapshot bool) RangeResult {
260	f := t.doGetRange(r, options, snapshot, 1)
261	begin, end := r.FDBRangeKeySelectors()
262	return RangeResult{
263		t:        t,
264		sr:       SelectorRange{begin, end},
265		options:  options,
266		snapshot: snapshot,
267		f:        &f,
268	}
269}
270
271// GetRange performs a range read. The returned RangeResult represents all
272// KeyValue objects kv where beginKey <= kv.Key < endKey, ordered by kv.Key
273// (where beginKey and endKey are the keys described by the key selectors
274// returned by r.FDBKeySelectors). All reads performed as a result of GetRange
275// are asynchronous and do not block the calling goroutine.
276func (t Transaction) GetRange(r Range, options RangeOptions) RangeResult {
277	return t.getRange(r, options, false)
278}
279
280func (t *transaction) getReadVersion() FutureInt64 {
281	return &futureInt64{newFuture(C.fdb_transaction_get_read_version(t.ptr))}
282}
283
284// (Infrequently used) GetReadVersion returns the (future) transaction read version. The read is
285// performed asynchronously and does not block the calling goroutine. The future
286// will become ready when the read version is available.
287func (t Transaction) GetReadVersion() FutureInt64 {
288	return t.getReadVersion()
289}
290
291// Set associated the given key and value, overwriting any previous association
292// with key. Set returns immediately, having modified the snapshot of the
293// database represented by the transaction.
294func (t Transaction) Set(key KeyConvertible, value []byte) {
295	kb := key.FDBKey()
296	C.fdb_transaction_set(t.ptr, byteSliceToPtr(kb), C.int(len(kb)), byteSliceToPtr(value), C.int(len(value)))
297}
298
299// Clear removes the specified key (and any associated value), if it
300// exists. Clear returns immediately, having modified the snapshot of the
301// database represented by the transaction.
302func (t Transaction) Clear(key KeyConvertible) {
303	kb := key.FDBKey()
304	C.fdb_transaction_clear(t.ptr, byteSliceToPtr(kb), C.int(len(kb)))
305}
306
307// ClearRange removes all keys k such that begin <= k < end, and their
308// associated values. ClearRange returns immediately, having modified the
309// snapshot of the database represented by the transaction.
310func (t Transaction) ClearRange(er ExactRange) {
311	begin, end := er.FDBRangeKeys()
312	bkb := begin.FDBKey()
313	ekb := end.FDBKey()
314	C.fdb_transaction_clear_range(t.ptr, byteSliceToPtr(bkb), C.int(len(bkb)), byteSliceToPtr(ekb), C.int(len(ekb)))
315}
316
317// (Infrequently used) GetCommittedVersion returns the version number at which a
318// successful commit modified the database. This must be called only after the
319// successful (non-error) completion of a call to Commit on this Transaction, or
320// the behavior is undefined. Read-only transactions do not modify the database
321// when committed and will have a committed version of -1. Keep in mind that a
322// transaction which reads keys and then sets them to their current values may
323// be optimized to a read-only transaction.
324func (t Transaction) GetCommittedVersion() (int64, error) {
325	var version C.int64_t
326
327	if err := C.fdb_transaction_get_committed_version(t.ptr, &version); err != 0 {
328		return 0, Error{int(err)}
329	}
330
331	return int64(version), nil
332}
333
334// (Infrequently used) Returns a future which will contain the versionstamp
335// which was used by any versionstamp operations in this transaction. The
336// future will be ready only after the successful completion of a call to Commit
337// on this Transaction. Read-only transactions do not modify the database when
338// committed and will result in the future completing with an error. Keep in
339// mind that a transaction which reads keys and then sets them to their current
340// values may be optimized to a read-only transaction.
341func (t Transaction) GetVersionstamp() FutureKey {
342	return &futureKey{future: newFuture(C.fdb_transaction_get_versionstamp(t.ptr))}
343}
344
345// Reset rolls back a transaction, completely resetting it to its initial
346// state. This is logically equivalent to destroying the transaction and
347// creating a new one.
348func (t Transaction) Reset() {
349	C.fdb_transaction_reset(t.ptr)
350}
351
352func boolToInt(b bool) int {
353	if b {
354		return 1
355	}
356	return 0
357}
358
359func (t *transaction) getKey(sel KeySelector, snapshot int) FutureKey {
360	key := sel.Key.FDBKey()
361	return &futureKey{future: newFuture(C.fdb_transaction_get_key(t.ptr, byteSliceToPtr(key), C.int(len(key)), C.fdb_bool_t(boolToInt(sel.OrEqual)), C.int(sel.Offset), C.fdb_bool_t(snapshot)))}
362}
363
364// GetKey returns the future key referenced by the provided key selector. The
365// read is performed asynchronously and does not block the calling
366// goroutine. The future will become ready when the read version is available.
367//
368// By default, the key is cached for the duration of the transaction, providing
369// a potential performance benefit. However, the value of the key is also
370// retrieved, using network bandwidth. Invoking
371// (TransactionOptions).SetReadYourWritesDisable will avoid both the caching and
372// the increased network bandwidth.
373func (t Transaction) GetKey(sel Selectable) FutureKey {
374	return t.getKey(sel.FDBKeySelector(), 0)
375}
376
377func (t Transaction) atomicOp(key []byte, param []byte, code int) {
378	C.fdb_transaction_atomic_op(t.ptr, byteSliceToPtr(key), C.int(len(key)), byteSliceToPtr(param), C.int(len(param)), C.FDBMutationType(code))
379}
380
381func addConflictRange(t *transaction, er ExactRange, crtype conflictRangeType) error {
382	begin, end := er.FDBRangeKeys()
383	bkb := begin.FDBKey()
384	ekb := end.FDBKey()
385	if err := C.fdb_transaction_add_conflict_range(t.ptr, byteSliceToPtr(bkb), C.int(len(bkb)), byteSliceToPtr(ekb), C.int(len(ekb)), C.FDBConflictRangeType(crtype)); err != 0 {
386		return Error{int(err)}
387	}
388
389	return nil
390}
391
392// AddReadConflictRange adds a range of keys to the transactions read conflict
393// ranges as if you had read the range. As a result, other transactions that
394// write a key in this range could cause the transaction to fail with a
395// conflict.
396//
397// For more information on conflict ranges, see
398// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges.
399func (t Transaction) AddReadConflictRange(er ExactRange) error {
400	return addConflictRange(t.transaction, er, conflictRangeTypeRead)
401}
402
403func copyAndAppend(orig []byte, b byte) []byte {
404	ret := make([]byte, len(orig)+1)
405	copy(ret, orig)
406	ret[len(orig)] = b
407	return ret
408}
409
410// AddReadConflictKey adds a key to the transactions read conflict ranges as if
411// you had read the key. As a result, other transactions that concurrently write
412// this key could cause the transaction to fail with a conflict.
413//
414// For more information on conflict ranges, see
415// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges.
416func (t Transaction) AddReadConflictKey(key KeyConvertible) error {
417	return addConflictRange(t.transaction, KeyRange{key, Key(copyAndAppend(key.FDBKey(), 0x00))}, conflictRangeTypeRead)
418}
419
420// AddWriteConflictRange adds a range of keys to the transactions write
421// conflict ranges as if you had cleared the range. As a result, other
422// transactions that concurrently read a key in this range could fail with a
423// conflict.
424//
425// For more information on conflict ranges, see
426// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges.
427func (t Transaction) AddWriteConflictRange(er ExactRange) error {
428	return addConflictRange(t.transaction, er, conflictRangeTypeWrite)
429}
430
431// AddWriteConflictKey adds a key to the transactions write conflict ranges as
432// if you had written the key. As a result, other transactions that concurrently
433// read this key could fail with a conflict.
434//
435// For more information on conflict ranges, see
436// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges.
437func (t Transaction) AddWriteConflictKey(key KeyConvertible) error {
438	return addConflictRange(t.transaction, KeyRange{key, Key(copyAndAppend(key.FDBKey(), 0x00))}, conflictRangeTypeWrite)
439}
440
441// Options returns a TransactionOptions instance suitable for setting options
442// specific to this transaction.
443func (t Transaction) Options() TransactionOptions {
444	return TransactionOptions{t.transaction}
445}
446
447func localityGetAddressesForKey(t *transaction, key KeyConvertible) FutureStringSlice {
448	kb := key.FDBKey()
449	return &futureStringSlice{newFuture(C.fdb_transaction_get_addresses_for_key(t.ptr, byteSliceToPtr(kb), C.int(len(kb))))}
450}
451
452// LocalityGetAddressesForKey returns the (future) public network addresses of
453// each of the storage servers responsible for storing key and its associated
454// value. The read is performed asynchronously and does not block the calling
455// goroutine. The future will become ready when the read is complete.
456func (t Transaction) LocalityGetAddressesForKey(key KeyConvertible) FutureStringSlice {
457	return localityGetAddressesForKey(t.transaction, key)
458}
459