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