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