Skip to content

Commit f65db3f

Browse files
committed
feat(spanner): set read lock mode at client level
The read lock mode can be set at the client level, which is then set as the read lock mode in all RW transactions. However, the read lock mode can be overridden at a transaction level by explicitly specifying the read lock mode in the transaction options. Also adds a sample for setting the read lock mode at the client level and overriding it at the transaction level.
1 parent fc9f8f0 commit f65db3f

4 files changed

Lines changed: 135 additions & 0 deletions

File tree

google/cloud/spanner/options.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,15 @@ struct TransactionIsolationLevelOption {
430430
using Type = spanner::Transaction::IsolationLevel;
431431
};
432432

433+
/**
434+
* Option for `google::cloud::Options` to set the transaction read lock mode.
435+
*
436+
* @ingroup google-cloud-spanner-options
437+
*/
438+
struct TransactionReadLockModeOption {
439+
using Type = spanner::Transaction::ReadLockMode;
440+
};
441+
433442
/**
434443
* Option for `google::cloud::Options` to return additional statistics
435444
* about the committed transaction in a `spanner::CommitResult`.

google/cloud/spanner/samples/samples.cc

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,6 +3539,71 @@ void IsolationLevelSettingCommand(std::vector<std::string> argv) {
35393539
IsolationLevelSetting(argv[0], argv[1], argv[2]);
35403540
}
35413541

3542+
//! [START spanner_read_lock_mode]
3543+
void ReadLockModeSetting(std::string const& project_id,
3544+
std::string const& instance_id,
3545+
std::string const& database_id) {
3546+
namespace spanner = ::google::cloud::spanner;
3547+
using ::google::cloud::Options;
3548+
using ::google::cloud::StatusOr;
3549+
3550+
auto db = spanner::Database(project_id, instance_id, database_id);
3551+
3552+
// The read lock mode specified at the client-level will be applied
3553+
// to all RW transactions.
3554+
auto options = Options{}.set<spanner::TransactionReadLockModeOption>(
3555+
spanner::Transaction::ReadLockMode::kOptimistic);
3556+
auto client = spanner::Client(spanner::MakeConnection(db, options));
3557+
3558+
auto commit = client.Commit(
3559+
[&client](
3560+
spanner::Transaction const& txn) -> StatusOr<spanner::Mutations> {
3561+
// Read an AlbumTitle.
3562+
auto sql = spanner::SqlStatement(
3563+
"SELECT AlbumTitle from Albums WHERE SingerId = @SingerId and "
3564+
"AlbumId = @AlbumId",
3565+
{{"SingerId", spanner::Value(2)}, {"AlbumId", spanner::Value(1)}});
3566+
auto rows = client.ExecuteQuery(txn, std::move(sql));
3567+
for (auto const& row :
3568+
spanner::StreamOf<std::tuple<std::string>>(rows)) {
3569+
if (!row) return row.status();
3570+
std::cout << "Current Album Title: " << std::get<0>(*row) << "\n";
3571+
}
3572+
3573+
// Update the title.
3574+
auto update_sql = spanner::SqlStatement(
3575+
"UPDATE Albums "
3576+
"SET AlbumTitle = @AlbumTitle "
3577+
"WHERE SingerId = @SingerId and AlbumId = @AlbumId",
3578+
{{"AlbumTitle", spanner::Value("A New Title")},
3579+
{"SingerId", spanner::Value(2)},
3580+
{"AlbumId", spanner::Value(1)}});
3581+
auto result = client.ExecuteDml(txn, std::move(update_sql));
3582+
if (!result) return result.status();
3583+
std::cout << result->RowsModified() << " record(s) updated.\n";
3584+
3585+
return spanner::Mutations{};
3586+
},
3587+
// The read lock mode specified at the transaction-level takes
3588+
// precedence over the read lock mode configured at the client-level.
3589+
// kPessimistic is used here to demonstrate overriding the client-level
3590+
// setting.
3591+
Options{}.set<spanner::TransactionReadLockModeOption>(
3592+
spanner::Transaction::ReadLockMode::kPessimistic));
3593+
3594+
if (!commit) throw std::move(commit).status();
3595+
std::cout << "Update was successful [spanner_read_lock_mode]\n";
3596+
}
3597+
//! [END spanner_read_lock_mode]
3598+
3599+
void ReadLockModeSettingCommand(std::vector<std::string> argv) {
3600+
if (argv.size() != 3) {
3601+
throw std::runtime_error(
3602+
"read-lock-mode-setting <project-id> <instance-id> <database-id>");
3603+
}
3604+
ReadLockModeSetting(argv[0], argv[1], argv[2]);
3605+
}
3606+
35423607
//! [START spanner_get_commit_stats]
35433608
void GetCommitStatistics(google::cloud::spanner::Client client) {
35443609
namespace spanner = ::google::cloud::spanner;
@@ -5258,6 +5323,7 @@ int RunOneCommand(std::vector<std::string> argv) {
52585323
ReadDataWithStoringIndex),
52595324
make_command_entry("read-write-transaction", ReadWriteTransaction),
52605325
{"isolation-level-setting", IsolationLevelSettingCommand},
5326+
{"read-lock-mode-setting", ReadLockModeSettingCommand},
52615327
make_command_entry("get-commit-stats", GetCommitStatistics),
52625328
make_command_entry("dml-standard-insert", DmlStandardInsert),
52635329
make_command_entry("dml-standard-update", DmlStandardUpdate),

google/cloud/spanner/transaction.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ google::spanner::v1::TransactionOptions MakeOpts(
9393
ProtoIsolationLevel(current.get<TransactionIsolationLevelOption>()));
9494
}
9595

96+
if (opts.read_write().read_lock_mode() ==
97+
google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
98+
TransactionOptions_ReadWrite_ReadLockMode_READ_LOCK_MODE_UNSPECIFIED &&
99+
current.has<TransactionReadLockModeOption>()) {
100+
opts.mutable_read_write()->set_read_lock_mode(
101+
ProtoReadLockMode(current.get<TransactionReadLockModeOption>()));
102+
}
103+
96104
return opts;
97105
}
98106

google/cloud/spanner/transaction_test.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,58 @@ TEST(Transaction, IsolationLevelNotSpecified) {
217217
});
218218
}
219219

220+
TEST(Transaction, ReadLockModePrecedence) {
221+
internal::OptionsSpan span(Options{}.set<TransactionReadLockModeOption>(
222+
Transaction::ReadLockMode::kOptimistic));
223+
224+
// Case 1: Per-call overrides default options
225+
auto opts =
226+
Transaction::ReadWriteOptions(Transaction::ReadLockMode::kPessimistic);
227+
Transaction txn = MakeReadWriteTransaction(opts);
228+
spanner_internal::Visit(
229+
txn, [](spanner_internal::SessionHolder&,
230+
StatusOr<google::spanner::v1::TransactionSelector>& s,
231+
spanner_internal::TransactionContext const&) {
232+
EXPECT_EQ(
233+
s->begin().read_write().read_lock_mode(),
234+
google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
235+
TransactionOptions_ReadWrite_ReadLockMode_PESSIMISTIC);
236+
return 0;
237+
});
238+
239+
// Case 2: Fallback to default options
240+
auto opts_default = Transaction::ReadWriteOptions();
241+
Transaction txn_default = MakeReadWriteTransaction(opts_default);
242+
spanner_internal::Visit(
243+
txn_default, [](spanner_internal::SessionHolder&,
244+
StatusOr<google::spanner::v1::TransactionSelector>& s,
245+
spanner_internal::TransactionContext const&) {
246+
EXPECT_EQ(
247+
s->begin().read_write().read_lock_mode(),
248+
google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
249+
TransactionOptions_ReadWrite_ReadLockMode_OPTIMISTIC);
250+
return 0;
251+
});
252+
}
253+
254+
TEST(Transaction, ReadLockModeNotSpecified) {
255+
// Case: Read lock mode not specified in transaction options or default
256+
// options
257+
auto opts = Transaction::ReadWriteOptions();
258+
Transaction txn = MakeReadWriteTransaction(opts);
259+
spanner_internal::Visit(txn, [](spanner_internal::SessionHolder&,
260+
StatusOr<
261+
google::spanner::v1::TransactionSelector>&
262+
s,
263+
spanner_internal::TransactionContext const&) {
264+
EXPECT_EQ(
265+
s->begin().read_write().read_lock_mode(),
266+
google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
267+
TransactionOptions_ReadWrite_ReadLockMode_READ_LOCK_MODE_UNSPECIFIED);
268+
return 0;
269+
});
270+
}
271+
220272
TEST(Transaction, ReadWriteOptionsWithTag) {
221273
auto opts = Transaction::ReadWriteOptions().WithTag("test-tag");
222274
Transaction txn = MakeReadWriteTransaction(opts);

0 commit comments

Comments
 (0)