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