1 // Copyright 2018 Google LLC
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/examples/bigtable_examples_common.h"
16 //! [bigtable includes]
17 #include "google/cloud/bigtable/table.h"
18 #include "google/cloud/bigtable/table_admin.h"
19 //! [bigtable includes]
20 #include "google/cloud/internal/getenv.h"
21 #include "google/cloud/internal/random.h"
22 #include "google/cloud/testing_util/crash_handler.h"
23 #include "absl/strings/str_split.h"
24 #include <chrono>
25 #include <sstream>
26
27 namespace {
28
29 using google::cloud::bigtable::examples::Usage;
30
Apply(google::cloud::bigtable::Table table,std::vector<std::string> const &)31 void Apply(google::cloud::bigtable::Table table,
32 std::vector<std::string> const&) {
33 //! [apply]
34 namespace cbt = google::cloud::bigtable;
35 [](cbt::Table table) {
36 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
37 std::chrono::system_clock::now().time_since_epoch());
38
39 cbt::SingleRowMutation mutation("test-key-for-apply");
40 mutation.emplace_back(
41 cbt::SetCell("fam", "column0", timestamp, "value for column0"));
42 mutation.emplace_back(
43 cbt::SetCell("fam", "column1", timestamp, "value for column1"));
44 auto status = table.Apply(std::move(mutation));
45 if (!status.ok()) throw std::runtime_error(status.message());
46 }
47 //! [apply]
48 (std::move(table));
49 }
50
ApplyRelaxedIdempotency(google::cloud::bigtable::Table const & table,std::vector<std::string> const & argv)51 void ApplyRelaxedIdempotency(google::cloud::bigtable::Table const& table,
52 std::vector<std::string> const& argv) {
53 //! [apply relaxed idempotency]
54 namespace cbt = google::cloud::bigtable;
55 [](std::string const& project_id, std::string const& instance_id,
56 std::string const& table_id, std::string const& row_key) {
57 cbt::Table table(cbt::CreateDefaultDataClient(project_id, instance_id,
58 cbt::ClientOptions()),
59 table_id, cbt::AlwaysRetryMutationPolicy());
60 // Normally this is not retried on transient failures, because the operation
61 // is not idempotent (each retry would set a different timestamp), in this
62 // case it would, because the table is setup to always retry.
63 cbt::SingleRowMutation mutation(
64 row_key, cbt::SetCell("fam", "some-column", "some-value"));
65 google::cloud::Status status = table.Apply(std::move(mutation));
66 if (!status.ok()) throw std::runtime_error(status.message());
67 }
68 //! [apply relaxed idempotency]
69 (table.project_id(), table.instance_id(), table.table_id(), argv.at(0));
70 }
71
ApplyCustomRetry(google::cloud::bigtable::Table const & table,std::vector<std::string> const & argv)72 void ApplyCustomRetry(google::cloud::bigtable::Table const& table,
73 std::vector<std::string> const& argv) {
74 //! [apply custom retry]
75 namespace cbt = google::cloud::bigtable;
76 [](std::string const& project_id, std::string const& instance_id,
77 std::string const& table_id, std::string const& row_key) {
78 cbt::Table table(cbt::CreateDefaultDataClient(project_id, instance_id,
79 cbt::ClientOptions()),
80 table_id, cbt::LimitedErrorCountRetryPolicy(7));
81 cbt::SingleRowMutation mutation(
82 row_key, cbt::SetCell("fam", "some-column",
83 std::chrono::milliseconds(0), "some-value"));
84 google::cloud::Status status = table.Apply(std::move(mutation));
85 if (!status.ok()) throw std::runtime_error(status.message());
86 }
87 //! [apply custom retry]
88 (table.project_id(), table.instance_id(), table.table_id(), argv.at(0));
89 }
90
BulkApply(google::cloud::bigtable::Table table,std::vector<std::string> const &)91 void BulkApply(google::cloud::bigtable::Table table,
92 std::vector<std::string> const&) {
93 //! [bulk apply] [START bigtable_mutate_insert_rows]
94 namespace cbt = google::cloud::bigtable;
95 [](cbt::Table table) {
96 // Write several rows in a single operation, each row has some trivial data.
97 cbt::BulkMutation bulk;
98 for (int i = 0; i != 5000; ++i) {
99 // Note: This example uses sequential numeric IDs for simplicity, but
100 // this can result in poor performance in a production application.
101 // Since rows are stored in sorted order by key, sequential keys can
102 // result in poor distribution of operations across nodes.
103 //
104 // For more information about how to design a Bigtable schema for the
105 // best performance, see the documentation:
106 //
107 // https://cloud.google.com/bigtable/docs/schema-design
108 char buf[32];
109 snprintf(buf, sizeof(buf), "key-%06d", i);
110 cbt::SingleRowMutation mutation(buf);
111 mutation.emplace_back(
112 cbt::SetCell("fam", "col0", "value0-" + std::to_string(i)));
113 mutation.emplace_back(
114 cbt::SetCell("fam", "col1", "value1-" + std::to_string(i)));
115 bulk.emplace_back(std::move(mutation));
116 }
117 std::vector<cbt::FailedMutation> failures =
118 table.BulkApply(std::move(bulk));
119 if (failures.empty()) {
120 std::cout << "All mutations applied successfully\n";
121 return;
122 }
123 // By default, the `table` object uses the `SafeIdempotentMutationPolicy`
124 // which does not retry if any of the mutations fails and are
125 // not-idempotent. In this example we simply print such failures, if any,
126 // and ignore them otherwise.
127 std::cerr << "The following mutations failed and were not retried:\n";
128 for (auto const& f : failures) {
129 std::cerr << "index[" << f.original_index() << "]=" << f.status() << "\n";
130 }
131 }
132 //! [bulk apply] [END bigtable_mutate_insert_rows]
133 (std::move(table));
134 }
135
ReadRow(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)136 void ReadRow(google::cloud::bigtable::Table table,
137 std::vector<std::string> const& argv) {
138 namespace cbt = google::cloud::bigtable;
139 using google::cloud::StatusOr;
140 [](google::cloud::bigtable::Table table, std::string const& row_key) {
141 // Filter the results, only include the latest value on each cell.
142 cbt::Filter filter = cbt::Filter::Latest(1);
143 // Read a row, this returns a tuple (bool, row)
144 StatusOr<std::pair<bool, cbt::Row>> tuple =
145 table.ReadRow(row_key, std::move(filter));
146 if (!tuple) throw std::runtime_error(tuple.status().message());
147 if (!tuple->first) {
148 std::cout << "Row " << row_key << " not found\n";
149 return;
150 }
151 std::cout << "key: " << tuple->second.row_key() << "\n";
152 for (auto const& cell : tuple->second.cells()) {
153 std::cout << " " << cell.family_name() << ":"
154 << cell.column_qualifier() << " = <";
155 if (cell.column_qualifier() == "counter") {
156 // This example uses "counter" to store 64-bit numbers in big-endian
157 // format, extract them as follows:
158 std::cout << cell.decode_big_endian_integer<std::int64_t>().value();
159 } else {
160 std::cout << cell.value();
161 }
162 std::cout << ">\n";
163 }
164 }(std::move(table), argv.at(0));
165 }
166
CheckAndMutate(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)167 void CheckAndMutate(google::cloud::bigtable::Table table,
168 std::vector<std::string> const& argv) {
169 //! [check and mutate]
170 namespace cbt = google::cloud::bigtable;
171 using google::cloud::StatusOr;
172 [](cbt::Table table, std::string const& row_key) {
173 // Check if the latest value of the flip-flop column is "on".
174 cbt::Filter predicate = cbt::Filter::Chain(
175 cbt::Filter::ColumnRangeClosed("fam", "flip-flop", "flip-flop"),
176 cbt::Filter::Latest(1), cbt::Filter::ValueRegex("on"));
177 // If the predicate matches, change the latest value to "off", otherwise,
178 // change the latest value to "on". Modify the "flop-flip" column at the
179 // same time.
180 StatusOr<cbt::MutationBranch> branch =
181 table.CheckAndMutateRow(row_key, std::move(predicate),
182 {cbt::SetCell("fam", "flip-flop", "off"),
183 cbt::SetCell("fam", "flop-flip", "on")},
184 {cbt::SetCell("fam", "flip-flop", "on"),
185 cbt::SetCell("fam", "flop-flip", "off")});
186
187 if (!branch) throw std::runtime_error(branch.status().message());
188 if (*branch == cbt::MutationBranch::kPredicateMatched) {
189 std::cout << "The predicate was matched\n";
190 } else {
191 std::cout << "The predicate was not matched\n";
192 }
193 }
194 //! [check and mutate]
195 (std::move(table), argv.at(0));
196 }
197
CheckAndMutateNotPresent(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)198 void CheckAndMutateNotPresent(google::cloud::bigtable::Table table,
199 std::vector<std::string> const& argv) {
200 //! [check and mutate not present]
201 namespace cbt = google::cloud::bigtable;
202 using google::cloud::StatusOr;
203 [](cbt::Table table, std::string const& row_key) {
204 // Check if the latest value of the "test-column" column is present,
205 // regardless of its value.
206 cbt::Filter predicate = cbt::Filter::Chain(
207 cbt::Filter::ColumnRangeClosed("fam", "test-column", "test-column"),
208 cbt::Filter::Latest(1));
209 // If the predicate matches, do nothing, otherwise set the
210 // "had-test-column" to "false":
211 StatusOr<cbt::MutationBranch> branch = table.CheckAndMutateRow(
212 row_key, std::move(predicate), {},
213 {cbt::SetCell("fam", "had-test-column", "false")});
214
215 if (!branch) throw std::runtime_error(branch.status().message());
216 if (*branch == cbt::MutationBranch::kPredicateMatched) {
217 std::cout << "The predicate was matched\n";
218 } else {
219 std::cout << "The predicate was not matched\n";
220 }
221 }
222 //! [check and mutate not present]
223 (std::move(table), argv.at(0));
224 }
225
ReadModifyWrite(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)226 void ReadModifyWrite(google::cloud::bigtable::Table table,
227 std::vector<std::string> const& argv) {
228 //! [read modify write]
229 namespace cbt = google::cloud::bigtable;
230 using google::cloud::StatusOr;
231 [](cbt::Table table, std::string const& row_key) {
232 StatusOr<cbt::Row> row = table.ReadModifyWriteRow(
233 row_key, cbt::ReadModifyWriteRule::IncrementAmount("fam", "counter", 1),
234 cbt::ReadModifyWriteRule::AppendValue("fam", "list", ";element"));
235
236 // As the modify in this example is not idempotent, and this example
237 // does not attempt to retry if there is a failure, we simply print
238 // such failures, if any, and otherwise ignore them.
239 if (!row) {
240 std::cout << "Failed to append row: " << row.status().message() << "\n";
241 return;
242 }
243 std::cout << row->row_key() << "\n";
244 }
245 //! [read modify write]
246 (std::move(table), argv.at(0));
247 }
248
SampleRows(google::cloud::bigtable::Table table,std::vector<std::string> const &)249 void SampleRows(google::cloud::bigtable::Table table,
250 std::vector<std::string> const&) {
251 //! [sample row keys] [START bigtable_table_sample_splits]
252 namespace cbt = google::cloud::bigtable;
253 using google::cloud::StatusOr;
254 [](cbt::Table table) {
255 StatusOr<std::vector<cbt::RowKeySample>> samples = table.SampleRows();
256 if (!samples) throw std::runtime_error(samples.status().message());
257 for (auto const& sample : *samples) {
258 std::cout << "key=" << sample.row_key << " - " << sample.offset_bytes
259 << "\n";
260 }
261 }
262 //! [sample row keys] [END bigtable_table_sample_splits]
263 (std::move(table));
264 }
265
DeleteAllCells(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)266 void DeleteAllCells(google::cloud::bigtable::Table table,
267 std::vector<std::string> const& argv) {
268 //! [delete all cells]
269 namespace cbt = google::cloud::bigtable;
270 [](cbt::Table table, std::string const& row_key) {
271 google::cloud::Status status =
272 table.Apply(cbt::SingleRowMutation(row_key, cbt::DeleteFromRow()));
273
274 if (!status.ok()) throw std::runtime_error(status.message());
275 }
276 //! [delete all cells]
277 (std::move(table), argv.at(0));
278 }
279
DeleteFamilyCells(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)280 void DeleteFamilyCells(google::cloud::bigtable::Table table,
281 std::vector<std::string> const& argv) {
282 //! [delete family cells]
283 namespace cbt = google::cloud::bigtable;
284 [](cbt::Table table, std::string const& row_key,
285 std::string const& family_name) {
286 // Delete all cells within a family.
287 google::cloud::Status status = table.Apply(
288 cbt::SingleRowMutation(row_key, cbt::DeleteFromFamily(family_name)));
289
290 if (!status.ok()) throw std::runtime_error(status.message());
291 }
292 //! [delete family cells]
293 (std::move(table), argv.at(0), argv.at(1));
294 }
295
DeleteSelectiveFamilyCells(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)296 void DeleteSelectiveFamilyCells(google::cloud::bigtable::Table table,
297 std::vector<std::string> const& argv) {
298 //! [delete selective family cells]
299 namespace cbt = google::cloud::bigtable;
300 [](cbt::Table table, std::string const& row_key,
301 std::string const& family_name, std::string const& column_name) {
302 // Delete selective cell within a family.
303 google::cloud::Status status = table.Apply(cbt::SingleRowMutation(
304 row_key, cbt::DeleteFromColumn(family_name, column_name)));
305
306 if (!status.ok()) throw std::runtime_error(status.message());
307 }
308 //! [delete selective family cells]
309 (std::move(table), argv.at(0), argv.at(1), argv.at(2));
310 }
311
RowExists(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)312 void RowExists(google::cloud::bigtable::Table table,
313 std::vector<std::string> const& argv) {
314 //! [row exists]
315 namespace cbt = google::cloud::bigtable;
316 using google::cloud::StatusOr;
317 [](cbt::Table table, std::string const& row_key) {
318 // Filter the results, turn any value into an empty string.
319 cbt::Filter filter = cbt::Filter::StripValueTransformer();
320
321 // Read a row, this returns a tuple (bool, row)
322 StatusOr<std::pair<bool, cbt::Row>> status = table.ReadRow(row_key, filter);
323 if (!status) throw std::runtime_error(status.status().message());
324
325 if (!status->first) {
326 std::cout << "Row not found\n";
327 return;
328 }
329 std::cout << "Row exists.\n";
330 }
331 //! [row exists]
332 (std::move(table), argv.at(0));
333 }
334
MutateDeleteColumns(std::vector<std::string> const & argv)335 void MutateDeleteColumns(std::vector<std::string> const& argv) {
336 if (argv.size() < 5) {
337 // Use the same format as the cbt tool to receive mutations from the
338 // command-line.
339 throw Usage{
340 "mutate-delete-columns <project-id> <instance-id>"
341 " <table-id> key family:column [family:column]"};
342 }
343
344 auto it = argv.cbegin();
345 auto const project_id = *it++;
346 auto const instance_id = *it++;
347 auto const table_id = *it++;
348 auto const key = *it++;
349 std::vector<std::pair<std::string, std::string>> columns;
350 for (; it != argv.cend(); ++it) {
351 auto pos = it->find_first_of(':');
352 if (pos == std::string::npos) {
353 throw std::runtime_error("Invalid argument (" + *it +
354 ") should be in family:column format");
355 }
356 columns.emplace_back(
357 std::make_pair(it->substr(0, pos), it->substr(pos + 1)));
358 }
359 //! [connect data]
360 google::cloud::bigtable::Table table(
361 google::cloud::bigtable::CreateDefaultDataClient(
362 project_id, instance_id, google::cloud::bigtable::ClientOptions()),
363 table_id);
364 //! [connect data]
365
366 // [START bigtable_mutate_delete_columns]
367 namespace cbt = google::cloud::bigtable;
368 [](cbt::Table table, std::string const& key,
369 std::vector<std::pair<std::string, std::string>> const& columns) {
370 cbt::SingleRowMutation mutation(key);
371 for (auto const& c : columns) {
372 mutation.emplace_back(cbt::DeleteFromColumn(c.first, c.second));
373 }
374 google::cloud::Status status = table.Apply(std::move(mutation));
375 if (!status.ok()) throw std::runtime_error(status.message());
376 std::cout << "Columns successfully deleted from row\n";
377 }
378 // [END bigtable_mutate_delete_columns]
379 (std::move(table), key, std::move(columns));
380 }
381
MutateDeleteRows(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)382 void MutateDeleteRows(google::cloud::bigtable::Table table,
383 std::vector<std::string> const& argv) {
384 // [START bigtable_mutate_delete_rows]
385 namespace cbt = google::cloud::bigtable;
386 [](cbt::Table table, std::vector<std::string> const& keys) {
387 cbt::BulkMutation mutation;
388 for (auto const& row_key : keys) {
389 mutation.emplace_back(
390 cbt::SingleRowMutation(row_key, cbt::DeleteFromRow()));
391 }
392 std::vector<cbt::FailedMutation> failures =
393 table.BulkApply(std::move(mutation));
394 if (failures.empty()) {
395 std::cout << "All rows successfully deleted\n";
396 return;
397 }
398 std::cerr << "The following mutations failed:\n";
399 for (auto const& f : failures) {
400 std::cerr << "index[" << f.original_index() << "]=" << f.status() << "\n";
401 }
402 }
403 // [END bigtable_mutate_delete_rows]
404 (std::move(table), std::move(argv));
405 }
406
MutateDeleteRowsCommand(std::vector<std::string> const & argv)407 void MutateDeleteRowsCommand(std::vector<std::string> const& argv) {
408 if (argv.size() < 4) {
409 // Use the same format as the cbt tool to receive mutations from the
410 // command-line.
411 throw Usage{
412 "mutate-delete-rows <project-id> <instance-id>"
413 " <table-id> row-key [row-key...]"};
414 }
415 auto it = argv.cbegin();
416 auto const project_id = *it++;
417 auto const instance_id = *it++;
418 auto const table_id = *it++;
419 std::vector<std::string> rows(it, argv.cend());
420 google::cloud::bigtable::Table table(
421 google::cloud::bigtable::CreateDefaultDataClient(
422 project_id, instance_id, google::cloud::bigtable::ClientOptions()),
423 table_id);
424 MutateDeleteRows(table, std::move(rows));
425 }
426
MutateInsertUpdateRows(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)427 void MutateInsertUpdateRows(google::cloud::bigtable::Table table,
428 std::vector<std::string> const& argv) {
429 // Fortunately region tags can appear more than once, the segments are merged
430 // by the region tag processing tools.
431
432 // [START bigtable_insert_update_rows]
433 struct InsertOrUpdate {
434 std::string column_family;
435 std::string column;
436 std::string value;
437 };
438 // [END bigtable_insert_update_rows]
439
440 // A simple, though probably not very efficient, parser for mutations.
441 auto parse = [](std::string const& mut) {
442 std::vector<std::string> const tokens =
443 absl::StrSplit(mut, absl::ByAnyChar(":="));
444 return InsertOrUpdate{tokens[0], tokens[1], tokens[2]};
445 };
446
447 auto it = argv.cbegin();
448 auto const key = *it++;
449 std::vector<InsertOrUpdate> mutations;
450 for (; it != argv.cend(); ++it) {
451 mutations.emplace_back(parse(*it));
452 }
453
454 // [START bigtable_insert_update_rows]
455 namespace cbt = google::cloud::bigtable;
456 [](cbt::Table table, std::string const& key,
457 std::vector<InsertOrUpdate> const& inserts) {
458 cbt::SingleRowMutation mutation(key);
459 for (auto const& mut : inserts) {
460 mutation.emplace_back(
461 cbt::SetCell(mut.column_family, mut.column, mut.value));
462 }
463 google::cloud::Status status = table.Apply(std::move(mutation));
464 if (!status.ok()) throw std::runtime_error(status.message());
465 std::cout << "Row successfully updated\n";
466 }
467 // [END bigtable_insert_update_rows]
468 (std::move(table), key, std::move(mutations));
469 }
470
MutateInsertUpdateRowsCommand(std::vector<std::string> const & argv)471 void MutateInsertUpdateRowsCommand(std::vector<std::string> const& argv) {
472 if (argv.size() < 5) {
473 // Use the same format as the cbt tool to receive mutations from the
474 // command-line.
475 throw Usage{
476 "mutate-insert-update-rows <project-id> <instance-id>"
477 " <table-id> key family:column=value [family:column=value...]"};
478 }
479
480 auto it = argv.cbegin();
481 auto const project_id = *it++;
482 auto const instance_id = *it++;
483 auto const table_id = *it++;
484 std::vector<std::string> rows(it, argv.cend());
485 google::cloud::bigtable::Table table(
486 google::cloud::bigtable::CreateDefaultDataClient(
487 project_id, instance_id, google::cloud::bigtable::ClientOptions()),
488 table_id);
489 MutateInsertUpdateRows(table, std::move(rows));
490 }
491
RenameColumn(google::cloud::bigtable::Table table,std::vector<std::string> const & argv)492 void RenameColumn(google::cloud::bigtable::Table table,
493 std::vector<std::string> const& argv) {
494 // [START bigtable_mutate_mix_match]
495 namespace cbt = google::cloud::bigtable;
496 using google::cloud::Status;
497 using google::cloud::StatusOr;
498 [](cbt::Table table, std::string const& key, std::string const& family,
499 std::string const& old_name, std::string const& new_name) {
500 StatusOr<std::pair<bool, cbt::Row>> row =
501 table.ReadRow(key, cbt::Filter::ColumnName(family, old_name));
502
503 if (!row) throw std::runtime_error(row.status().message());
504 if (!row->first) throw std::runtime_error("Cannot find row " + key);
505
506 cbt::SingleRowMutation mutation(key);
507 for (auto const& cell : row->second.cells()) {
508 // Create a new cell
509 auto timestamp_in_milliseconds =
510 std::chrono::duration_cast<std::chrono::milliseconds>(
511 cell.timestamp());
512 mutation.emplace_back(cbt::SetCell(family, new_name,
513 timestamp_in_milliseconds,
514 std::move(cell).value()));
515 }
516 mutation.emplace_back(cbt::DeleteFromColumn("fam", old_name));
517
518 google::cloud::Status status = table.Apply(std::move(mutation));
519 if (!status.ok()) throw std::runtime_error(status.message());
520 std::cout << "Row successfully updated\n";
521 }
522 // [END bigtable_mutate_mix_match]
523 (std::move(table), argv.at(0), argv.at(1), argv.at(2), argv.at(3));
524 }
525
526 // This command just generates data suitable for other examples to run. This
527 // code is not extracted into the documentation.
InsertTestData(google::cloud::bigtable::Table table,std::vector<std::string> const &)528 void InsertTestData(google::cloud::bigtable::Table table,
529 std::vector<std::string> const&) {
530 // Write several rows in a single operation, each row has some trivial data.
531 // This is not a code sample in the normal sense, we do not display this code
532 // in the documentation. We use it to populate data in the table used to run
533 // the actual examples during the CI builds.
534 namespace cbt = google::cloud::bigtable;
535 cbt::BulkMutation bulk;
536 for (int i = 0; i != 5000; ++i) {
537 // Note: This example uses sequential numeric IDs for simplicity, but
538 // this can result in poor performance in a production application.
539 // Since rows are stored in sorted order by key, sequential keys can
540 // result in poor distribution of operations across nodes.
541 //
542 // For more information about how to design a Bigtable schema for the
543 // best performance, see the documentation:
544 //
545 // https://cloud.google.com/bigtable/docs/schema-design
546 char buf[32];
547 snprintf(buf, sizeof(buf), "key-%06d", i);
548 cbt::SingleRowMutation mutation(buf);
549 mutation.emplace_back(cbt::SetCell("fam", "col0",
550 std::chrono::milliseconds(0),
551 "value0-" + std::to_string(i)));
552 mutation.emplace_back(cbt::SetCell("fam", "col1",
553 std::chrono::milliseconds(0),
554 "value1-" + std::to_string(i)));
555 mutation.emplace_back(cbt::SetCell("fam", "col2",
556 std::chrono::milliseconds(0),
557 "value2-" + std::to_string(i)));
558 bulk.emplace_back(std::move(mutation));
559 }
560 auto failures = table.BulkApply(std::move(bulk));
561 if (failures.empty()) {
562 return;
563 }
564 std::cerr << "The following mutations failed:\n";
565 for (auto const& f : failures) {
566 std::cerr << "index[" << f.original_index() << "]=" << f.status() << "\n";
567 }
568 throw std::runtime_error(failures.front().status().message());
569 }
570
571 // This command just generates data suitable for other examples to run. This
572 // code is not extracted into the documentation.
PopulateTableHierarchy(google::cloud::bigtable::Table table,std::vector<std::string> const &)573 void PopulateTableHierarchy(google::cloud::bigtable::Table table,
574 std::vector<std::string> const&) {
575 namespace cbt = google::cloud::bigtable;
576 // Write several rows.
577 int q = 0;
578 for (int i = 0; i != 4; ++i) {
579 for (int j = 0; j != 4; ++j) {
580 for (int k = 0; k != 4; ++k) {
581 std::string row_key = "root/" + std::to_string(i) + "/";
582 row_key += std::to_string(j) + "/";
583 row_key += std::to_string(k);
584 cbt::SingleRowMutation mutation(row_key);
585 mutation.emplace_back(cbt::SetCell("fam", "col0",
586 std::chrono::milliseconds(0),
587 "value-" + std::to_string(q)));
588 ++q;
589 google::cloud::Status status = table.Apply(std::move(mutation));
590 if (!status.ok()) throw std::runtime_error(status.message());
591 }
592 }
593 }
594 }
595
WriteSimple(google::cloud::bigtable::Table table,std::vector<std::string> const &)596 void WriteSimple(google::cloud::bigtable::Table table,
597 std::vector<std::string> const&) {
598 // [START bigtable_writes_simple]
599 namespace cbt = google::cloud::bigtable;
600 [](cbt::Table table) {
601 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
602 std::chrono::system_clock::now().time_since_epoch());
603
604 std::string row_key = "phone#4c410523#20190501";
605 cbt::SingleRowMutation mutation(row_key);
606 std::string column_family = "stats_summary";
607
608 mutation.emplace_back(cbt::SetCell(column_family, "connected_cell",
609 timestamp, std::int64_t{1}));
610 mutation.emplace_back(cbt::SetCell(column_family, "connected_wifi",
611 timestamp, std::int64_t{1}));
612 mutation.emplace_back(
613 cbt::SetCell(column_family, "os_build", timestamp, "PQ2A.190405.003"));
614 google::cloud::Status status = table.Apply(std::move(mutation));
615 if (!status.ok()) throw std::runtime_error(status.message());
616 std::cout << "Successfully wrote row" << row_key << "\n";
617 }
618 // [END bigtable_writes_simple]
619 (std::move(table));
620 }
621
WriteBatch(google::cloud::bigtable::Table table,std::vector<std::string> const &)622 void WriteBatch(google::cloud::bigtable::Table table,
623 std::vector<std::string> const&) {
624 // [START bigtable_writes_batch]
625 namespace cbt = google::cloud::bigtable;
626 [](cbt::Table table) {
627 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
628 std::chrono::system_clock::now().time_since_epoch());
629 std::string column_family = "stats_summary";
630
631 cbt::BulkMutation bulk;
632 bulk.emplace_back(cbt::SingleRowMutation(
633 "tablet#a0b81f74#20190501",
634 cbt::SetCell(column_family, "connected_cell", timestamp,
635 std::int64_t{1}),
636 cbt::SetCell(column_family, "os_build", timestamp, "12155.0.0-rc1")));
637 bulk.emplace_back(cbt::SingleRowMutation(
638 "tablet#a0b81f74#20190502",
639 cbt::SetCell(column_family, "connected_cell", timestamp,
640 std::int64_t{1}),
641 cbt::SetCell(column_family, "os_build", timestamp, "12145.0.0-rc6")));
642
643 std::vector<cbt::FailedMutation> failures =
644 table.BulkApply(std::move(bulk));
645 if (failures.empty()) {
646 std::cout << "Successfully wrote 2 rows.\n";
647 return;
648 }
649 std::cerr << "The following mutations failed:\n";
650 for (auto const& f : failures) {
651 std::cerr << "rowkey[" << f.original_index() << "]=" << f.status()
652 << "\n";
653 }
654 }
655 // [END bigtable_writes_batch]
656 (std::move(table));
657 }
658
WriteIncrement(google::cloud::bigtable::Table table,std::vector<std::string> const &)659 void WriteIncrement(google::cloud::bigtable::Table table,
660 std::vector<std::string> const&) {
661 // [START bigtable_writes_increment]
662 namespace cbt = google::cloud::bigtable;
663 [](cbt::Table table) {
664 std::string row_key = "phone#4c410523#20190501";
665 std::string column_family = "stats_summary";
666
667 google::cloud::StatusOr<cbt::Row> row = table.ReadModifyWriteRow(
668 row_key, cbt::ReadModifyWriteRule::IncrementAmount(
669 column_family, "connected_wifi", -1));
670
671 if (!row) throw std::runtime_error(row.status().message());
672 std::cout << "Successfully updated row" << row->row_key() << "\n";
673 }
674 // [END bigtable_writes_increment]
675 (std::move(table));
676 }
677
WriteConditionally(google::cloud::bigtable::Table table,std::vector<std::string> const &)678 void WriteConditionally(google::cloud::bigtable::Table table,
679 std::vector<std::string> const&) {
680 // [START bigtable_writes_conditional]
681 namespace cbt = google::cloud::bigtable;
682 [](cbt::Table table) {
683 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
684 std::chrono::system_clock::now().time_since_epoch());
685
686 std::string row_key = "phone#4c410523#20190501";
687 cbt::SingleRowMutation mutation(row_key);
688 std::string column_family = "stats_summary";
689 cbt::Filter predicate = cbt::Filter::Chain(
690 cbt::Filter::ColumnName(column_family, "os_build"),
691 cbt::Filter::Latest(1), cbt::Filter::ValueRegex("PQ2A\\..*"));
692
693 google::cloud::StatusOr<cbt::MutationBranch> branch =
694 table.CheckAndMutateRow(
695 row_key, std::move(predicate),
696 {cbt::SetCell(column_family, "os_name", timestamp, "android")}, {});
697
698 if (!branch) throw std::runtime_error(branch.status().message());
699 if (*branch == cbt::MutationBranch::kPredicateMatched) {
700 std::cout << "Successfully updated row\n";
701 } else {
702 std::cout << "The predicate was not matched\n";
703 }
704 }
705 // [END bigtable_writes_conditional]
706 (std::move(table));
707 }
708
DefaultTablePrefix()709 std::string DefaultTablePrefix() { return "tbl-data-"; }
710
RunMutateExamples(google::cloud::bigtable::TableAdmin admin,google::cloud::internal::DefaultPRNG & generator)711 void RunMutateExamples(google::cloud::bigtable::TableAdmin admin,
712 google::cloud::internal::DefaultPRNG& generator) {
713 namespace examples = ::google::cloud::bigtable::examples;
714 namespace cbt = google::cloud::bigtable;
715
716 auto table_id = examples::RandomTableId(DefaultTablePrefix(), generator);
717 auto schema = admin.CreateTable(
718 table_id,
719 cbt::TableConfig({{"fam", cbt::GcRule::MaxNumVersions(10)}}, {}));
720 if (!schema) throw std::runtime_error(schema.status().message());
721
722 google::cloud::bigtable::Table table(
723 google::cloud::bigtable::CreateDefaultDataClient(
724 admin.project(), admin.instance_id(),
725 google::cloud::bigtable::ClientOptions()),
726 table_id, cbt::AlwaysRetryMutationPolicy());
727
728 std::cout << "Running MutateInsertUpdateRows() example [1]" << std::endl;
729 MutateInsertUpdateRows(table, {"row1", "fam:col1=value1.1",
730 "fam:col2=value1.2", "fam:col3=value1.3"});
731 std::cout << "Running MutateInsertUpdateRows() example [2]" << std::endl;
732 MutateInsertUpdateRows(table, {"row2", "fam:col1=value2.1",
733 "fam:col2=value2.2", "fam:col3=value2.3"});
734
735 admin.DeleteTable(table_id);
736 }
737
RunWriteExamples(google::cloud::bigtable::TableAdmin admin,google::cloud::internal::DefaultPRNG & generator)738 void RunWriteExamples(google::cloud::bigtable::TableAdmin admin,
739 google::cloud::internal::DefaultPRNG& generator) {
740 namespace examples = ::google::cloud::bigtable::examples;
741 namespace cbt = google::cloud::bigtable;
742
743 auto table_id = examples::RandomTableId("mobile-time-series-", generator);
744 auto schema = admin.CreateTable(
745 table_id, cbt::TableConfig(
746 {{"stats_summary", cbt::GcRule::MaxNumVersions(11)}}, {}));
747 if (!schema) throw std::runtime_error(schema.status().message());
748
749 // Some temporary variables to make the snippet below more readable.
750 auto const project_id = admin.project();
751 auto const instance_id = admin.instance_id();
752 google::cloud::bigtable::Table table(
753 google::cloud::bigtable::CreateDefaultDataClient(
754 project_id, instance_id, google::cloud::bigtable::ClientOptions()),
755 table_id, cbt::AlwaysRetryMutationPolicy());
756
757 std::cout << "Running WriteSimple() example" << std::endl;
758 WriteSimple(table, {});
759 std::cout << "Running WriteBatch() example" << std::endl;
760 WriteBatch(table, {});
761 std::cout << "Running WriteIncrement() example" << std::endl;
762 WriteIncrement(table, {});
763 std::cout << "Running WriteConditionally() example" << std::endl;
764 WriteConditionally(table, {});
765
766 admin.DeleteTable(table_id);
767 }
768
RunDataExamples(google::cloud::bigtable::TableAdmin admin,google::cloud::internal::DefaultPRNG & generator)769 void RunDataExamples(google::cloud::bigtable::TableAdmin admin,
770 google::cloud::internal::DefaultPRNG& generator) {
771 namespace examples = ::google::cloud::bigtable::examples;
772 namespace cbt = google::cloud::bigtable;
773
774 auto table_id = examples::RandomTableId(DefaultTablePrefix(), generator);
775 std::cout << "Creating table " << table_id << std::endl;
776 auto schema = admin.CreateTable(
777 table_id,
778 cbt::TableConfig({{"fam", cbt::GcRule::MaxNumVersions(10)}}, {}));
779 if (!schema) throw std::runtime_error(schema.status().message());
780
781 google::cloud::bigtable::Table table(
782 google::cloud::bigtable::CreateDefaultDataClient(
783 admin.project(), admin.instance_id(),
784 google::cloud::bigtable::ClientOptions()),
785 table_id, cbt::AlwaysRetryMutationPolicy());
786
787 std::cout << "\nPreparing data for MutateDeleteColumns()" << std::endl;
788 MutateInsertUpdateRows(
789 table, {"insert-update-01", "fam:col0=value0-0", "fam:col1=value2-0",
790 "fam:col3=value3-0", "fam:col4=value4-0"});
791 std::cout << "Running MutateDeleteColumns() example" << std::endl;
792 MutateDeleteColumns({table.project_id(), table.instance_id(),
793 table.table_id(), "insert-update-01", "fam:col3",
794 "fam:col4"});
795 std::cout << "Running MutateDeleteRows() example [1]" << std::endl;
796 MutateDeleteRows(table, {"insert-update-01"});
797
798 std::cout << "\nPreparing data for MutateDeleteRows()" << std::endl;
799 MutateInsertUpdateRows(
800 table, {"to-delete-01", "fam:col0=value0-0", "fam:col1=value2-0",
801 "fam:col3=value3-0", "fam:col4=value4-0"});
802 MutateInsertUpdateRows(
803 table, {"to-delete-02", "fam:col0=value0-0", "fam:col1=value2-0",
804 "fam:col3=value3-0", "fam:col4=value4-0"});
805 std::cout << "Running MutateDeleteRows() example [2]" << std::endl;
806 MutateDeleteRows(table, {"to-delete-01", "to-delete-02"});
807
808 std::cout << "\nPreparing data for RenameColumn()" << std::endl;
809 MutateInsertUpdateRows(table, {"mix-match-01", "fam:col0=value0-0"});
810 MutateInsertUpdateRows(table, {"mix-match-01", "fam:col0=value0-1"});
811 MutateInsertUpdateRows(table, {"mix-match-01", "fam:col0=value0-2"});
812 std::cout << "Running RenameColumn() example" << std::endl;
813 RenameColumn(table, {"mix-match-01", "fam", "col0", "new-name"});
814
815 std::cout << "\nPreparing data for multiple examples" << std::endl;
816 InsertTestData(table, {});
817 std::cout << "Running Apply() example" << std::endl;
818 Apply(table, {});
819 std::cout << "Running Apply() with relaxed idempotency example" << std::endl;
820 ApplyRelaxedIdempotency(table, {"apply-relaxed-idempotency"});
821 std::cout << "Running Apply() with custom retry example" << std::endl;
822 ApplyCustomRetry(table, {"apply-custom-retry"});
823 std::cout << "Running BulkApply() example" << std::endl;
824 BulkApply(table, {});
825
826 std::cout << "\nPopulate data for prefix and row set examples" << std::endl;
827 PopulateTableHierarchy(table, {});
828
829 std::cout << "Running SampleRows() example" << std::endl;
830 SampleRows(table, {});
831
832 std::cout << "Running RowExists example" << std::endl;
833 RowExists(table, {"root/0/0/1"});
834 std::cout << "Running DeleteAllCells example" << std::endl;
835 DeleteAllCells(table, {"root/0/0/1"});
836 std::cout << "Running DeleteFamilyCells() example" << std::endl;
837 DeleteFamilyCells(table, {"root/0/1/0", "fam"});
838 std::cout << "Running DeleteSelectiveFamilyCells() example" << std::endl;
839 DeleteSelectiveFamilyCells(table, {"root/0/1/0", "fam", "col2"});
840
841 std::cout << "\nPopulating data for CheckAndMutate() example" << std::endl;
842 MutateInsertUpdateRows(table, {"check-and-mutate-row", "fam:flip-flop:on"});
843 MutateInsertUpdateRows(
844 table, {"check-and-mutate-row-not-present", "fam:unused=unused-value"});
845 std::cout << "Running CheckAndMutate() example [1]" << std::endl;
846 CheckAndMutate(table, {"check-and-mutate-row"});
847 std::cout << "Running CheckAndMutate() example [2]" << std::endl;
848 CheckAndMutate(table, {"check-and-mutate-row"});
849 std::cout << "Running CheckAndMutate() example [3]" << std::endl;
850 CheckAndMutateNotPresent(table, {"check-and-mutate-row-not-present"});
851 std::cout << "Running CheckAndMutate() example [4]" << std::endl;
852 MutateInsertUpdateRows(
853 table, {"check-and-mutate-row-not-present", "fam:unused=unused-value"});
854 CheckAndMutateNotPresent(table, {"check-and-mutate-row-not-present"});
855
856 auto random_key_suffix = [&generator] {
857 return google::cloud::internal::Sample(
858 generator, 8, "abcdefghijklmnopqrstuvwxyz0123456789");
859 };
860
861 // TODO(#3523) why do we need to run this twice?
862 std::cout << "\nPopulating data for CheckAndMutate() example" << std::endl;
863 auto read_row_key = "read-row-" + random_key_suffix();
864 ReadRow(table, {read_row_key});
865 MutateInsertUpdateRows(table, {read_row_key, "fam:flip-flop:on"});
866 ReadRow(table, {read_row_key});
867 CheckAndMutate(table, {read_row_key});
868 ReadRow(table, {read_row_key});
869 CheckAndMutate(table, {read_row_key});
870 ReadRow(table, {read_row_key});
871
872 std::cout << "\nPopulating data for ReadModifyWrite() example" << std::endl;
873 auto read_modify_write_key = "read-modify-write-" + random_key_suffix();
874 ReadModifyWrite(table, {read_modify_write_key});
875 ReadRow(table, {read_modify_write_key});
876 ReadModifyWrite(table, {read_modify_write_key});
877 ReadRow(table, {read_modify_write_key});
878 ReadModifyWrite(table, {read_modify_write_key});
879 ReadRow(table, {read_modify_write_key});
880
881 admin.DeleteTable(table_id);
882 }
883
RunAll(std::vector<std::string> const & argv)884 void RunAll(std::vector<std::string> const& argv) {
885 namespace examples = ::google::cloud::bigtable::examples;
886 namespace cbt = google::cloud::bigtable;
887
888 if (!argv.empty()) throw google::cloud::bigtable::examples::Usage{"auto"};
889 examples::CheckEnvironmentVariablesAreSet({
890 "GOOGLE_CLOUD_PROJECT",
891 "GOOGLE_CLOUD_CPP_BIGTABLE_TEST_INSTANCE_ID",
892 });
893 auto const project_id =
894 google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value();
895 auto const instance_id = google::cloud::internal::GetEnv(
896 "GOOGLE_CLOUD_CPP_BIGTABLE_TEST_INSTANCE_ID")
897 .value();
898
899 cbt::TableAdmin admin(
900 cbt::CreateDefaultAdminClient(project_id, cbt::ClientOptions{}),
901 instance_id);
902
903 // If a previous run of these samples crashes before cleaning up there may be
904 // old tables left over. As there are quotas on the total number of tables we
905 // remove stale tables after 48 hours.
906 examples::CleanupOldTables(DefaultTablePrefix(), admin);
907 examples::CleanupOldTables("mobile-time-series-", admin);
908
909 // Initialize a generator with some amount of entropy.
910 auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
911 RunMutateExamples(admin, generator);
912 RunWriteExamples(admin, generator);
913 RunDataExamples(admin, generator);
914 }
915
916 } // anonymous namespace
917
main(int argc,char * argv[])918 int main(int argc, char* argv[]) { // NOLINT(bugprone-exception-escape)
919 google::cloud::testing_util::InstallCrashHandler(argv[0]);
920
921 using google::cloud::bigtable::examples::MakeCommandEntry;
922 google::cloud::bigtable::examples::Commands commands = {
923 MakeCommandEntry("apply", {}, Apply),
924 MakeCommandEntry("apply-relaxed-idempotency", {"<row-key>"},
925 ApplyRelaxedIdempotency),
926 MakeCommandEntry("apply-custom-retry", {"<row-key>"}, ApplyCustomRetry),
927 MakeCommandEntry("bulk-apply", {}, BulkApply),
928 MakeCommandEntry("check-and-mutate", {"<row-key>"}, CheckAndMutate),
929 MakeCommandEntry("check-and-mutate-not-present", {"<row-key>"},
930 CheckAndMutateNotPresent),
931 MakeCommandEntry("read-modify-write", {"<row-key>"}, ReadModifyWrite),
932 MakeCommandEntry("sample-rows", {}, SampleRows),
933 MakeCommandEntry("delete-all-cells", {"<row-key>"}, DeleteAllCells),
934 MakeCommandEntry("delete-family-cells", {"<row-key>", "<family-name>"},
935 DeleteFamilyCells),
936 MakeCommandEntry("delete-selective-family-cells",
937 {"<row-key>", "<family-name>", "<column-name>"},
938 DeleteSelectiveFamilyCells),
939 MakeCommandEntry("row-exists", {"<row-key>"}, RowExists),
940 {"mutate-delete-columns", MutateDeleteColumns},
941 {"mutate-delete-rows", MutateDeleteRowsCommand},
942 {"mutate-insert-update-rows", MutateInsertUpdateRowsCommand},
943 MakeCommandEntry("rename-column",
944 {"<key> <family> <old-name> <new-name>"}, RenameColumn),
945 MakeCommandEntry("insert-test-data", {}, InsertTestData),
946 MakeCommandEntry("populate-table-hierarchy", {}, PopulateTableHierarchy),
947 MakeCommandEntry("write-simple", {}, WriteSimple),
948 MakeCommandEntry("write-batch", {}, WriteBatch),
949 MakeCommandEntry("write-increment", {}, WriteIncrement),
950 MakeCommandEntry("write-conditional", {}, WriteConditionally),
951 {"auto", RunAll},
952 };
953
954 google::cloud::bigtable::examples::Example example(std::move(commands));
955 return example.Run(argc, argv);
956 }
957