Google Cloud Bigtable C++ Client  2.5.0
A C++ Client Library for Google Cloud Bigtable
mutations.h
Go to the documentation of this file.
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 // https://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 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_MUTATIONS_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_MUTATIONS_H
17 
18 #include "google/cloud/bigtable/cell.h"
19 #include "google/cloud/bigtable/row_key.h"
20 #include "google/cloud/bigtable/version.h"
21 #include "google/cloud/grpc_error_delegate.h"
22 #include "google/cloud/internal/big_endian.h"
23 #include "google/cloud/status.h"
24 #include "google/cloud/status_or.h"
25 #include "absl/meta/type_traits.h"
26 #include <google/bigtable/v2/bigtable.pb.h>
27 #include <google/bigtable/v2/data.pb.h>
28 #include <google/protobuf/util/message_differencer.h>
29 #include <grpcpp/grpcpp.h>
30 #include <chrono>
31 #include <string>
32 #include <type_traits>
33 #include <vector>
34 
35 namespace google {
36 namespace cloud {
37 namespace bigtable {
39 /**
40  * Represent a single change to a specific row in a Table.
41  *
42  * Mutations come in different forms, they can set a specific cell,
43  * delete a specific cell or delete multiple cells in a row.
44  */
45 struct Mutation {
46  google::bigtable::v2::Mutation op;
47 };
48 
49 /**
50  * A magic value where the server sets the timestamp.
51  *
52  * Notice that using this value in a SetCell() mutation makes it non-idempotent,
53  * and by default the client will not retry such mutations.
54  */
55 constexpr std::int64_t ServerSetTimestamp() { return -1; }
56 
57 /// Create a mutation to set a cell value.
58 template <typename ColumnType, typename ValueType>
59 Mutation SetCell(std::string family, ColumnType&& column,
60  std::chrono::milliseconds timestamp, ValueType&& value) {
61  Mutation m;
62  auto& set_cell = *m.op.mutable_set_cell();
63  set_cell.set_family_name(std::move(family));
64  set_cell.set_column_qualifier(std::forward<ColumnType>(column));
65  set_cell.set_timestamp_micros(
66  std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count());
67  set_cell.set_value(std::forward<ValueType>(value));
68  return m;
69 }
70 
71 /// Create a mutation to store a 64-bit big endian integer value.
72 template <typename ColumnType>
73 Mutation SetCell(std::string family, ColumnType&& column,
74  std::chrono::milliseconds timestamp, std::int64_t value) {
75  Mutation m;
76  auto& set_cell = *m.op.mutable_set_cell();
77  set_cell.set_family_name(std::move(family));
78  set_cell.set_column_qualifier(std::forward<ColumnType>(column));
79  set_cell.set_timestamp_micros(
80  std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count());
81  set_cell.set_value(google::cloud::internal::EncodeBigEndian(value));
82  return m;
83 }
84 
85 /**
86  * Create a mutation to set a cell value where the server sets the time.
87  *
88  * These mutations are not idempotent and not retried by default.
89  */
90 template <typename ColumnType, typename ValueType>
91 Mutation SetCell(std::string family, ColumnType&& column, ValueType&& value) {
92  Mutation m;
93  auto& set_cell = *m.op.mutable_set_cell();
94  set_cell.set_family_name(std::move(family));
95  set_cell.set_column_qualifier(std::forward<ColumnType>(column));
96  set_cell.set_timestamp_micros(ServerSetTimestamp());
97  set_cell.set_value(std::forward<ValueType>(value));
98  return m;
99 }
100 
101 /**
102  * Create a mutation to store a 64-bit big endian integer value.
103  *
104  * @note This mutation is not idempotent, the default policies do not retry
105  * transient failures for this mutation.
106  */
107 template <typename ColumnType>
108 Mutation SetCell(std::string family, ColumnType&& column, std::int64_t value) {
109  Mutation m;
110  auto& set_cell = *m.op.mutable_set_cell();
111  set_cell.set_family_name(std::move(family));
112  set_cell.set_column_qualifier(std::forward<ColumnType>(column));
113  set_cell.set_timestamp_micros(ServerSetTimestamp());
114  set_cell.set_value(google::cloud::internal::EncodeBigEndian(value));
115  return m;
116 }
117 
118 /**
119  * Create a mutation to set a cell value based on a `bigtable::Cell`.
120  *
121  * These mutations are not idempotent and not retried by default.
122  */
123 Mutation SetCell(Cell cell);
124 
125 //@{
126 /**
127  * @name Create mutations to delete a range of cells from a column.
128  *
129  * The following functions create a mutation that deletes all the
130  * cells in the given column family and, column within the given
131  * timestamp in the range.
132  *
133  * The function accepts any instantiation of `std::chrono::duration<>` for the
134  * @p timestamp_begin and @p timestamp_end parameters. For example:
135  *
136  * @code
137  * using namespace std::chrono_literals; // C++14
138  * bigtable::DeleteFromColumn("fam", "col", 0us, 10us)
139  * @endcode
140  *
141  * The ending timestamp is exclusive, while the beginning timestamp is
142  * inclusive. That is, the interval is [@p timestamp_begin, @p timestamp_end).
143  * The value 0 is special and treated as "unbounded" for both the begin and
144  * end endpoints of the time range. The Cloud Bigtable server rejects
145  * invalid and empty ranges, i.e., any range where the endpoint is smaller or
146  * equal than to the initial endpoint unless either endpoint is 0.
147  *
148  * @tparam Rep1 a placeholder to match the Rep tparam for @p timestamp_begin
149  * type. The semantics of this template parameter are documented in
150  * std::chrono::duration<>` (in brief, the underlying arithmetic type
151  * used to store the number of ticks), for our purposes it is simply a
152  * formal parameter.
153  *
154  * @tparam Rep2 similar formal parameter for the type of @p timestamp_end.
155  *
156  * @tparam Period1 a placeholder to match the Period tparam for
157  * @p timestamp_begin type. The semantics of this template parameter are
158  * documented in `std::chrono::duration<>` (in brief, the length of the tick
159  * in seconds,vexpressed as a `std::ratio<>`), for our purposes it is simply
160  * a formal parameter.
161  *
162  * @tparam Period2 similar formal parameter for the type of @p timestamp_end.
163  *
164  * @tparam ColumnType the type of the column qualifier. It should satisfy
165  * std::is_constructible<ColumnQualifierType, ColumnType>.
166  */
167 template <typename Rep1, typename Period1, typename Rep2, typename Period2,
168  typename ColumnType>
169 Mutation DeleteFromColumn(std::string family, ColumnType&& column,
170  std::chrono::duration<Rep1, Period1> timestamp_begin,
171  std::chrono::duration<Rep2, Period2> timestamp_end) {
172  Mutation m;
173  auto& d = *m.op.mutable_delete_from_column();
174  d.set_family_name(std::move(family));
175  d.set_column_qualifier(std::forward<ColumnType>(column));
176  d.mutable_time_range()->set_start_timestamp_micros(
177  std::chrono::duration_cast<std::chrono::microseconds>(timestamp_begin)
178  .count());
179  d.mutable_time_range()->set_end_timestamp_micros(
180  std::chrono::duration_cast<std::chrono::microseconds>(timestamp_end)
181  .count());
182  return m;
183 }
184 
185 //@{
186 /**
187  * @name The following functions create a mutation that deletes all the
188  * cells in the given column family and column, starting from and
189  * including, @a timestamp_begin.
190  *
191  * The function accepts any instantiation of `std::chrono::duration<>` for the
192  * @p timestamp_begin For example:
193  *
194  * @code
195  * using namespace std::chrono_literals; // C++14
196  * bigtable::DeleteFromColumn("fam", "col", 10us)
197  * @endcode
198  *
199  * @tparam Rep1 a placeholder to match the Rep tparam for @p timestamp_begin
200  * type. The semantics of this template parameter are documented in
201  * `std::chrono::duration<>` (in brief, the underlying arithmetic type
202  * used to store the number of ticks), for our purposes it is simply a
203  * formal parameter.
204  *
205  * @tparam Period1 a placeholder to match the Period tparam for @p
206  * timestamp_begin type. The semantics of this template parameter
207  * are documented in `std::chrono::duration<>` (in brief, the length
208  * of the tick in seconds, expressed as a `std::ratio<>`), for our
209  * purposes it is simply a formal parameter.
210  *
211  * @tparam ColumnType the type of the column qualifier. It should satisfy
212  * std::is_constructible<ColumnQualifierType, ColumnType>.
213  */
214 template <typename Rep1, typename Period1, typename ColumnType>
216  std::string family, ColumnType&& column,
217  std::chrono::duration<Rep1, Period1> timestamp_begin) {
218  Mutation m;
219  auto& d = *m.op.mutable_delete_from_column();
220  d.set_family_name(std::move(family));
221  d.set_column_qualifier(std::forward<ColumnType>(column));
222  d.mutable_time_range()->set_start_timestamp_micros(
223  std::chrono::duration_cast<std::chrono::microseconds>(timestamp_begin)
224  .count());
225  return m;
226 }
227 
228 //@{
229 /**
230  * @name The following functions create a mutation that deletes all the
231  * cells in the given column family and column, Delete up to @a timestamp_end,
232  * but excluding, @a timestamp_end.
233  *
234  * The function accepts any instantiation of `std::chrono::duration<>` for the
235  * @p timestamp_end For example:
236  *
237  * @code
238  * using namespace std::chrono_literals; // C++14
239  * bigtable::DeleteFromColumn("fam", "col", 10us)
240  * @endcode
241  *
242  * @tparam Rep2 a placeholder to match the Rep tparam for @p timestamp_end type.
243  * The semantics of this template parameter are documented in
244  * `std::chrono::duration<>` (in brief, the underlying arithmetic type
245  * used to store the number of ticks), for our purposes it is simply a
246  * formal parameter.
247  *
248  * @tparam Period2 a placeholder to match the Period tparam for @p timestamp_end
249  * type. The semantics of this template parameter are documented in
250  * `std::chrono::duration<>` (in brief, the length of the tick in seconds,
251  * expressed as a `std::ratio<>`), for our purposes it is simply a formal
252  * parameter.
253  *
254  * @tparam ColumnType the type of the column qualifier. It should satisfy
255  * std::is_constructible<ColumnQualifierType, ColumnType>.
256  */
257 template <typename Rep2, typename Period2, typename ColumnType>
259  std::string family, ColumnType&& column,
260  std::chrono::duration<Rep2, Period2> timestamp_end) {
261  Mutation m;
262  auto& d = *m.op.mutable_delete_from_column();
263  d.set_family_name(std::move(family));
264  d.set_column_qualifier(std::forward<ColumnType>(column));
265  d.mutable_time_range()->set_end_timestamp_micros(
266  std::chrono::duration_cast<std::chrono::microseconds>(timestamp_end)
267  .count());
268  return m;
269 }
270 
271 /// Delete all the values for the column.
272 template <typename ColumnType>
273 Mutation DeleteFromColumn(std::string family, ColumnType&& column) {
274  Mutation m;
275  auto& d = *m.op.mutable_delete_from_column();
276  d.set_family_name(std::move(family));
277  d.set_column_qualifier(std::forward<ColumnType>(column));
278  return m;
279 }
280 //@}
281 
282 /// Create a mutation to delete all the cells in a column family.
283 Mutation DeleteFromFamily(std::string family);
284 
285 /// Create a mutation to delete all the cells in a row.
287 
288 /**
289  * Represent a single row mutation.
290  *
291  * Bigtable can perform multiple changes to a single row atomically.
292  * This class represents 0 or more changes to apply to a single row.
293  * The changes may include setting cells (which implicitly insert the
294  * values), deleting values, etc.
295  */
297  public:
298  /// Create an empty mutation.
299  template <
300  typename RowKey,
301  typename std::enable_if<std::is_constructible<RowKeyType, RowKey>::value,
302  int>::type = 0>
303  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
304  explicit SingleRowMutation(RowKey&& row_key) {
305  request_.set_row_key(RowKeyType(std::forward<RowKey>(row_key)));
306  }
307 
308  /// Create a row mutation from a initializer list.
309  template <typename RowKey>
310  SingleRowMutation(RowKey&& row_key, std::initializer_list<Mutation> list) {
311  request_.set_row_key(std::forward<RowKey>(row_key));
312  for (auto&& i : list) {
313  *request_.add_mutations() = i.op;
314  }
315  }
316 
317  /// Create a single-row multiple-cell mutation from a variadic list.
318  template <
319  typename RowKey, typename... M,
320  typename std::enable_if<std::is_constructible<RowKeyType, RowKey>::value,
321  int>::type = 0>
322  explicit SingleRowMutation(RowKey&& row_key, M&&... m) {
323  static_assert(
324  absl::conjunction<std::is_convertible<M, Mutation>...>::value,
325  "The arguments passed to SingleRowMutation(std::string, ...) must be "
326  "convertible to Mutation");
327  request_.set_row_key(std::forward<RowKey>(row_key));
328  emplace_many(std::forward<M>(m)...);
329  }
330 
331  /// Create a row mutation from gRPC proto
333  ::google::bigtable::v2::MutateRowsRequest::Entry entry) {
334  using std::swap;
335  swap(*request_.mutable_row_key(), *entry.mutable_row_key());
336  swap(*request_.mutable_mutations(), *entry.mutable_mutations());
337  }
338 
339  /// Create a row mutation from gRPC proto
340  explicit SingleRowMutation(::google::bigtable::v2::MutateRowRequest request)
341  : request_(std::move(request)) {}
342 
343  // Add a mutation at the end.
345  *request_.add_mutations() = std::move(mut.op);
346  return *this;
347  }
348 
349  // Get the row key.
350  RowKeyType const& row_key() const { return request_.row_key(); }
351 
352  friend class Table;
353 
358 
359  friend bool operator==(SingleRowMutation const& a,
360  SingleRowMutation const& b) noexcept {
361  return google::protobuf::util::MessageDifferencer::Equivalent(a.request_,
362  b.request_);
363  }
364  friend bool operator!=(SingleRowMutation const& a,
365  SingleRowMutation const& b) noexcept {
366  return !(a == b);
367  }
368 
369  /// Move the contents into a bigtable::v2::MutateRowsRequest::Entry.
370  void MoveTo(google::bigtable::v2::MutateRowsRequest::Entry* entry) {
371  entry->set_row_key(std::move(*request_.mutable_row_key()));
372  *entry->mutable_mutations() = std::move(*request_.mutable_mutations());
373  }
374 
375  /// Transfer the contents to @p request.
376  void MoveTo(google::bigtable::v2::MutateRowRequest& request) {
377  request.set_row_key(std::move(*request_.mutable_row_key()));
378  *request.mutable_mutations() = std::move(*request_.mutable_mutations());
379  }
380 
381  /// Remove the contents of the mutation.
382  void Clear() { request_.Clear(); }
383 
384  private:
385  /// Add multiple mutations to single row
386  template <typename... M>
387  void emplace_many(Mutation first, M&&... tail) {
388  emplace_back(std::move(first));
389  emplace_many(std::forward<M>(tail)...);
390  }
391 
392  void emplace_many(Mutation m) { emplace_back(std::move(m)); }
393 
394  ::google::bigtable::v2::MutateRowRequest request_;
395 };
396 
397 /**
398  * A SingleRowMutation that failed.
399  *
400  * A multi-row mutation returns the list of operations that failed,
401  * this class encapsulates both the failure and the original
402  * mutation. The application can then choose to resend the mutation,
403  * or log it, or save it for processing via some other means.
404  */
406  public:
407  FailedMutation(google::cloud::Status status, int index)
408  : status_(std::move(status)), original_index_(index) {}
409 
410  FailedMutation(google::rpc::Status const& status, int index)
411  : status_(MakeStatusFromRpcError(status)), original_index_(index) {}
412 
415  FailedMutation(FailedMutation const&) = default;
417 
418  friend bool operator==(FailedMutation const& a,
419  FailedMutation const& b) noexcept {
420  return a.status_ == b.status_ && a.original_index_ == b.original_index_;
421  }
422  friend bool operator!=(FailedMutation const& a,
423  FailedMutation const& b) noexcept {
424  return !(a == b);
425  }
426 
427  //@{
428  /// @name accessors
429  google::cloud::Status const& status() const { return status_; }
430  int original_index() const { return original_index_; }
431  //@}
432 
433  friend class BulkMutation;
434 
435  private:
436  google::cloud::Status status_;
437  int original_index_;
438 };
439 
440 /**
441  * Report unrecoverable errors in a partially completed mutation.
442  */
443 class PermanentMutationFailure : public std::runtime_error {
444  public:
445  PermanentMutationFailure(char const* msg,
446  std::vector<FailedMutation> failures)
447  : std::runtime_error(msg), failures_(std::move(failures)) {}
448 
449  PermanentMutationFailure(char const* msg, grpc::Status status,
450  std::vector<FailedMutation> failures)
451  : std::runtime_error(msg),
452  failures_(std::move(failures)),
453  status_(std::move(status)) {}
454 
455  /**
456  * The details of each mutation failure.
457  *
458  * Because BulkApply() and Apply() take ownership of the data in the mutations
459  * the failures are returned with their full contents, in case the application
460  * wants to take further action with them. Any successful mutations are
461  * discarded.
462  *
463  * Any mutations that fail with an unknown state are included with a
464  * `grpc::StatusCode::OK`.
465  */
466  std::vector<FailedMutation> const& failures() const { return failures_; }
467 
468  /**
469  * The `grpc::Status` of the request.
470  *
471  * Notice that it can return `grpc::Status::OK` when there are partial
472  * failures in a `BulkApply()` operation.
473  */
474  grpc::Status const& status() const { return status_; }
475 
476  private:
477  std::vector<FailedMutation> failures_;
478  grpc::Status status_;
479 };
480 
481 /**
482  * Represent a set of mutations across multiple rows.
483  *
484  * Cloud Bigtable can batch multiple mutations in a single request.
485  * The mutations are not atomic, but it is more efficient to send them
486  * in a batch than to make multiple smaller requests.
487  */
489  public:
490  /// Create an empty set of mutations.
491  BulkMutation() = default;
492 
493  /// Create a multi-row mutation from a range of SingleRowMutations.
494  template <typename Iterator>
495  BulkMutation(Iterator begin, Iterator end) {
496  static_assert(
497  std::is_convertible<decltype(*begin), SingleRowMutation>::value,
498  "The iterator value type must be convertible to SingleRowMutation");
499  for (auto i = begin; i != end; ++i) {
500  push_back(*i);
501  }
502  }
503 
504  /// Create a multi-row mutation from a initializer list.
505  BulkMutation(std::initializer_list<SingleRowMutation> list)
506  : BulkMutation(list.begin(), list.end()) {}
507 
508  /// Create a multi-row mutation from a SingleRowMutation
510  emplace_back(std::move(mutation));
511  }
512 
513  /// Create a multi-row mutation from two SingleRowMutation
515  emplace_back(std::move(m1));
516  emplace_back(std::move(m2));
517  }
518 
519  /// Create a multi-row mutation from a variadic list.
520  template <typename... M,
521  typename std::enable_if<absl::conjunction<std::is_convertible<
522  M, SingleRowMutation>...>::value,
523  int>::type = 0>
524  // NOLINTNEXTLINE(google-explicit-constructor)
525  BulkMutation(M&&... m) : BulkMutation() {
526  emplace_many(std::forward<M>(m)...);
527  }
528 
529  // Add a mutation to the batch.
531  mut.MoveTo(request_.add_entries());
532  return *this;
533  }
534 
535  // Add a failed mutation to the batch.
537  fm.status_ = google::cloud::Status();
538  return *this;
539  }
540 
541  // Add a mutation to the batch.
543  mut.MoveTo(request_.add_entries());
544  return *this;
545  }
546 
547  /// Move the contents into a bigtable::v2::MutateRowsRequest
548  void MoveTo(google::bigtable::v2::MutateRowsRequest* request) {
549  request_.Swap(request);
550  request_ = {};
551  }
552 
553  /// Return true if there are no mutations in this set.
554  bool empty() const { return request_.entries().empty(); }
555 
556  /// Return the number of mutations in this set.
557  std::size_t size() const { return request_.entries().size(); }
558 
559  /// Return the estimated size in bytes of all the mutations in this set.
560  std::size_t estimated_size_in_bytes() const {
561  return request_.ByteSizeLong();
562  }
563 
564  private:
565  template <typename... M>
566  void emplace_many(SingleRowMutation first, M&&... tail) {
567  emplace_back(std::move(first));
568  emplace_many(std::forward<M>(tail)...);
569  }
570 
571  void emplace_many(SingleRowMutation m) { emplace_back(std::move(m)); }
572 
573  google::bigtable::v2::MutateRowsRequest request_;
574 };
575 
577 } // namespace bigtable
578 } // namespace cloud
579 } // namespace google
580 
581 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_MUTATIONS_H