diff --git a/google/cloud/spanner/doc/spanner-main.dox b/google/cloud/spanner/doc/spanner-main.dox index fa936026d09a7..a6062481265cd 100644 --- a/google/cloud/spanner/doc/spanner-main.dox +++ b/google/cloud/spanner/doc/spanner-main.dox @@ -34,6 +34,7 @@ into your project. - @ref spanner-auth-example - @ref spanner-universe-domain-example - @ref spanner-mocking +- @ref spanner-isolation-level-example - The [Setting up your development environment] guide describes how to set up a C++ development environment in various platforms, including the Google Cloud C++ client libraries. @@ -65,6 +66,19 @@ Follow these links to find examples for other `*Client` classes: */ +/** +@page spanner-isolation-level-example Support Isolation Level + +Cloud Spanner supports different isolation levels for read-write transactions. +You can specify a default isolation level at the client-level, or override it +at the transaction-level. + +The following example shows how to set the isolation level: + +@snippet samples.cc spanner_isolation_level + +*/ + /** @page spanner-auth-example Override the authentication configuration diff --git a/google/cloud/spanner/samples/samples.cc b/google/cloud/spanner/samples/samples.cc index 1b677c184dda1..4b6bedc77cf53 100644 --- a/google/cloud/spanner/samples/samples.cc +++ b/google/cloud/spanner/samples/samples.cc @@ -22,6 +22,7 @@ #include "google/cloud/spanner/backoff_policy.h" #include "google/cloud/spanner/backup.h" #include "google/cloud/spanner/create_instance_request_builder.h" +#include "google/cloud/spanner/options.h" #include "google/cloud/spanner/row.h" #include "google/cloud/spanner/testing/debug_log.h" // TODO(#4758): remove #include "google/cloud/spanner/testing/instance_location.h" @@ -3472,6 +3473,63 @@ void ReadWriteTransaction(google::cloud::spanner::Client client) { } //! [END spanner_read_write_transaction] +//! [START spanner_isolation_level] [spanner_isolation_level] +void IsolationLevelSetting(std::string const& project_id, + std::string const& instance_id, + std::string const& database_id) { + namespace spanner = ::google::cloud::spanner; + using ::google::cloud::Options; + using ::google::cloud::StatusOr; + + auto db = spanner::Database(project_id, instance_id, database_id); + + // The isolation level specified at the client-level will be applied + // to all RW transactions. + auto options = Options{}.set( + spanner::Transaction::IsolationLevel::kSerializable); + auto client = spanner::Client(spanner::MakeConnection(db, options)); + + auto commit = client.Commit( + [&client](spanner::Transaction const& txn) -> StatusOr { + // Read an AlbumTitle. + auto sql = spanner::SqlStatement( + "SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1"); + auto rows = client.ExecuteQuery(txn, std::move(sql)); + for (auto const& row : spanner::StreamOf>(rows)) { + if (!row) return row.status(); + std::cout << "Current album title: " << std::get<0>(*row) << "\n"; + } + + // Update the title. + auto update_sql = spanner::SqlStatement( + "UPDATE Albums " + "SET AlbumTitle = 'New Album Title' " + "WHERE SingerId = 1 and AlbumId = 1"); + auto result = client.ExecuteDml(txn, std::move(update_sql)); + if (!result) return result.status(); + std::cout << result->RowsModified() << " record updated.\n"; + + return spanner::Mutations{}; + }, + // The isolation level specified at the transaction-level takes + // precedence over the isolation level configured at the client-level. + // kRepeatableRead is used here to demonstrate overriding the client-level setting. + Options{}.set( + spanner::Transaction::IsolationLevel::kRepeatableRead)); + + if (!commit) throw std::move(commit).status(); + std::cout << "Update was successful [spanner_isolation_level]\n"; +} +//! [END spanner_isolation_level] [spanner_isolation_level] + +void IsolationLevelSettingCommand(std::vector argv) { + if (argv.size() != 3) { + throw std::runtime_error( + "isolation-level-setting "); + } + IsolationLevelSetting(argv[0], argv[1], argv[2]); +} + //! [START spanner_get_commit_stats] void GetCommitStatistics(google::cloud::spanner::Client client) { namespace spanner = ::google::cloud::spanner; @@ -5190,6 +5248,7 @@ int RunOneCommand(std::vector argv) { make_command_entry("read-data-with-storing-index", ReadDataWithStoringIndex), make_command_entry("read-write-transaction", ReadWriteTransaction), + {"isolation-level-setting", IsolationLevelSettingCommand}, make_command_entry("get-commit-stats", GetCommitStatistics), make_command_entry("dml-standard-insert", DmlStandardInsert), make_command_entry("dml-standard-update", DmlStandardUpdate), diff --git a/google/cloud/spanner/transaction.h b/google/cloud/spanner/transaction.h index c208d1bc0360a..6bb76fe700ddc 100644 --- a/google/cloud/spanner/transaction.h +++ b/google/cloud/spanner/transaction.h @@ -83,7 +83,7 @@ class Transaction { * absence of conflicts between its updates and any concurrent updates * that have occurred since that snapshot. Consequently, in contrast to * `kSerializable` transactions, only write-write conflicts are detected in - * snapshot transactions. + * repeatable read transactions. */ kRepeatableRead, }; diff --git a/google/cloud/spanner/transaction_test.cc b/google/cloud/spanner/transaction_test.cc index 1da79cf35b3de..32fe36539c7a5 100644 --- a/google/cloud/spanner/transaction_test.cc +++ b/google/cloud/spanner/transaction_test.cc @@ -188,7 +188,7 @@ TEST(Transaction, IsolationLevelPrecedence) { return 0; }); - // Case 2: Fallback to client default + // Case 2: Fallback to default options auto opts_default = Transaction::ReadWriteOptions(); Transaction txn_default = MakeReadWriteTransaction(opts_default); spanner_internal::Visit( @@ -202,7 +202,7 @@ TEST(Transaction, IsolationLevelPrecedence) { } TEST(Transaction, IsolationLevelNotSpecified) { - // Case: Isolation not specified in transaction level or client level + // Case: Isolation not specified in transaction options or default options auto opts = Transaction::ReadWriteOptions(); Transaction txn = MakeReadWriteTransaction(opts); spanner_internal::Visit(