Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "google/cloud/spanner/client.h"
#include "google/cloud/spanner/database.h"
#include "google/cloud/spanner/mutations.h"
#include "google/cloud/spanner/options.h"
#include "google/cloud/spanner/testing/database_integration_test.h"
#include "google/cloud/credentials.h"
#include "google/cloud/internal/getenv.h"
Expand Down Expand Up @@ -787,6 +788,34 @@ void CheckExecuteQueryWithSingleUseOptions(
EXPECT_THAT(actual_rows, UnorderedElementsAreArray(expected_rows));
}

TEST_F(ClientIntegrationTest, ReadLockModeOptionIsSent) {
auto const singer_id = 101;
auto mutation_helper = [singer_id](std::string const& new_name) {
return Mutations{MakeInsertOrUpdateMutation(
"Singers", {"SingerId", "FirstName"}, singer_id, new_name)};
};

// Initial insert
auto insert = client_->Commit(mutation_helper("InitialName"));
ASSERT_STATUS_OK(insert);

auto read_lock_mode = Transaction::ReadLockMode::kOptimistic;
auto tx_a =
MakeReadWriteTransaction(Transaction::ReadWriteOptions(read_lock_mode));
auto tx_a_read_result = client_->Read(
tx_a, "Singers", KeySet().AddKey(MakeKey(singer_id)), {"SingerId"});
for (auto const& row : StreamOf<std::tuple<std::int64_t>>(tx_a_read_result)) {
EXPECT_STATUS_OK(row);
}
tx_a = MakeReadWriteTransaction(
tx_a, Transaction::ReadWriteOptions(read_lock_mode));

auto optimistic_result =
client_->Commit(tx_a, mutation_helper("SecondModifiedName"));

EXPECT_STATUS_OK(optimistic_result);
}

/// @test Test ExecuteQuery() with bounded staleness set by a timestamp.
TEST_F(ClientIntegrationTest, ExecuteQueryBoundedStalenessTimestamp) {
CheckExecuteQueryWithSingleUseOptions(
Expand Down
26 changes: 25 additions & 1 deletion google/cloud/spanner/transaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ google::protobuf::Duration ToProto(std::chrono::nanoseconds ns) {
return proto;
}

google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode
ProtoReadLockMode(
absl::optional<Transaction::ReadLockMode> const& read_lock_mode) {
if (!read_lock_mode) {
return google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
TransactionOptions_ReadWrite_ReadLockMode_READ_LOCK_MODE_UNSPECIFIED;
}
switch (*read_lock_mode) {
case Transaction::ReadLockMode::kOptimistic:
return google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
TransactionOptions_ReadWrite_ReadLockMode_OPTIMISTIC;
case Transaction::ReadLockMode::kPessimistic:
return google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
TransactionOptions_ReadWrite_ReadLockMode_PESSIMISTIC;
default:
return google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode::
TransactionOptions_ReadWrite_ReadLockMode_READ_LOCK_MODE_UNSPECIFIED;
}
}

google::spanner::v1::TransactionOptions MakeOpts(
google::spanner::v1::TransactionOptions_ReadOnly ro_opts) {
google::spanner::v1::TransactionOptions opts;
Expand Down Expand Up @@ -71,7 +91,11 @@ Transaction::ReadOnlyOptions::ReadOnlyOptions(
ro_opts_.set_return_read_timestamp(true);
}

Transaction::ReadWriteOptions::ReadWriteOptions() = default; // currently none
Transaction::ReadWriteOptions::ReadWriteOptions() = default;

Transaction::ReadWriteOptions::ReadWriteOptions(ReadLockMode read_lock_mode) {
rw_opts_.set_read_lock_mode(ProtoReadLockMode(read_lock_mode));
}

Transaction::ReadWriteOptions& Transaction::ReadWriteOptions::WithTag(
absl::optional<std::string> tag) {
Expand Down
16 changes: 15 additions & 1 deletion google/cloud/spanner/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,28 @@ class Transaction {
google::spanner::v1::TransactionOptions_ReadOnly ro_opts_;
};

/**
* Read lock mode for ReadWrite transactions
* The Spanner V1 Transaction proto classes have their own enum
* implementations.
* See google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode
* This is a shorthand convenience for the developer.
*/
enum class ReadLockMode {
kUnspecified,
kPessimistic,
kOptimistic,
};
Comment thread
diegomarquezp marked this conversation as resolved.

/**
* Options for ReadWrite transactions.
*/
class ReadWriteOptions {
public:
// There are currently no read-write options.
ReadWriteOptions();

explicit ReadWriteOptions(ReadLockMode read_lock_mode);

// A tag used for collecting statistics about the transaction.
ReadWriteOptions& WithTag(absl::optional<std::string> tag);

Expand Down
43 changes: 43 additions & 0 deletions google/cloud/spanner/transaction_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ TEST(TransactionOptions, Construction) {
Transaction::ReadOnlyOptions exact_dur(staleness);

Transaction::ReadWriteOptions none;
Transaction::ReadWriteOptions rw_with_read_lock(
Transaction::ReadLockMode::kOptimistic);

Transaction::SingleUseOptions su_strong(strong);
Transaction::SingleUseOptions su_exact_ts(exact_ts);
Expand Down Expand Up @@ -167,6 +169,47 @@ TEST(Transaction, MultiplexedPreviousTransactionId) {
});
}

TEST(Transaction, ReadWriteOptionsWithTag) {
auto opts = Transaction::ReadWriteOptions().WithTag("test-tag");
Transaction txn = MakeReadWriteTransaction(opts);
spanner_internal::Visit(
txn, [&](spanner_internal::SessionHolder& /*session*/,
StatusOr<google::spanner::v1::TransactionSelector>& s,
spanner_internal::TransactionContext const& ctx) {
EXPECT_TRUE(s->has_begin());
EXPECT_TRUE(s->begin().has_read_write());
EXPECT_EQ(ctx.tag, "test-tag");
return 0;
});
}

TEST(Transaction, ReadWriteOptionsWithReadLockMode) {
auto check_lock_mode =
[](Transaction::ReadLockMode mode,
google::spanner::v1::TransactionOptions_ReadWrite_ReadLockMode
expected_proto_mode) {
auto opts = Transaction::ReadWriteOptions(mode);
Transaction txn = MakeReadWriteTransaction(opts);
spanner_internal::Visit(
txn, [&](spanner_internal::SessionHolder& /*session*/,
StatusOr<google::spanner::v1::TransactionSelector>& s,
spanner_internal::TransactionContext const& /*ctx*/) {
EXPECT_TRUE(s->has_begin());
EXPECT_TRUE(s->begin().has_read_write());
EXPECT_EQ(s->begin().read_write().read_lock_mode(),
expected_proto_mode);
return 0;
});
};

check_lock_mode(
Transaction::ReadLockMode::kPessimistic,
google::spanner::v1::TransactionOptions_ReadWrite::PESSIMISTIC);
check_lock_mode(
Transaction::ReadLockMode::kOptimistic,
google::spanner::v1::TransactionOptions_ReadWrite::OPTIMISTIC);
}

} // namespace
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace spanner
Expand Down