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_TABLE_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_TABLE_H
17 
18 #include "google/cloud/bigtable/async_row_reader.h"
19 #include "google/cloud/bigtable/completion_queue.h"
20 #include "google/cloud/bigtable/data_client.h"
21 #include "google/cloud/bigtable/filters.h"
22 #include "google/cloud/bigtable/idempotent_mutation_policy.h"
23 #include "google/cloud/bigtable/mutations.h"
24 #include "google/cloud/bigtable/read_modify_write_rule.h"
25 #include "google/cloud/bigtable/row_key_sample.h"
26 #include "google/cloud/bigtable/row_reader.h"
27 #include "google/cloud/bigtable/row_set.h"
28 #include "google/cloud/bigtable/rpc_backoff_policy.h"
29 #include "google/cloud/bigtable/rpc_retry_policy.h"
30 #include "google/cloud/bigtable/version.h"
31 #include "google/cloud/future.h"
32 #include "google/cloud/grpc_error_delegate.h"
33 #include "google/cloud/status.h"
34 #include "google/cloud/status_or.h"
35 #include "absl/meta/type_traits.h"
36 
37 namespace google {
38 namespace cloud {
39 namespace bigtable {
40 inline namespace BIGTABLE_CLIENT_NS {
41 /// The branch taken by a Table::CheckAndMutateRow operation.
42 enum class MutationBranch {
43   /// The predicate provided to CheckAndMutateRow did not match and the
44   /// `false_mutations` (if any) were applied.
45   kPredicateNotMatched,
46   /// The predicate provided to CheckAndMutateRow matched and the
47   /// `true_mutations` (if any) were applied.
48   kPredicateMatched,
49 };
50 
51 class MutationBatcher;
52 
53 /**
54  * Return the full table name.
55  *
56  * The full table name is:
57  *
58  * `projects/<PROJECT_ID>/instances/<INSTANCE_ID>/tables/<table_id>`
59  *
60  * Where the project id and instance id come from the @p client parameter.
61  */
TableName(std::shared_ptr<DataClient> const & client,std::string const & table_id)62 inline std::string TableName(std::shared_ptr<DataClient> const& client,
63                              std::string const& table_id) {
64   return InstanceName(client) + "/tables/" + table_id;
65 }
66 
67 /**
68  * The main interface to interact with data in a Cloud Bigtable table.
69  *
70  * This class provides member functions to:
71  * - read specific rows: `Table::ReadRow()`
72  * - scan a ranges of rows: `Table::ReadRows()`
73  * - update or create a single row: `Table::Apply()`
74  * - update or modify multiple rows: `Table::BulkApply()`
75  * - update a row based on previous values: `Table::CheckAndMutateRow()`
76  * - to atomically append data and/or increment multiple values in a row:
77  *   `Table::ReadModifyWriteRow()`
78  * - to sample the row keys: `Table::SampleRows()`.
79  *
80  * The class deals with the most common transient failures, and retries the
81  * underlying RPC calls subject to the policies configured by the application.
82  * These policies are documented in `Table::Table()`.
83  *
84  * @par Thread-safety
85  * Instances of this class created via copy-construction or copy-assignment
86  * share the underlying pool of connections. Access to these copies via multiple
87  * threads is guaranteed to work. Two threads operating on the same instance of
88  * this class is not guaranteed to work.
89  *
90  * @par Cost
91  * Creating a new object of type `Table` is comparable to creating a few objects
92  * of type `std::string` or a few objects of type `std::shared_ptr<int>`. The
93  * class represents a shallow handle to a remote object.
94  *
95  * @par Error Handling
96  * This class uses `StatusOr<T>` to report errors. When an operation fails to
97  * perform its work the returned `StatusOr<T>` contains the error details. If
98  * the `ok()` member function in the `StatusOr<T>` returns `true` then it
99  * contains the expected result. Operations that do not return a value simply
100  * return a `google::cloud::Status` indicating success or the details of the
101  * error Please consult the
102  * [`StatusOr<T>` documentation](#google::cloud::v1::StatusOr) for more details.
103  *
104  * @code
105  * namespace cbt = google::cloud::bigtable;
106  * cbt::Table = ...;
107  * google::cloud::StatusOr<std::pair<bool, cbt::Row>> row = table.ReadRow(...);
108  *
109  * if (!row) {
110  *   std::cerr << "Error reading row\n";
111  *   return;
112  * }
113  *
114  * // Use "row" as a smart pointer here, e.g.:
115  * if (!row->first) {
116  *   std::cout << "Contacting the server was successful, but the row does not"
117  *             << " exist\n";
118  *   return;
119  * }
120  * std::cout << "The row has " << row->second.cells().size() << " cells\n";
121  * @endcode
122  *
123  * In addition, the @ref index "main page" contains examples using `StatusOr<T>`
124  * to handle errors.
125  *
126  * @par Retry, Backoff, and Idempotency Policies
127  * The library automatically retries requests that fail with transient errors,
128  * and uses [truncated exponential backoff][backoff-link] to backoff between
129  * retries. The default policies are to continue retrying for up to 10 minutes.
130  * On each transient failure the backoff period is doubled, starting with an
131  * initial backoff of 100 milliseconds. The backoff period growth is truncated
132  * at 60 seconds. The default idempotency policy is to only retry idempotent
133  * operations. Note that most operations that change state are **not**
134  * idempotent.
135  *
136  * The application can override these policies when constructing objects of this
137  * class. The documentation for the constructors show examples of this in
138  * action.
139  *
140  * [backoff-link]: https://cloud.google.com/storage/docs/exponential-backoff
141  *
142  * @see https://cloud.google.com/bigtable/ for an overview of Cloud Bigtable.
143  *
144  * @see https://cloud.google.com/bigtable/docs/overview for an overview of the
145  *     Cloud Bigtable data model.
146  *
147  * @see https://cloud.google.com/bigtable/docs/instances-clusters-nodes for an
148  *     introduction of the main APIs into Cloud Bigtable.
149  *
150  * @see https://cloud.google.com/bigtable/docs/reference/service-apis-overview
151  *     for an overview of the underlying Cloud Bigtable API.
152  *
153  * @see #google::cloud::v1::StatusOr for a description of the error reporting
154  *     class used by this library.
155  *
156  * @see `LimitedTimeRetryPolicy` and `LimitedErrorCountRetryPolicy` for
157  *     alternative retry policies.
158  *
159  * @see `ExponentialBackoffPolicy` to configure different parameters for the
160  *     exponential backoff policy.
161  *
162  * @see `SafeIdempotentMutationPolicy` and `AlwaysRetryMutationPolicy` for
163  *     alternative idempotency policies.
164  */
165 class Table {
166  private:
167   // We need to eliminate some function overloads from resolution, and that
168   // requires a bit of infrastructure in the private section.
169 
170   /// A meta function to check if @p P is a valid Policy type.
171   template <typename P>
172   struct ValidPolicy
173       : absl::disjunction<std::is_base_of<RPCBackoffPolicy, P>,
174                           std::is_base_of<RPCRetryPolicy, P>,
175                           std::is_base_of<IdempotentMutationPolicy, P>> {};
176 
177   /// A meta function to check if all the @p Policies are valid policy types.
178   template <typename... Policies>
179   struct ValidPolicies : absl::conjunction<ValidPolicy<Policies>...> {};
180 
181  public:
182   /**
183    * Constructor with default policies.
184    *
185    * @param client how to communicate with Cloud Bigtable, including
186    *     credentials, the project id, and the instance id.
187    * @param table_id the table id within the instance defined by client.  The
188    *     full table name is `client->instance_name() + '/tables/' + table_id`.
189    */
Table(std::shared_ptr<DataClient> client,std::string const & table_id)190   Table(std::shared_ptr<DataClient> client, std::string const& table_id)
191       : Table(std::move(client), std::string{}, table_id) {}
192 
193   /**
194    * Constructor with default policies.
195    *
196    * @param client how to communicate with Cloud Bigtable, including
197    *     credentials, the project id, and the instance id.
198    * @param app_profile_id the app_profile_id needed for using the replication
199    * API.
200    * @param table_id the table id within the instance defined by client.  The
201    *     full table name is `client->instance_name() + '/tables/' + table_id`.
202    *
203    * @par Example
204    * @snippet bigtable_hello_app_profile.cc cbt namespace
205    *
206    * @par Example Using AppProfile
207    * @snippet bigtable_hello_app_profile.cc read with app profile
208    */
Table(std::shared_ptr<DataClient> client,std::string app_profile_id,std::string const & table_id)209   Table(std::shared_ptr<DataClient> client, std::string app_profile_id,
210         std::string const& table_id)
211       : client_(std::move(client)),
212         app_profile_id_(std::move(app_profile_id)),
213         table_name_(TableName(client_, table_id)),
214         table_id_(table_id),
215         rpc_retry_policy_prototype_(
216             bigtable::DefaultRPCRetryPolicy(internal::kBigtableLimits)),
217         rpc_backoff_policy_prototype_(
218             bigtable::DefaultRPCBackoffPolicy(internal::kBigtableLimits)),
219         metadata_update_policy_(
220             MetadataUpdatePolicy(table_name_, MetadataParamTypes::TABLE_NAME)),
221         idempotent_mutation_policy_(
222             bigtable::DefaultIdempotentMutationPolicy()) {}
223 
224   /**
225    * Constructor with explicit policies.
226    *
227    * The policies are passed by value, because this makes it easy for
228    * applications to create them.
229    *
230    * @par Example
231    * @code
232    * using namespace std::chrono_literals; // assuming C++14.
233    * auto client = bigtable::CreateDefaultClient(...); // details ommitted
234    * bigtable::Table table(client, "my-table",
235    *                       // Allow up to 20 minutes to retry operations
236    *                       bigtable::LimitedTimeRetryPolicy(20min),
237    *                       // Start with 50 milliseconds backoff, grow
238    *                       // exponentially to 5 minutes.
239    *                       bigtable::ExponentialBackoffPolicy(50ms, 5min),
240    *                       // Only retry idempotent mutations.
241    *                       bigtable::SafeIdempotentMutationPolicy());
242    * @endcode
243    *
244    * @param client how to communicate with Cloud Bigtable, including
245    *     credentials, the project id, and the instance id.
246    * @param table_id the table id within the instance defined by client.  The
247    *     full table name is `client->instance_name() + "/tables/" + table_id`.
248    * @param policies the set of policy overrides for this object.
249    * @tparam Policies the types of the policies to override, the types must
250    *     derive from one of the following types:
251    *
252    *     - `IdempotentMutationPolicy` which mutations are retried. Use
253    *       `SafeIdempotentMutationPolicy` to only retry idempotent operations,
254    *       use `AlwaysRetryMutationPolicy` to retry all operations. Read the
255    *       caveats in the class definition to understand the downsides of the
256    *       latter. You can also create your own policies that decide which
257    *       mutations to retry.
258    *     - `RPCBackoffPolicy` how to backoff from a failed RPC. Currently only
259    *       `ExponentialBackoffPolicy` is implemented. You can also create your
260    *       own policies that backoff using a different algorithm.
261    *     - `RPCRetryPolicy` for how long to retry failed RPCs. Use
262    *       `LimitedErrorCountRetryPolicy` to limit the number of failures
263    *       allowed. Use `LimitedTimeRetryPolicy` to bound the time for any
264    *       request. You can also create your own policies that combine time and
265    *       error counts.
266    *
267    * @see SafeIdempotentMutationPolicy, AlwaysRetryMutationPolicy,
268    *     ExponentialBackoffPolicy, LimitedErrorCountRetryPolicy,
269    *     LimitedTimeRetryPolicy.
270    *
271    * @par Idempotency Policy Example
272    * @snippet data_snippets.cc apply relaxed idempotency
273    *
274    * @par Modified Retry Policy Example
275    * @snippet data_snippets.cc apply custom retry
276    */
277   template <
278       typename... Policies,
279       typename std::enable_if<ValidPolicies<Policies...>::value, int>::type = 0>
280   // NOLINTNEXTLINE(performance-unnecessary-value-param) TODO(#4112)
Table(std::shared_ptr<DataClient> client,std::string const & table_id,Policies &&...policies)281   Table(std::shared_ptr<DataClient> client, std::string const& table_id,
282         Policies&&... policies)
283       : Table(std::move(client), table_id) {
284     ChangePolicies(std::forward<Policies>(policies)...);
285   }
286 
287   /**
288    * Constructor with explicit policies.
289    *
290    * The policies are passed by value, because this makes it easy for
291    * applications to create them.
292    *
293    * @par Example
294    * @code
295    * using namespace std::chrono_literals; // assuming C++14.
296    * auto client = bigtable::CreateDefaultClient(...); // details ommitted
297    * bigtable::Table table(client, "app_id", "my-table",
298    *                       // Allow up to 20 minutes to retry operations
299    *                       bigtable::LimitedTimeRetryPolicy(20min),
300    *                       // Start with 50 milliseconds backoff, grow
301    *                       // exponentially to 5 minutes.
302    *                       bigtable::ExponentialBackoffPolicy(50ms, 5min),
303    *                       // Only retry idempotent mutations.
304    *                       bigtable::SafeIdempotentMutationPolicy());
305    * @endcode
306    *
307    * @param client how to communicate with Cloud Bigtable, including
308    *     credentials, the project id, and the instance id.
309    * @param app_profile_id the app_profile_id needed for using the replication
310    * API.
311    * @param table_id the table id within the instance defined by client.  The
312    *     full table name is `client->instance_name() + "/tables/" + table_id`.
313    * @param policies the set of policy overrides for this object.
314    * @tparam Policies the types of the policies to override, the types must
315    *     derive from one of the following types:
316    *     - `IdempotentMutationPolicy` which mutations are retried. Use
317    *       `SafeIdempotentMutationPolicy` to only retry idempotent operations,
318    *       use `AlwaysRetryMutationPolicy` to retry all operations. Read the
319    *       caveats in the class definition to understand the downsides of the
320    *       latter. You can also create your own policies that decide which
321    *       mutations to retry.
322    *     - `RPCBackoffPolicy` how to backoff from a failed RPC. Currently only
323    *       `ExponentialBackoffPolicy` is implemented. You can also create your
324    *       own policies that backoff using a different algorithm.
325    *     - `RPCRetryPolicy` for how long to retry failed RPCs. Use
326    *       `LimitedErrorCountRetryPolicy` to limit the number of failures
327    *       allowed. Use `LimitedTimeRetryPolicy` to bound the time for any
328    *       request. You can also create your own policies that combine time and
329    *       error counts.
330    *
331    * @see SafeIdempotentMutationPolicy, AlwaysRetryMutationPolicy,
332    *     ExponentialBackoffPolicy, LimitedErrorCountRetryPolicy,
333    *     LimitedTimeRetryPolicy.
334    *
335    * @par Idempotency Policy Example
336    * @snippet data_snippets.cc apply relaxed idempotency
337    *
338    * @par Modified Retry Policy Example
339    * @snippet data_snippets.cc apply custom retry
340    */
341   template <
342       typename... Policies,
343       typename std::enable_if<ValidPolicies<Policies...>::value, int>::type = 0>
344   // NOLINTNEXTLINE(performance-unnecessary-value-param) TODO(#4112)
Table(std::shared_ptr<DataClient> client,std::string app_profile_id,std::string const & table_id,Policies &&...policies)345   Table(std::shared_ptr<DataClient> client, std::string app_profile_id,
346         std::string const& table_id, Policies&&... policies)
347       : Table(std::move(client), std::move(app_profile_id), table_id) {
348     ChangePolicies(std::forward<Policies>(policies)...);
349   }
350 
table_name()351   std::string const& table_name() const { return table_name_; }
app_profile_id()352   std::string const& app_profile_id() const { return app_profile_id_; }
project_id()353   std::string const& project_id() const { return client_->project_id(); }
instance_id()354   std::string const& instance_id() const { return client_->instance_id(); }
table_id()355   std::string const& table_id() const { return table_id_; }
356 
357   /**
358    * Attempts to apply the mutation to a row.
359    *
360    * @param mut the mutation. Note that this function takes ownership (and
361    *     then discards) the data in the mutation.  In general, a
362    *     `SingleRowMutation` can be used to modify and/or delete multiple cells,
363    *     across different columns and column families.
364    *
365    * @return status of the operation.
366    *
367    * @par Idempotency
368    * This operation is idempotent if the provided mutations are idempotent. Note
369    * that `google::cloud::bigtable::SetCell()` without an explicit timestamp is
370    * **not** an idempotent operation.
371    *
372    * @par Example
373    * @snippet data_snippets.cc apply
374    */
375   Status Apply(SingleRowMutation mut);
376 
377   /**
378    * Makes asynchronous attempts to apply the mutation to a row.
379    *
380    * @warning This is an early version of the asynchronous APIs for Cloud
381    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
382    *     is not subject to any SLA or deprecation policy.
383    *
384    * @param mut the mutation. Note that this function takes ownership
385    * (and then discards) the data in the mutation.  In general, a
386    *     `SingleRowMutation` can be used to modify and/or delete
387    * multiple cells, across different columns and column families.
388    * @param cq the completion queue that will execute the asynchronous
389    *    calls, the application must ensure that one or more threads are
390    *    blocked on `cq.Run()`.
391    *
392    * @par Idempotency
393    * This operation is idempotent if the provided mutations are idempotent. Note
394    * that `google::cloud::bigtable::SetCell()` without an explicit timestamp is
395    * **not** an idempotent operation.
396    *
397    * @par Example
398    * @snippet data_async_snippets.cc async-apply
399    */
400 
401   future<Status> AsyncApply(SingleRowMutation mut, CompletionQueue& cq);
402 
403   /**
404    * Attempts to apply mutations to multiple rows.
405    *
406    * @param mut the mutations, note that this function takes
407    *     ownership (and then discards) the data in the mutation. In general, a
408    *     `BulkMutation` can modify multiple rows, and the modifications for each
409    *     row can change (or create) multiple cells, across different columns and
410    *     column families.
411    *
412    * @par Idempotency
413    * This operation is idempotent if the provided mutations are idempotent. Note
414    * that `google::cloud::bigtable::SetCell()` without an explicit timestamp is
415    * **not** an idempotent operation.
416    *
417    * @par Example
418    * @snippet data_snippets.cc bulk apply
419    */
420   std::vector<FailedMutation> BulkApply(BulkMutation mut);
421 
422   /**
423    * Makes asynchronous attempts to apply mutations to multiple rows.
424    *
425    * @warning This is an early version of the asynchronous APIs for Cloud
426    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
427    *     is not subject to any SLA or deprecation policy.
428    *
429    * @param mut the mutations, note that this function takes
430    *     ownership (and then discards) the data in the mutation. In general, a
431    *     `BulkMutation` can modify multiple rows, and the modifications for each
432    *     row can change (or create) multiple cells, across different columns and
433    *     column families.
434    * @param cq the completion queue that will execute the asynchronous calls,
435    *     the application must ensure that one or more threads are blocked on
436    *     `cq.Run()`.
437    *
438    * @par Idempotency
439    * This operation is idempotent if the provided mutations are idempotent. Note
440    * that `google::cloud::bigtable::SetCell()` without an explicit timestamp is
441    * **not** an idempotent operation.
442    *
443    * @par Example
444    * @snippet data_async_snippets.cc bulk async-bulk-apply
445    */
446   future<std::vector<FailedMutation>> AsyncBulkApply(BulkMutation mut,
447                                                      CompletionQueue& cq);
448 
449   /**
450    * Reads a set of rows from the table.
451    *
452    * @param row_set the rows to read from.
453    * @param filter is applied on the server-side to data in the rows.
454    *
455    * @par Idempotency
456    * This is a read-only operation and therefore it is always idempotent.
457    *
458    * @par Example
459    * @snippet read_snippets.cc read rows
460    */
461   RowReader ReadRows(RowSet row_set, Filter filter);
462 
463   /**
464    * Reads a limited set of rows from the table.
465    *
466    * @param row_set the rows to read from.
467    * @param rows_limit the maximum number of rows to read. Cannot be a negative
468    *     number or zero. Use `ReadRows(RowSet, Filter)` to read all matching
469    *     rows.
470    * @param filter is applied on the server-side to data in the rows.
471    *
472    * @par Idempotency
473    * This is a read-only operation and therefore it is always idempotent.
474    *
475    * @par Example
476    * @snippet read_snippets.cc read rows with limit
477    */
478   RowReader ReadRows(RowSet row_set, std::int64_t rows_limit, Filter filter);
479 
480   /**
481    * Read and return a single row from the table.
482    *
483    * @param row_key the row to read.
484    * @param filter a filter expression, can be used to select a subset of the
485    *     column families and columns in the row.
486    * @returns a tuple, the first element is a boolean, with value `false` if the
487    *     row does not exist.  If the first element is `true` the second element
488    *     has the contents of the Row.  Note that the contents may be empty
489    *     if the filter expression removes all column families and columns.
490    *
491    * @par Idempotency
492    * This is a read-only operation and therefore it is always idempotent.
493    *
494    * @par Example
495    * @snippet read_snippets.cc read row
496    */
497   StatusOr<std::pair<bool, Row>> ReadRow(std::string row_key, Filter filter);
498 
499   /**
500    * Atomic test-and-set for a row using filter expressions.
501    *
502    * Atomically check the value of a row using a filter expression.  If the
503    * expression passes (meaning at least one element is returned by it), one
504    * set of mutations is applied.  If the filter does not pass, a different set
505    * of mutations is applied.  The changes are atomically applied in the server.
506    *
507    * @param row_key the row to modify.
508    * @param filter the filter expression.
509    * @param true_mutations the mutations for the "filter passed" case.
510    * @param false_mutations the mutations for the "filter did not pass" case.
511    * @returns true if the filter passed.
512    *
513    * @par Idempotency
514    * This operation is always treated as non-idempotent.
515    *
516    * @par Check for Value Example
517    * @snippet data_snippets.cc check and mutate
518    *
519    * @par Check for Cell Presence Example
520    * @snippet data_snippets.cc check and mutate not present
521    */
522   StatusOr<MutationBranch> CheckAndMutateRow(
523       std::string row_key, Filter filter, std::vector<Mutation> true_mutations,
524       std::vector<Mutation> false_mutations);
525 
526   /**
527    * Make an asynchronous request to conditionally mutate a row.
528    *
529    * @warning This is an early version of the asynchronous APIs for Cloud
530    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
531    *     is not subject to any SLA or deprecation policy.
532    *
533    * @param row_key the row key on which the conditional mutation will be
534    *     performed
535    * @param filter the condition, depending on which the mutation will be
536    *     performed
537    * @param true_mutations the mutations which will be performed if @p filter is
538    *     true
539    * @param false_mutations the mutations which will be performed if @p filter
540    *     is false
541    * @param cq the completion queue that will execute the asynchronous calls,
542    *     the application must ensure that one or more threads are blocked on
543    *     `cq.Run()`.
544    *
545    * @par Idempotency
546    * This operation is always treated as non-idempotent.
547    *
548    * @par Example
549    * @snippet data_async_snippets.cc async check and mutate
550    */
551   future<StatusOr<MutationBranch>> AsyncCheckAndMutateRow(
552       std::string row_key, Filter filter, std::vector<Mutation> true_mutations,
553       std::vector<Mutation> false_mutations, CompletionQueue& cq);
554 
555   /**
556    * Sample of the row keys in the table, including approximate data sizes.
557    *
558    * @returns Note that the sample may only include one element for small
559    *     tables.  In addition, the sample may include row keys that do not exist
560    *     on the table, and may include the empty row key to indicate
561    *     "end of table".
562    *
563    * @par Idempotency
564    * This operation is always treated as non-idempotent.
565    *
566    * @par Examples
567    * @snippet data_snippets.cc sample row keys
568    */
569   StatusOr<std::vector<bigtable::RowKeySample>> SampleRows();
570 
571   /**
572    * Atomically read and modify the row in the server, returning the
573    * resulting row
574    *
575    * @tparam Args this is zero or more ReadModifyWriteRules to apply on a row
576    * @param row_key the row to read
577    * @param rule to modify the row. Two types of rules are applied here
578    *     AppendValue which will read the existing value and append the
579    *     text provided to the value.
580    *     IncrementAmount which will read the existing uint64 big-endian-int
581    *     and add the value provided.
582    *     Both rules accept the family and column identifier to modify.
583    * @param rules is the zero or more ReadModifyWriteRules to apply on a row.
584    * @returns The new contents of all modified cells.
585    *
586    * @par Idempotency
587    * This operation is always treated as non-idempotent.
588    *
589    * @par Example
590    * @snippet data_snippets.cc read modify write
591    */
592   template <typename... Args>
ReadModifyWriteRow(std::string row_key,bigtable::ReadModifyWriteRule rule,Args &&...rules)593   StatusOr<Row> ReadModifyWriteRow(std::string row_key,
594                                    bigtable::ReadModifyWriteRule rule,
595                                    Args&&... rules) {
596     grpc::Status status;
597 
598     ::google::bigtable::v2::ReadModifyWriteRowRequest request;
599     request.set_row_key(std::move(row_key));
600 
601     // Generate a better compile time error message than the default one
602     // if the types do not match
603     static_assert(
604         absl::conjunction<
605             std::is_convertible<Args, bigtable::ReadModifyWriteRule>...>::value,
606         "The arguments passed to ReadModifyWriteRow(row_key,...) must be "
607         "convertible to bigtable::ReadModifyWriteRule");
608 
609     *request.add_rules() = std::move(rule).as_proto();
610     AddRules(request, std::forward<Args>(rules)...);
611     return ReadModifyWriteRowImpl(std::move(request));
612   }
613 
614   /**
615    * Make an asynchronous request to atomically read and modify a row.
616    *
617    * @warning This is an early version of the asynchronous APIs for Cloud
618    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
619    *     is not subject to any SLA or deprecation policy.
620    *
621    * @param row_key the row key on which modification will be performed
622    * @param cq the completion queue that will execute the asynchronous calls,
623    *     the application must ensure that one or more threads are blocked on
624    *     `cq.Run()`.
625    *
626    * @param rule to modify the row. Two types of rules are applied here
627    *     AppendValue which will read the existing value and append the
628    *     text provided to the value.
629    *     IncrementAmount which will read the existing uint64 big-endian-int
630    *     and add the value provided.
631    *     Both rules accept the family and column identifier to modify.
632    * @param rules is the zero or more ReadModifyWriteRules to apply on a row.
633    * @returns A future, that becomes satisfied when the operation completes,
634    *     at that point the future has the contents of all modified cells.
635    *
636    * @par Idempotency
637    * This operation is always treated as non-idempotent.
638    *
639    * @par Example
640    * @snippet data_async_snippets.cc async read modify write
641    */
642   template <typename... Args>
AsyncReadModifyWriteRow(std::string row_key,CompletionQueue & cq,bigtable::ReadModifyWriteRule rule,Args &&...rules)643   future<StatusOr<Row>> AsyncReadModifyWriteRow(
644       std::string row_key, CompletionQueue& cq,
645       bigtable::ReadModifyWriteRule rule, Args&&... rules) {
646     ::google::bigtable::v2::ReadModifyWriteRowRequest request;
647     request.set_row_key(std::move(row_key));
648     *request.add_rules() = std::move(rule).as_proto();
649     AddRules(request, std::forward<Args>(rules)...);
650 
651     return AsyncReadModifyWriteRowImpl(cq, std::move(request));
652   }
653 
654   /**
655    * Asynchronously reads a set of rows from the table.
656    *
657    * @warning This is an early version of the asynchronous APIs for Cloud
658    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
659    *     is not subject to any SLA or deprecation policy.
660    *
661    * @param cq the completion queue that will execute the asynchronous calls,
662    *     the application must ensure that one or more threads are blocked on
663    *     `cq.Run()`.
664    * @param on_row the callback to be invoked on each successfully read row; it
665    *     should be invocable with `Row` and return a future<bool>; the returned
666    *     `future<bool>` should be satisfied with `true` when the user is ready
667    *     to receive the next callback and with `false` when the user doesn't
668    *     want any more rows; if `on_row` throws, the results are undefined
669    * @param on_finish the callback to be invoked when the stream is closed; it
670    *     should be invocable with `Status` and not return anything; it will
671    *     always be called as the last callback; if `on_finish` throws, the
672    *     results are undefined
673    * @param row_set the rows to read from.
674    * @param filter is applied on the server-side to data in the rows.
675    *
676    * @tparam RowFunctor the type of the @p on_row callback.
677    * @tparam FinishFunctor the type of the @p on_finish callback.
678    *
679    * @par Example
680    * @snippet data_async_snippets.cc async read rows
681    */
682   template <typename RowFunctor, typename FinishFunctor>
AsyncReadRows(CompletionQueue & cq,RowFunctor on_row,FinishFunctor on_finish,RowSet row_set,Filter filter)683   void AsyncReadRows(CompletionQueue& cq, RowFunctor on_row,
684                      FinishFunctor on_finish, RowSet row_set, Filter filter) {
685     AsyncRowReader<RowFunctor, FinishFunctor>::Create(
686         cq, client_, app_profile_id_, table_name_, std::move(on_row),
687         std::move(on_finish), std::move(row_set),
688         AsyncRowReader<RowFunctor, FinishFunctor>::NO_ROWS_LIMIT,
689         std::move(filter), clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
690         metadata_update_policy_,
691         absl::make_unique<bigtable::internal::ReadRowsParserFactory>());
692   }
693 
694   /**
695    * Asynchronously reads a set of rows from the table.
696    *
697    * @warning This is an early version of the asynchronous APIs for Cloud
698    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
699    *     is not subject to any SLA or deprecation policy.
700    *
701    * @param cq the completion queue that will execute the asynchronous calls,
702    *     the application must ensure that one or more threads are blocked on
703    *     `cq.Run()`.
704    * @param on_row the callback to be invoked on each successfully read row; it
705    *     should be invocable with `Row` and return a future<bool>; the returned
706    *     `future<bool>` should be satisfied with `true` when the user is ready
707    *     to receive the next callback and with `false` when the user doesn't
708    *     want any more rows; if `on_row` throws, the results are undefined
709    * @param on_finish the callback to be invoked when the stream is closed; it
710    *     should be invocable with `Status` and not return anything; it will
711    *     always be called as the last callback; if `on_finish` throws, the
712    *     results are undefined
713    * @param row_set the rows to read from.
714    * @param rows_limit the maximum number of rows to read. Cannot be a negative
715    *     number or zero. Use `AsyncReadRows(CompletionQueue, RowSet, Filter)` to
716    *     read all matching rows.
717    * @param filter is applied on the server-side to data in the rows.
718    *
719    * @tparam RowFunctor the type of the @p on_row callback.
720    * @tparam FinishFunctor the type of the @p on_finish callback.
721    *
722    * @par Example
723    * @snippet data_async_snippets.cc async read rows with limit
724    */
725   template <typename RowFunctor, typename FinishFunctor>
AsyncReadRows(CompletionQueue & cq,RowFunctor on_row,FinishFunctor on_finish,RowSet row_set,std::int64_t rows_limit,Filter filter)726   void AsyncReadRows(CompletionQueue& cq, RowFunctor on_row,
727                      FinishFunctor on_finish, RowSet row_set,
728                      std::int64_t rows_limit, Filter filter) {
729     AsyncRowReader<RowFunctor, FinishFunctor>::Create(
730         cq, client_, app_profile_id_, table_name_, std::move(on_row),
731         std::move(on_finish), std::move(row_set), rows_limit, std::move(filter),
732         clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
733         metadata_update_policy_,
734         absl::make_unique<bigtable::internal::ReadRowsParserFactory>());
735   }
736 
737   /**
738    * Asynchronously read and return a single row from the table.
739    *
740    * @warning This is an early version of the asynchronous APIs for Cloud
741    *     Bigtable. These APIs might be changed in backward-incompatible ways. It
742    *     is not subject to any SLA or deprecation policy.
743    *
744    * @param cq the completion queue that will execute the asynchronous calls,
745    *     the application must ensure that one or more threads are blocked on
746    *     `cq.Run()`.
747    * @param row_key the row to read.
748    * @param filter a filter expression, can be used to select a subset of the
749    *     column families and columns in the row.
750    * @returns a future satisfied when the operation completes, failes
751    *     permanently or keeps failing transiently, but the retry policy has been
752    *     exhausted. The future will return a tuple. The first element is a
753    *     boolean, with value `false` if the row does not exist.  If the first
754    *     element is `true` the second element has the contents of the Row.  Note
755    *     that the contents may be empty if the filter expression removes all
756    *     column families and columns.
757    *
758    * @par Idempotency
759    * This is a read-only operation and therefore it is always idempotent.
760    *
761    * @par Example
762    * @snippet data_async_snippets.cc async read row
763    */
764   future<StatusOr<std::pair<bool, Row>>> AsyncReadRow(CompletionQueue& cq,
765                                                       std::string row_key,
766                                                       Filter filter);
767 
768  private:
769   /**
770    * Send request ReadModifyWriteRowRequest to modify the row and get it back
771    */
772   StatusOr<Row> ReadModifyWriteRowImpl(
773       ::google::bigtable::v2::ReadModifyWriteRowRequest request);
774 
775   future<StatusOr<Row>> AsyncReadModifyWriteRowImpl(
776       CompletionQueue& cq,
777       ::google::bigtable::v2::ReadModifyWriteRowRequest request);
778 
AddRules(google::bigtable::v2::ReadModifyWriteRowRequest &)779   void AddRules(google::bigtable::v2::ReadModifyWriteRowRequest&) {
780     // no-op for empty list
781   }
782 
783   template <typename... Args>
AddRules(google::bigtable::v2::ReadModifyWriteRowRequest & request,bigtable::ReadModifyWriteRule rule,Args &&...args)784   void AddRules(google::bigtable::v2::ReadModifyWriteRowRequest& request,
785                 bigtable::ReadModifyWriteRule rule, Args&&... args) {
786     *request.add_rules() = std::move(rule).as_proto();
787     AddRules(request, std::forward<Args>(args)...);
788   }
789 
clone_rpc_retry_policy()790   std::unique_ptr<RPCRetryPolicy> clone_rpc_retry_policy() {
791     return rpc_retry_policy_prototype_->clone();
792   }
793 
clone_rpc_backoff_policy()794   std::unique_ptr<RPCBackoffPolicy> clone_rpc_backoff_policy() {
795     return rpc_backoff_policy_prototype_->clone();
796   }
797 
clone_metadata_update_policy()798   MetadataUpdatePolicy clone_metadata_update_policy() {
799     return metadata_update_policy_;
800   }
801 
clone_idempotent_mutation_policy()802   std::unique_ptr<IdempotentMutationPolicy> clone_idempotent_mutation_policy() {
803     return idempotent_mutation_policy_->clone();
804   }
805 
806   //@{
807   /// @name Helper functions to implement constructors with changed policies.
ChangePolicy(RPCRetryPolicy const & policy)808   void ChangePolicy(RPCRetryPolicy const& policy) {
809     rpc_retry_policy_prototype_ = policy.clone();
810   }
811 
ChangePolicy(RPCBackoffPolicy const & policy)812   void ChangePolicy(RPCBackoffPolicy const& policy) {
813     rpc_backoff_policy_prototype_ = policy.clone();
814   }
815 
ChangePolicy(IdempotentMutationPolicy const & policy)816   void ChangePolicy(IdempotentMutationPolicy const& policy) {
817     idempotent_mutation_policy_ = policy.clone();
818   }
819 
820   template <typename Policy, typename... Policies>
ChangePolicies(Policy && policy,Policies &&...policies)821   void ChangePolicies(Policy&& policy, Policies&&... policies) {
822     ChangePolicy(policy);
823     ChangePolicies(std::forward<Policies>(policies)...);
824   }
ChangePolicies()825   void ChangePolicies() {}
826   //@}
827 
828   friend class MutationBatcher;
829   std::shared_ptr<DataClient> client_;
830   std::string app_profile_id_;
831   std::string table_name_;
832   std::string table_id_;
833   std::shared_ptr<RPCRetryPolicy const> rpc_retry_policy_prototype_;
834   std::shared_ptr<RPCBackoffPolicy const> rpc_backoff_policy_prototype_;
835   MetadataUpdatePolicy metadata_update_policy_;
836   std::shared_ptr<IdempotentMutationPolicy> idempotent_mutation_policy_;
837 };
838 
839 }  // namespace BIGTABLE_CLIENT_NS
840 }  // namespace bigtable
841 }  // namespace cloud
842 }  // namespace google
843 
844 #endif  // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_TABLE_H
845