Skip to content

Commit 295925b

Browse files
committed
Merge branch 'upstream_emulator' into sample_row_keys
2 parents 38a5a25 + 9e5efee commit 295925b

6 files changed

Lines changed: 306 additions & 17 deletions

File tree

google/cloud/bigtable/emulator/column_family.cc

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,108 @@
1313
// limitations under the License.
1414

1515
#include "google/cloud/bigtable/emulator/column_family.h"
16+
#include "google/cloud/internal/big_endian.h"
1617
#include <absl/types/optional.h>
1718
#include <chrono>
19+
#include <cstdint>
1820
#include <map>
1921

2022
namespace google {
2123
namespace cloud {
2224
namespace bigtable {
2325
namespace emulator {
2426

27+
// FIXME: Workaround our current incorrect ordering of
28+
// timestamps. Remove when that is fixed and they are in decreasing
29+
// order, at which point we can just pick the first element.
30+
std::map<std::chrono::milliseconds, std::string>::iterator latest(
31+
std::map<std::chrono::milliseconds, std::string>& cells_not_empty) {
32+
assert(!cells_not_empty.empty());
33+
34+
auto first_it = cells_not_empty.begin();
35+
auto last_it = std::prev(cells_not_empty.end());
36+
auto latest_it = first_it->first >= last_it->first ? first_it : last_it;
37+
38+
return latest_it;
39+
}
40+
41+
StatusOr<ReadModifyWriteCellResult> ColumnRow::ReadModifyWrite(
42+
std::int64_t inc_value) {
43+
auto system_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
44+
std::chrono::system_clock::now().time_since_epoch());
45+
46+
if (cells_.empty()) {
47+
std::string value = google::cloud::internal::EncodeBigEndian(inc_value);
48+
cells_[system_ms] = value;
49+
50+
return ReadModifyWriteCellResult{system_ms, std::move(value),
51+
absl::nullopt};
52+
}
53+
54+
// FIXME: Workaround our current incorrect ordering of
55+
// timestamps. Remove when that is fixed and they are in decreasing
56+
// order, at which point we can just pick the first element.
57+
auto latest_it = latest(cells_);
58+
59+
auto maybe_old_value =
60+
google::cloud::internal::DecodeBigEndian<std::int64_t>(latest_it->second);
61+
if (!maybe_old_value) {
62+
return maybe_old_value.status();
63+
}
64+
65+
auto value = google::cloud::internal::EncodeBigEndian(
66+
inc_value + maybe_old_value.value());
67+
68+
if (latest_it->first < system_ms) {
69+
// We need to add a cell with the current system timestamp
70+
cells_[system_ms] = value;
71+
72+
return ReadModifyWriteCellResult{system_ms, std::move(value),
73+
absl::nullopt};
74+
}
75+
76+
// Latest timestamp is >= system time. Overwrite latest timestamp
77+
auto old_value = std::move(latest_it->second);
78+
latest_it->second = value;
79+
80+
return ReadModifyWriteCellResult{latest_it->first, std::move(value),
81+
std::move(old_value)};
82+
}
83+
84+
ReadModifyWriteCellResult ColumnRow::ReadModifyWrite(
85+
std::string const& append_value) {
86+
auto system_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
87+
std::chrono::system_clock::now().time_since_epoch());
88+
if (cells_.empty()) {
89+
cells_[system_ms] = append_value;
90+
91+
return ReadModifyWriteCellResult{system_ms, std::move(append_value),
92+
absl::nullopt};
93+
}
94+
95+
// FIXME: Workaround our current incorrect ordering of
96+
// timestamps. Remove when that is fixed and they are in decreasing
97+
// order, at which point we can just pick the first element.
98+
auto latest_it = latest(cells_);
99+
100+
auto value = latest_it->second + append_value;
101+
102+
if (latest_it->first < system_ms) {
103+
// We need to add a cell with the current system timestamp
104+
cells_[system_ms] = value;
105+
106+
return ReadModifyWriteCellResult{system_ms, std::move(value),
107+
absl::nullopt};
108+
}
109+
110+
// Latest timestamp is >= system time. Overwrite latest timestamp
111+
auto old_value = std::move(latest_it->second);
112+
latest_it->second = value;
113+
114+
return ReadModifyWriteCellResult{latest_it->first, value,
115+
std::move(old_value)};
116+
}
117+
25118
absl::optional<std::string> ColumnRow::SetCell(
26119
std::chrono::milliseconds timestamp, std::string const& value) {
27120
absl::optional<std::string> ret = absl::nullopt;

google/cloud/bigtable/emulator/column_family.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@
1919
#include "google/cloud/bigtable/emulator/filter.h"
2020
#include "google/cloud/bigtable/emulator/filtered_map.h"
2121
#include "google/cloud/bigtable/emulator/range_set.h"
22+
#include "google/cloud/bigtable/read_modify_write_rule.h"
23+
#include "google/cloud/internal/big_endian.h"
24+
#include "google/cloud/internal/make_status.h"
2225
#include "absl/types/optional.h"
2326
#include <google/bigtable/admin/v2/table.pb.h>
2427
#include <google/bigtable/v2/data.pb.h>
2528
#include <chrono>
2629
#include <cstddef>
2730
#include <map>
31+
#include <memory>
32+
#include <optional>
33+
#include <sstream>
2834

2935
namespace google {
3036
namespace cloud {
@@ -36,6 +42,24 @@ struct Cell {
3642
std::string value;
3743
};
3844

45+
// ReadModifyWriteCellResult supports undo and return value
46+
// construction for the ReadModifyWrite RPC.
47+
//
48+
// The timestamp and value written are always returned in timestamp
49+
// and value and will be used to construct the Row returned by the
50+
// RPC.
51+
//
52+
// If maybe_old_value has a value, then a timestamp was overwritten
53+
// and the ReadModifyWriteCellResult will be used to create a
54+
// RestoreValue for undo log. Otherwise, a new cell was added and the
55+
// ReadmodifyWriteCellResult will be used to create a DeleteValue for
56+
// the undo log.
57+
struct ReadModifyWriteCellResult {
58+
std::chrono::milliseconds timestamp;
59+
std::string value;
60+
absl::optional<std::string> maybe_old_value;
61+
};
62+
3963
/**
4064
* Objects of this class hold contents of a specific column in a specific row.
4165
*
@@ -48,6 +72,10 @@ class ColumnRow {
4872
ColumnRow(ColumnRow const&) = delete;
4973
ColumnRow& operator=(ColumnRow const&) = delete;
5074

75+
StatusOr<ReadModifyWriteCellResult> ReadModifyWrite(std::int64_t inc_value);
76+
77+
ReadModifyWriteCellResult ReadModifyWrite(std::string const& append_value);
78+
5179
/**
5280
* Insert or update and existing cell at a given timestamp.
5381
*
@@ -84,8 +112,11 @@ class ColumnRow {
84112
bool HasCells() const { return !cells_.empty(); }
85113
using const_iterator =
86114
std::map<std::chrono::milliseconds, std::string>::const_iterator;
115+
using iterator = std::map<std::chrono::milliseconds, std::string>::iterator;
87116
const_iterator begin() const { return cells_.begin(); }
88117
const_iterator end() const { return cells_.end(); }
118+
iterator begin() { return cells_.begin(); }
119+
iterator end() { return cells_.end(); }
89120
const_iterator lower_bound(std::chrono::milliseconds timestamp) const {
90121
return cells_.lower_bound(timestamp);
91122
}
@@ -117,6 +148,16 @@ class ColumnRow {
117148
*/
118149
class ColumnFamilyRow {
119150
public:
151+
StatusOr<ReadModifyWriteCellResult> ReadModifyWrite(
152+
std::string const& column_qualifier, std::int64_t inc_value) {
153+
return columns_[column_qualifier].ReadModifyWrite(inc_value);
154+
};
155+
156+
ReadModifyWriteCellResult ReadModifyWrite(std::string const& column_qualifier,
157+
std::string const& append_value) {
158+
return columns_[column_qualifier].ReadModifyWrite(append_value);
159+
}
160+
120161
/**
121162
* Insert or update and existing cell at a given column and timestamp.
122163
*
@@ -204,6 +245,18 @@ class ColumnFamily {
204245
using const_iterator = std::map<std::string, ColumnFamilyRow>::const_iterator;
205246
using iterator = std::map<std::string, ColumnFamilyRow>::iterator;
206247

248+
StatusOr<ReadModifyWriteCellResult> ReadModifyWrite(
249+
std::string const& row_key, std::string const& column_qualifier,
250+
std::int64_t inc_value) {
251+
return rows_[row_key].ReadModifyWrite(column_qualifier, inc_value);
252+
};
253+
254+
ReadModifyWriteCellResult ReadModifyWrite(std::string const& row_key,
255+
std::string const& column_qualifier,
256+
std::string const& append_value) {
257+
return rows_[row_key].ReadModifyWrite(column_qualifier, append_value);
258+
};
259+
207260
/**
208261
* Insert or update and existing cell at a given row, column and timestamp.
209262
*

google/cloud/bigtable/emulator/rollback_test.cc

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ Status HasRow(std::shared_ptr<google::cloud::bigtable::emulator::Table>& table,
273273
//
274274
// Supplied with a timestamp < -1, it should return an error and fail the entire
275275
// mutation chain.
276-
TEST(TransactonRollback, ZeroOrNegativeTimestampHandling) {
276+
TEST(TransactionRollback, ZeroOrNegativeTimestampHandling) {
277277
::google::bigtable::admin::v2::Table schema;
278278
::google::bigtable::admin::v2::ColumnFamily column_family;
279279

@@ -346,12 +346,13 @@ TEST(TransactonRollback, ZeroOrNegativeTimestampHandling) {
346346
{column_family_name, column_qualifier, -1, data},
347347
};
348348
auto const* const row_key_5 = "4";
349-
auto system_time_ms_before = std::chrono::duration_cast<std::chrono::milliseconds>(
350-
std::chrono::system_clock::now().time_since_epoch());
349+
auto system_time_ms_before =
350+
std::chrono::duration_cast<std::chrono::milliseconds>(
351+
std::chrono::system_clock::now().time_since_epoch());
351352
status = SetCells(table, table_name, row_key_5, v);
352353
ASSERT_STATUS_OK(status);
353-
auto column_or = GetColumn(
354-
table, v[0].column_family_name, row_key_5, v[0].column_qualifier);
354+
auto column_or = GetColumn(table, v[0].column_family_name, row_key_5,
355+
v[0].column_qualifier);
355356
ASSERT_STATUS_OK(column_or.status());
356357
auto col = column_or.value();
357358
ASSERT_EQ(col.size(), 1);
@@ -362,7 +363,7 @@ TEST(TransactonRollback, ZeroOrNegativeTimestampHandling) {
362363
}
363364

364365
// Does the SetCell mutation work to set a cell to a specific value?
365-
TEST(TransactonRollback, SetCellBasicFunction) {
366+
TEST(TransactionRollback, SetCellBasicFunction) {
366367
::google::bigtable::admin::v2::Table schema;
367368
::google::bigtable::admin::v2::ColumnFamily column_family;
368369

@@ -395,7 +396,7 @@ TEST(TransactonRollback, SetCellBasicFunction) {
395396
// Test that an old value is correctly restored in a pre-populated
396397
// cell, when one of a set of SetCell mutations fails after the cell
397398
// had been updated with a new value.
398-
TEST(TransactonRollback, TestRestoreValue) {
399+
TEST(TransactionRollback, TestRestoreValue) {
399400
::google::bigtable::admin::v2::Table schema;
400401
::google::bigtable::admin::v2::ColumnFamily column_family;
401402

@@ -458,7 +459,7 @@ TEST(TransactonRollback, TestRestoreValue) {
458459

459460
// Test that a new cell introduced in a chain of SetCell mutations is
460461
// deleted on rollback if a subsequent mutation fails.
461-
TEST(TransactonRollback, DeleteValue) {
462+
TEST(TransactionRollback, DeleteValue) {
462463
::google::bigtable::admin::v2::Table schema;
463464
::google::bigtable::admin::v2::ColumnFamily column_family;
464465

@@ -513,7 +514,7 @@ TEST(TransactonRollback, DeleteValue) {
513514
// column family name that is not in the table schema) then the column
514515
// and any of the cells introduced is deleted in the rollback, but
515516
// that any pre-transaction-attemot data in the row is unaffected.
516-
TEST(TransactonRollback, DeleteColumn) {
517+
TEST(TransactionRollback, DeleteColumn) {
517518
::google::bigtable::admin::v2::Table schema;
518519
::google::bigtable::admin::v2::ColumnFamily column_family;
519520

@@ -562,7 +563,7 @@ TEST(TransactonRollback, DeleteColumn) {
562563
// Test that a chain of SetCell mutations that initially introduces a
563564
// new row, but one of which eventually fails, will end with the whole
564565
// row rolled back.
565-
TEST(TransactonRollback, DeleteRow) {
566+
TEST(TransactionRollback, DeleteRow) {
566567
::google::bigtable::admin::v2::Table schema;
567568
::google::bigtable::admin::v2::ColumnFamily column_family;
568569

@@ -599,7 +600,7 @@ TEST(TransactonRollback, DeleteRow) {
599600
// Does the DeleteFromfamily mutation work to delete a row from a
600601
// specific family and does it rows with the same row key in other
601602
// column families alone?
602-
TEST(TransactonRollback, DeleteFromFamilyBasicFunction) {
603+
TEST(TransactionRollback, DeleteFromFamilyBasicFunction) {
603604
::google::bigtable::admin::v2::Table schema;
604605
::google::bigtable::admin::v2::ColumnFamily column_family;
605606

@@ -648,7 +649,7 @@ TEST(TransactonRollback, DeleteFromFamilyBasicFunction) {
648649

649650
// Test that DeleteFromfamily can be rolled back in case a subsequent
650651
// mutation fails.
651-
TEST(TransactonRollback, DeleteFromFamilyRollback) {
652+
TEST(TransactionRollback, DeleteFromFamilyRollback) {
652653
::google::bigtable::admin::v2::Table schema;
653654
::google::bigtable::admin::v2::ColumnFamily column_family;
654655

@@ -715,7 +716,7 @@ ::google::bigtable::v2::TimestampRange* NewTimestampRange(int64_t start,
715716
}
716717

717718
// Does DeleteFromColumn basically work?
718-
TEST(TransactonRollback, DeleteFromColumnBasicFunction) {
719+
TEST(TransactionRollback, DeleteFromColumnBasicFunction) {
719720
::google::bigtable::admin::v2::Table schema;
720721
::google::bigtable::admin::v2::ColumnFamily column_family;
721722

@@ -757,7 +758,7 @@ TEST(TransactonRollback, DeleteFromColumnBasicFunction) {
757758
}
758759

759760
// Does DeleteFromColumn rollback work?
760-
TEST(TransactonRollback, DeleteFromColumnRollback) {
761+
TEST(TransactionRollback, DeleteFromColumnRollback) {
761762
::google::bigtable::admin::v2::Table schema;
762763
::google::bigtable::admin::v2::ColumnFamily column_family;
763764

@@ -816,7 +817,7 @@ TEST(TransactonRollback, DeleteFromColumnRollback) {
816817
}
817818

818819
// Can we delete a row from all column families?
819-
TEST(TransactonRollback, DeleteFromRowBasicFunction) {
820+
TEST(TransactionRollback, DeleteFromRowBasicFunction) {
820821
::google::bigtable::admin::v2::Table schema;
821822
::google::bigtable::admin::v2::ColumnFamily column_family;
822823

google/cloud/bigtable/emulator/server.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,20 @@ class EmulatorService final : public btproto::Bigtable::Service {
143143

144144
grpc::Status ReadModifyWriteRow(
145145
grpc::ServerContext* /* context */,
146-
btproto::ReadModifyWriteRowRequest const* /* request */,
147-
btproto::ReadModifyWriteRowResponse* /* response */) override {
146+
btproto::ReadModifyWriteRowRequest const* request,
147+
btproto::ReadModifyWriteRowResponse* response) override {
148+
auto maybe_table = cluster_->FindTable(request->table_name());
149+
if (!maybe_table) {
150+
return ToGrpcStatus(maybe_table.status());
151+
}
152+
153+
auto maybe_response = (*maybe_table)->ReadModifyWriteRow(*request);
154+
if (!maybe_response) {
155+
return ToGrpcStatus(maybe_response.status());
156+
}
157+
158+
*response = std::move(maybe_response.value());
159+
148160
return grpc::Status::OK;
149161
}
150162

0 commit comments

Comments
 (0)