1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "google/cloud/bigtable/testing/table_integration_test.h"
16 #include "google/cloud/testing_util/assert_ok.h"
17 #include "google/cloud/testing_util/chrono_literals.h"
18
19 namespace {
20 namespace bigtable = google::cloud::bigtable;
21 using ::google::cloud::testing_util::chrono_literals::operator"" _ms;
22 using ::std::chrono::duration_cast;
23 using ::std::chrono::microseconds;
24 using ::std::chrono::milliseconds;
25
26 using DataIntegrationTest = bigtable::testing::TableIntegrationTest;
27
28 /// Use Table::Apply() to insert a single row.
Apply(bigtable::Table & table,std::string const & row_key,std::vector<bigtable::Cell> const & cells)29 void Apply(bigtable::Table& table, std::string const& row_key,
30 std::vector<bigtable::Cell> const& cells) {
31 auto mutation = bigtable::SingleRowMutation(row_key);
32 for (auto const& cell : cells) {
33 mutation.emplace_back(bigtable::SetCell(
34 cell.family_name(), cell.column_qualifier(),
35 duration_cast<milliseconds>(microseconds(cell.timestamp())),
36 cell.value()));
37 }
38 auto status = table.Apply(std::move(mutation));
39 ASSERT_STATUS_OK(status);
40 }
41
42 /// Use Table::BulkApply() to insert multiple rows.
BulkApply(bigtable::Table & table,std::vector<bigtable::Cell> const & cells)43 void BulkApply(bigtable::Table& table,
44 std::vector<bigtable::Cell> const& cells) {
45 std::map<bigtable::RowKeyType, bigtable::SingleRowMutation> mutations;
46 for (auto const& cell : cells) {
47 auto key = cell.row_key();
48 auto inserted = mutations.emplace(key, bigtable::SingleRowMutation(key));
49 inserted.first->second.emplace_back(bigtable::SetCell(
50 cell.family_name(), cell.column_qualifier(),
51 duration_cast<milliseconds>(microseconds(cell.timestamp())),
52 cell.value()));
53 }
54 bigtable::BulkMutation bulk;
55 for (auto& kv : mutations) {
56 bulk.emplace_back(std::move(kv.second));
57 }
58 auto failures = table.BulkApply(std::move(bulk));
59 ASSERT_TRUE(failures.empty());
60 }
61
62 /// The column families used in this test.
63 std::string const kFamily1 = "family1";
64 std::string const kFamily2 = "family2";
65 std::string const kFamily3 = "family3";
66 std::string const kFamily4 = "family4";
67
TEST_F(DataIntegrationTest,TableApply)68 TEST_F(DataIntegrationTest, TableApply) {
69 auto table = GetTable();
70
71 std::string const row_key = "row-key-1";
72 std::vector<bigtable::Cell> created{{row_key, kFamily4, "c0", 1000, "v1000"},
73 {row_key, kFamily4, "c1", 2000, "v2000"}};
74 Apply(table, row_key, created);
75 std::vector<bigtable::Cell> expected{
76 {row_key, kFamily4, "c0", 1000, "v1000"},
77 {row_key, kFamily4, "c1", 2000, "v2000"}};
78
79 auto actual = ReadRows(table, bigtable::Filter::PassAllFilter());
80 CheckEqualUnordered(expected, actual);
81 }
82
TEST_F(DataIntegrationTest,TableBulkApply)83 TEST_F(DataIntegrationTest, TableBulkApply) {
84 auto table = GetTable();
85
86 std::vector<bigtable::Cell> created{
87 {"row-key-1", kFamily4, "c0", 1000, "v1000"},
88 {"row-key-1", kFamily4, "c1", 2000, "v2000"},
89 {"row-key-2", kFamily4, "c0", 1000, "v1000"},
90 {"row-key-2", kFamily4, "c1", 2000, "v2000"},
91 {"row-key-3", kFamily4, "c0", 1000, "v1000"},
92 {"row-key-3", kFamily4, "c1", 2000, "v2000"},
93 {"row-key-4", kFamily4, "c0", 1000, "v1000"},
94 {"row-key-4", kFamily4, "c1", 2000, "v2000"}};
95 BulkApply(table, created);
96 std::vector<bigtable::Cell> expected{
97 {"row-key-1", kFamily4, "c0", 1000, "v1000"},
98 {"row-key-1", kFamily4, "c1", 2000, "v2000"},
99 {"row-key-2", kFamily4, "c0", 1000, "v1000"},
100 {"row-key-2", kFamily4, "c1", 2000, "v2000"},
101 {"row-key-3", kFamily4, "c0", 1000, "v1000"},
102 {"row-key-3", kFamily4, "c1", 2000, "v2000"},
103 {"row-key-4", kFamily4, "c0", 1000, "v1000"},
104 {"row-key-4", kFamily4, "c1", 2000, "v2000"}};
105
106 auto actual = ReadRows(table, bigtable::Filter::PassAllFilter());
107 CheckEqualUnordered(expected, actual);
108 }
109
TEST_F(DataIntegrationTest,TableSingleRow)110 TEST_F(DataIntegrationTest, TableSingleRow) {
111 std::string const row_key = "row-key-1";
112 auto table = GetTable();
113
114 auto mutation = bigtable::SingleRowMutation(
115 row_key, bigtable::SetCell(kFamily4, "c1", 1_ms, "V1000"),
116 bigtable::SetCell(kFamily4, "c2", 2_ms, "V2000"),
117 bigtable::SetCell(kFamily4, "c3", 3_ms, "V3000"));
118 ASSERT_STATUS_OK(table.Apply(std::move(mutation)));
119 std::vector<bigtable::Cell> expected{
120 {row_key, kFamily4, "c1", 1000, "V1000"},
121 {row_key, kFamily4, "c2", 2000, "V2000"},
122 {row_key, kFamily4, "c3", 3000, "V3000"}};
123
124 auto actual = ReadRows(table, bigtable::Filter::PassAllFilter());
125 CheckEqualUnordered(expected, actual);
126 }
127
TEST_F(DataIntegrationTest,TableReadRowTest)128 TEST_F(DataIntegrationTest, TableReadRowTest) {
129 auto table = GetTable();
130 std::string const row_key1 = "row-key-1";
131 std::string const row_key2 = "row-key-2";
132
133 std::vector<bigtable::Cell> created{
134 {row_key1, kFamily4, "c1", 1000, "v1000"},
135 {row_key2, kFamily4, "c2", 2000, "v2000"}};
136 std::vector<bigtable::Cell> expected{
137 {row_key1, kFamily4, "c1", 1000, "v1000"}};
138
139 CreateCells(table, created);
140 auto row_cell = table.ReadRow(row_key1, bigtable::Filter::PassAllFilter());
141 ASSERT_STATUS_OK(row_cell);
142 std::vector<bigtable::Cell> actual;
143 actual.emplace_back(row_cell->second.cells().at(0));
144 CheckEqualUnordered(expected, actual);
145 }
146
TEST_F(DataIntegrationTest,TableReadRowNotExistTest)147 TEST_F(DataIntegrationTest, TableReadRowNotExistTest) {
148 auto table = GetTable();
149 std::string const row_key1 = "row-key-1";
150 std::string const row_key2 = "row-key-2";
151
152 std::vector<bigtable::Cell> created{
153 {row_key1, kFamily4, "c1", 1000, "v1000"}};
154
155 CreateCells(table, created);
156 auto row_cell = table.ReadRow(row_key2, bigtable::Filter::PassAllFilter());
157 ASSERT_STATUS_OK(row_cell);
158 EXPECT_FALSE(row_cell->first);
159 }
160
TEST_F(DataIntegrationTest,TableReadRowsAllRows)161 TEST_F(DataIntegrationTest, TableReadRowsAllRows) {
162 auto table = GetTable();
163 std::string const row_key1 = "row-key-1";
164 std::string const row_key2 = "row-key-2";
165 std::string const row_key3(1024, '3'); // a long key
166 std::string const long_value(1024, 'v'); // a long value
167
168 std::vector<bigtable::Cell> created{
169 {row_key1, kFamily4, "c1", 1000, "data1"},
170 {row_key1, kFamily4, "c2", 1000, "data2"},
171 {row_key2, kFamily4, "c1", 1000, ""},
172 {row_key3, kFamily4, "c1", 1000, long_value}};
173
174 CreateCells(table, created);
175
176 // Some equivalent ways to read the three rows
177 auto read1 =
178 table.ReadRows(bigtable::RowSet(bigtable::RowRange::InfiniteRange()),
179 bigtable::Filter::PassAllFilter());
180 CheckEqualUnordered(created, MoveCellsFromReader(read1));
181
182 auto read2 =
183 table.ReadRows(bigtable::RowSet(bigtable::RowRange::InfiniteRange()), 3,
184 bigtable::Filter::PassAllFilter());
185 CheckEqualUnordered(created, MoveCellsFromReader(read2));
186
187 auto read3 = table.ReadRows(
188 bigtable::RowSet(bigtable::RowRange::InfiniteRange()),
189 bigtable::RowReader::NO_ROWS_LIMIT, bigtable::Filter::PassAllFilter());
190 CheckEqualUnordered(created, MoveCellsFromReader(read3));
191
192 if (!UsingCloudBigtableEmulator()) {
193 // TODO(#151) - remove workarounds for emulator bug(s).
194 auto read4 =
195 table.ReadRows(bigtable::RowSet(), bigtable::Filter::PassAllFilter());
196 CheckEqualUnordered(created, MoveCellsFromReader(read4));
197 }
198 }
199
TEST_F(DataIntegrationTest,TableReadRowsPartialRows)200 TEST_F(DataIntegrationTest, TableReadRowsPartialRows) {
201 auto table = GetTable();
202 std::string const row_key1 = "row-key-1";
203 std::string const row_key2 = "row-key-2";
204 std::string const row_key3 = "row-key-3";
205
206 std::vector<bigtable::Cell> created{
207 {row_key1, kFamily4, "c1", 1000, "data1"},
208 {row_key1, kFamily4, "c2", 1000, "data2"},
209 {row_key2, kFamily4, "c1", 1000, "data3"},
210 {row_key3, kFamily4, "c1", 1000, "data4"}};
211
212 CreateCells(table, created);
213
214 std::vector<bigtable::Cell> expected{
215 {row_key1, kFamily4, "c1", 1000, "data1"},
216 {row_key1, kFamily4, "c2", 1000, "data2"},
217 {row_key2, kFamily4, "c1", 1000, "data3"}};
218
219 // Some equivalent ways of reading just the first two rows
220 {
221 SCOPED_TRACE(table.table_name() + " ReadRows(key1, key2)");
222 bigtable::RowSet rows;
223 rows.Append(row_key1);
224 rows.Append(row_key2);
225 auto reader =
226 table.ReadRows(std::move(rows), bigtable::Filter::PassAllFilter());
227 CheckEqualUnordered(expected, MoveCellsFromReader(reader));
228 }
229
230 {
231 SCOPED_TRACE(table.table_name() + " ReadRows(, limit = 2, )");
232 auto reader =
233 table.ReadRows(bigtable::RowSet(bigtable::RowRange::InfiniteRange()), 2,
234 bigtable::Filter::PassAllFilter());
235 CheckEqualUnordered(expected, MoveCellsFromReader(reader));
236 }
237
238 {
239 SCOPED_TRACE(table.table_name() + " ReadRows([key1, key2], ...)");
240 bigtable::RowSet rows(bigtable::RowRange::Closed(row_key1, row_key2));
241 auto reader =
242 table.ReadRows(std::move(rows), bigtable::Filter::PassAllFilter());
243 CheckEqualUnordered(expected, MoveCellsFromReader(reader));
244 }
245 }
246
TEST_F(DataIntegrationTest,TableReadRowsNoRows)247 TEST_F(DataIntegrationTest, TableReadRowsNoRows) {
248 auto table = GetTable();
249 std::string const row_key1 = "row-key-1";
250 std::string const row_key2 = "row-key-2";
251 std::string const row_key3 = "row-key-3";
252
253 std::vector<bigtable::Cell> created{
254 {row_key1, kFamily4, "c1", 1000, "data1"},
255 {row_key3, kFamily4, "c1", 1000, "data2"}};
256
257 CreateCells(table, created);
258
259 std::vector<bigtable::Cell> expected; // empty
260
261 // read nonexistent rows
262 auto read1 = table.ReadRows(bigtable::RowSet(row_key2),
263 bigtable::Filter::PassAllFilter());
264 CheckEqualUnordered(expected, MoveCellsFromReader(read1));
265
266 auto read2 =
267 table.ReadRows(bigtable::RowSet(bigtable::RowRange::Prefix(row_key2)),
268 bigtable::Filter::PassAllFilter());
269 CheckEqualUnordered(expected, MoveCellsFromReader(read2));
270
271 auto read3 = table.ReadRows(bigtable::RowSet(bigtable::RowRange::Empty()),
272 bigtable::Filter::PassAllFilter());
273 CheckEqualUnordered(expected, MoveCellsFromReader(read3));
274 }
275
TEST_F(DataIntegrationTest,TableReadRowsWrongTable)276 TEST_F(DataIntegrationTest, TableReadRowsWrongTable) {
277 std::string const table_id = RandomTableId();
278
279 bigtable::Table table(data_client_, table_id);
280
281 auto read1 =
282 table.ReadRows(bigtable::RowSet(bigtable::RowRange::InfiniteRange()),
283 bigtable::Filter::PassAllFilter());
284
285 auto it = read1.begin();
286 ASSERT_NE(read1.end(), it);
287 EXPECT_FALSE(*it);
288 ++it;
289 EXPECT_EQ(read1.end(), it);
290 }
291
TEST_F(DataIntegrationTest,TableCheckAndMutateRowPass)292 TEST_F(DataIntegrationTest, TableCheckAndMutateRowPass) {
293 auto table = GetTable();
294 std::string const key = "row-key";
295
296 std::vector<bigtable::Cell> created{{key, kFamily4, "c1", 0, "v1000"}};
297 CreateCells(table, created);
298 auto result = table.CheckAndMutateRow(
299 key, bigtable::Filter::ValueRegex("v1000"),
300 {bigtable::SetCell(kFamily4, "c2", 0_ms, "v2000")},
301 {bigtable::SetCell(kFamily4, "c3", 0_ms, "v3000")});
302 ASSERT_STATUS_OK(result);
303 EXPECT_EQ(bigtable::MutationBranch::kPredicateMatched, *result);
304 std::vector<bigtable::Cell> expected{{key, kFamily4, "c1", 0, "v1000"},
305 {key, kFamily4, "c2", 0, "v2000"}};
306 auto actual = ReadRows(table, bigtable::Filter::PassAllFilter());
307 CheckEqualUnordered(expected, actual);
308 }
309
TEST_F(DataIntegrationTest,TableCheckAndMutateRowFail)310 TEST_F(DataIntegrationTest, TableCheckAndMutateRowFail) {
311 auto table = GetTable();
312 std::string const key = "row-key";
313
314 std::vector<bigtable::Cell> created{{key, kFamily4, "c1", 0, "v1000"}};
315 CreateCells(table, created);
316 auto result = table.CheckAndMutateRow(
317 key, bigtable::Filter::ValueRegex("not-there"),
318 {bigtable::SetCell(kFamily4, "c2", 0_ms, "v2000")},
319 {bigtable::SetCell(kFamily4, "c3", 0_ms, "v3000")});
320 ASSERT_STATUS_OK(result);
321 EXPECT_EQ(bigtable::MutationBranch::kPredicateNotMatched, *result);
322 std::vector<bigtable::Cell> expected{{key, kFamily4, "c1", 0, "v1000"},
323 {key, kFamily4, "c3", 0, "v3000"}};
324 auto actual = ReadRows(table, bigtable::Filter::PassAllFilter());
325 CheckEqualUnordered(expected, actual);
326 }
327
TEST_F(DataIntegrationTest,TableReadModifyWriteAppendValueTest)328 TEST_F(DataIntegrationTest, TableReadModifyWriteAppendValueTest) {
329 auto table = GetTable();
330 std::string const row_key1 = "row-key-1";
331 std::string const row_key2 = "row-key-2";
332 std::string const add_suffix1 = "-suffix";
333 std::string const add_suffix2 = "-next";
334 std::string const add_suffix3 = "-newrecord";
335
336 std::vector<bigtable::Cell> created{
337 {row_key1, kFamily1, "column-id1", 1000, "v1000"},
338 {row_key1, kFamily2, "column-id2", 2000, "v2000"},
339 {row_key1, kFamily3, "column-id1", 2000, "v3000"},
340 {row_key1, kFamily1, "column-id3", 2000, "v5000"}};
341
342 std::vector<bigtable::Cell> expected{
343 {row_key1, kFamily1, "column-id1", 1000, "v1000" + add_suffix1},
344 {row_key1, kFamily2, "column-id2", 2000, "v2000" + add_suffix2},
345 {row_key1, kFamily3, "column-id3", 2000, add_suffix3}};
346
347 CreateCells(table, created);
348 auto result_row =
349 table.ReadModifyWriteRow(row_key1,
350 bigtable::ReadModifyWriteRule::AppendValue(
351 kFamily1, "column-id1", add_suffix1),
352 bigtable::ReadModifyWriteRule::AppendValue(
353 kFamily2, "column-id2", add_suffix2),
354 bigtable::ReadModifyWriteRule::AppendValue(
355 kFamily3, "column-id3", add_suffix3));
356 ASSERT_STATUS_OK(result_row);
357 // Returned cells contains timestamp in microseconds which is
358 // not matching with the timestamp in expected cells, So creating
359 // cells by ignoring timestamp
360 auto expected_cells_ignore_timestamp = GetCellsIgnoringTimestamp(expected);
361 auto actual_cells_ignore_timestamp =
362 GetCellsIgnoringTimestamp(result_row->cells());
363
364 CheckEqualUnordered(expected_cells_ignore_timestamp,
365 actual_cells_ignore_timestamp);
366 }
367
TEST_F(DataIntegrationTest,TableReadModifyWriteRowIncrementAmountTest)368 TEST_F(DataIntegrationTest, TableReadModifyWriteRowIncrementAmountTest) {
369 auto table = GetTable();
370 std::string const key = "row-key";
371
372 // An initial; big-endian int64 number with value 0.
373 std::string v1("\x00\x00\x00\x00\x00\x00\x00\x00", 8);
374 std::vector<bigtable::Cell> created{{key, kFamily1, "c1", 0, v1}};
375
376 // The expected values as buffers containing big-endian int64 numbers.
377 std::string e1("\x00\x00\x00\x00\x00\x00\x00\x2A", 8);
378 std::string e2("\x00\x00\x00\x00\x00\x00\x00\x07", 8);
379 std::vector<bigtable::Cell> expected{{key, kFamily1, "c1", 0, e1},
380 {key, kFamily1, "c2", 0, e2}};
381
382 CreateCells(table, created);
383 auto row = table.ReadModifyWriteRow(
384 key, bigtable::ReadModifyWriteRule::IncrementAmount(kFamily1, "c1", 42),
385 bigtable::ReadModifyWriteRule::IncrementAmount(kFamily1, "c2", 7));
386 ASSERT_STATUS_OK(row);
387 // Ignore the server set timestamp on the returned cells because it is not
388 // predictable.
389 auto expected_ignore_timestamp = GetCellsIgnoringTimestamp(expected);
390 auto actual_ignore_timestamp = GetCellsIgnoringTimestamp(row->cells());
391
392 CheckEqualUnordered(expected_ignore_timestamp, actual_ignore_timestamp);
393 }
394
TEST_F(DataIntegrationTest,TableReadModifyWriteRowMultipleTest)395 TEST_F(DataIntegrationTest, TableReadModifyWriteRowMultipleTest) {
396 auto table = GetTable();
397 std::string const key = "row-key";
398
399 std::string v1("\x00\x00\x00\x00\x00\x00\x00\x00", 8);
400 std::vector<bigtable::Cell> created{{key, kFamily1, "c1", 0, v1},
401 {key, kFamily1, "c3", 0, "start;"},
402 {key, kFamily2, "d1", 0, v1},
403 {key, kFamily2, "d3", 0, "start;"}};
404
405 // The expected values as buffers containing big-endian int64 numbers.
406 std::string e1("\x00\x00\x00\x00\x00\x00\x00\x2A", 8);
407 std::string e2("\x00\x00\x00\x00\x00\x00\x00\x07", 8);
408 std::string e3("\x00\x00\x00\x00\x00\x00\x07\xD0", 8);
409 std::string e4("\x00\x00\x00\x00\x00\x00\x0B\xB8", 8);
410 std::vector<bigtable::Cell> expected{{key, kFamily1, "c1", 0, e1},
411 {key, kFamily1, "c2", 0, e2},
412 {key, kFamily1, "c3", 0, "start;suffix"},
413 {key, kFamily1, "c4", 0, "suffix"},
414 {key, kFamily2, "d1", 0, e3},
415 {key, kFamily2, "d2", 0, e4},
416 {key, kFamily2, "d3", 0, "start;suffix"},
417 {key, kFamily2, "d4", 0, "suffix"}};
418
419 CreateCells(table, created);
420 using R = bigtable::ReadModifyWriteRule;
421 auto row =
422 table.ReadModifyWriteRow(key, R::IncrementAmount(kFamily1, "c1", 42),
423 R::IncrementAmount(kFamily1, "c2", 7),
424 R::IncrementAmount(kFamily2, "d1", 2000),
425 R::IncrementAmount(kFamily2, "d2", 3000),
426 R::AppendValue(kFamily1, "c3", "suffix"),
427 R::AppendValue(kFamily1, "c4", "suffix"),
428 R::AppendValue(kFamily2, "d3", "suffix"),
429 R::AppendValue(kFamily2, "d4", "suffix"));
430 ASSERT_STATUS_OK(row);
431 // Ignore the server set timestamp on the returned cells because it is not
432 // predictable.
433 auto expected_ignore_timestamp = GetCellsIgnoringTimestamp(expected);
434 auto actual_ignore_timestamp = GetCellsIgnoringTimestamp(row->cells());
435
436 CheckEqualUnordered(expected_ignore_timestamp, actual_ignore_timestamp);
437 }
438
TEST_F(DataIntegrationTest,TableCellValueInt64Test)439 TEST_F(DataIntegrationTest, TableCellValueInt64Test) {
440 auto table = GetTable();
441 std::string const key = "row-key";
442
443 std::vector<bigtable::Cell> created{{key, kFamily1, "c1", 0, 42},
444 {key, kFamily1, "c3", 0, "start;"},
445 {key, kFamily2, "d1", 0, 2},
446 {key, kFamily2, "d2", 0, 5012},
447 {key, kFamily2, "d3", 0, "start;"}};
448
449 std::vector<bigtable::Cell> expected{
450 {key, kFamily1, "c1", 0, 40},
451 {key, kFamily1, "c2", 0, 7},
452 {key, kFamily1, "c3", 0, "start;suffix"},
453 {key, kFamily2, "d1", 0, 2002},
454 {key, kFamily2, "d2", 0, 9999998012},
455 {key, kFamily2, "d3", 0, "start;suffix"}};
456
457 CreateCells(table, created);
458 using R = bigtable::ReadModifyWriteRule;
459 auto row =
460 table.ReadModifyWriteRow(key, R::IncrementAmount(kFamily1, "c1", -2),
461 R::IncrementAmount(kFamily1, "c2", 7),
462 R::IncrementAmount(kFamily2, "d1", 2000),
463 R::IncrementAmount(kFamily2, "d2", 9999993000),
464 R::AppendValue(kFamily1, "c3", "suffix"),
465 R::AppendValue(kFamily2, "d3", "suffix"));
466 ASSERT_STATUS_OK(row);
467 // Ignore the server set timestamp on the returned cells because it is not
468 // predictable.
469 auto expected_ignore_timestamp = GetCellsIgnoringTimestamp(expected);
470 auto actual_ignore_timestamp = GetCellsIgnoringTimestamp(row->cells());
471
472 CheckEqualUnordered(expected_ignore_timestamp, actual_ignore_timestamp);
473 }
474
TEST_F(DataIntegrationTest,TableSampleRowKeysTest)475 TEST_F(DataIntegrationTest, TableSampleRowKeysTest) {
476 auto table = GetTable();
477
478 // Create kBatchSize * kBatchCount rows.
479 int constexpr kBatchCount = 10;
480 int constexpr kBatchSize = 5000;
481 int constexpr kColumnCount = 10;
482 int rowid = 0;
483 for (int batch = 0; batch != kBatchCount; ++batch) {
484 bigtable::BulkMutation bulk;
485 for (int row = 0; row != kBatchSize; ++row) {
486 std::ostringstream os;
487 os << "row:" << std::setw(9) << std::setfill('0') << rowid;
488
489 // Build a mutation that creates 10 columns.
490 bigtable::SingleRowMutation mutation(os.str());
491 for (int col = 0; col != kColumnCount; ++col) {
492 std::string colid = "c" + std::to_string(col);
493 std::string value = colid + "#" + os.str();
494 mutation.emplace_back(bigtable::SetCell(kFamily1, std::move(colid),
495 std::chrono::milliseconds(0),
496 std::move(value)));
497 }
498 bulk.emplace_back(std::move(mutation));
499 ++rowid;
500 }
501 auto failures = table.BulkApply(std::move(bulk));
502 ASSERT_TRUE(failures.empty()) << "failures=" << [&failures]() {
503 std::ostringstream os;
504 char const* sep = "[";
505 for (auto const& f : failures) {
506 os << sep << "failed[" << f.original_index() << "]=" << f.status();
507 sep = ", ";
508 }
509 os << "]";
510 return os.str();
511 }();
512 }
513 auto samples = table.SampleRows();
514 ASSERT_STATUS_OK(samples);
515
516 // It is somewhat hard to verify that the values returned here are correct.
517 // We cannot check the specific values, not even the format, of the row keys
518 // because Cloud Bigtable might return an empty row key (for "end of table"),
519 // and it might return row keys that have never been written to.
520 // All we can check is that this is not empty, and that the offsets are in
521 // ascending order.
522 EXPECT_FALSE(samples->empty());
523 std::int64_t previous = 0;
524 for (auto const& s : *samples) {
525 EXPECT_LE(previous, s.offset_bytes);
526 previous = s.offset_bytes;
527 }
528 // At least one of the samples should have non-zero offset:
529 auto last = samples->back();
530 EXPECT_LT(0, last.offset_bytes);
531 }
532
TEST_F(DataIntegrationTest,TableReadMultipleCellsBigValue)533 TEST_F(DataIntegrationTest, TableReadMultipleCellsBigValue) {
534 if (UsingCloudBigtableEmulator()) {
535 // TODO(#151) - remove workarounds for emulator bug(s).
536 return;
537 }
538
539 auto table = GetTable();
540
541 std::string const row_key = "row-key-1";
542 // cell vector contains 4 cells of 32 MiB each, or 128 MiB (without
543 // considering any overhead). That is much larger that the default gRPC
544 // message size (~4 MiB), and yet much smaller than the configured message
545 // size (~256MiB). Therefore, the row would not fit in a message if we failed
546 // to change the default configuration, but it is not so large that it will
547 // fail to work if we miss the overhead estimation.
548 auto constexpr kMib = 1024 * 1024UL;
549 auto constexpr kCellSize = 32 * kMib;
550 auto constexpr kCellCount = 4;
551 // Smaller rows than this size are not a good test, they would pass with the
552 // default setting, so only accept rows that are at least 10x the default
553 // setting of 4 MiB.
554 auto const min_row_size = 10 * 4 * kMib;
555 // Larger rows than this size are not a good test, they would fail even if the
556 // setting was working.
557 auto const max_row_size = 256 * kMib;
558
559 std::string value(kCellSize, 'a');
560 std::vector<bigtable::Cell> created;
561 std::vector<bigtable::Cell> expected;
562
563 for (int i = 0; i < kCellCount; i++) {
564 auto col_qualifier = "c" + std::to_string(i);
565 created.emplace_back(row_key, kFamily4, col_qualifier, 0, value);
566 expected.emplace_back(row_key, kFamily4, col_qualifier, 0, value);
567 }
568
569 CreateCells(table, created);
570
571 auto result = table.ReadRow(row_key, bigtable::Filter::PassAllFilter());
572 ASSERT_STATUS_OK(result);
573 EXPECT_TRUE(result->first);
574
575 std::size_t total_row_size = 0;
576 for (auto const& cell : result->second.cells()) {
577 total_row_size += cell.value().size();
578 }
579 EXPECT_LT(total_row_size, max_row_size);
580 EXPECT_GT(total_row_size, min_row_size);
581
582 // Ignore the server set timestamp on the returned cells because it is not
583 // predictable.
584 auto expected_ignore_timestamp = GetCellsIgnoringTimestamp(expected);
585 auto actual_ignore_timestamp =
586 GetCellsIgnoringTimestamp(result->second.cells());
587 CheckEqualUnordered(expected_ignore_timestamp, actual_ignore_timestamp);
588 }
589
590 } // anonymous namespace
591
main(int argc,char * argv[])592 int main(int argc, char* argv[]) {
593 ::testing::InitGoogleMock(&argc, argv);
594 (void)::testing::AddGlobalTestEnvironment(
595 new ::bigtable::testing::TableTestEnvironment);
596
597 return RUN_ALL_TESTS();
598 }
599