Google Cloud Spanner C++ Client
A C++ Client Library for Google Cloud Spanner
instance_admin_connection.cc
Go to the documentation of this file.
1 // Copyright 2019 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 
17 #include "google/cloud/spanner/internal/polling_loop.h"
18 #include "google/cloud/spanner/internal/retry_loop.h"
19 #include <chrono>
20 
21 namespace google {
22 namespace cloud {
23 namespace spanner {
24 inline namespace SPANNER_CLIENT_NS {
25 namespace gcsa = ::google::spanner::admin::instance::v1;
26 namespace giam = ::google::iam::v1;
27 
28 namespace {
29 
30 std::unique_ptr<RetryPolicy> DefaultInstanceAdminRetryPolicy() {
32  std::chrono::minutes(30))
33  .clone();
34 }
35 
36 std::unique_ptr<BackoffPolicy> DefaultInstanceAdminBackoffPolicy() {
37  auto constexpr kBackoffScaling = 2.0;
39  std::chrono::seconds(1), std::chrono::minutes(5), kBackoffScaling)
40  .clone();
41 }
42 
43 std::unique_ptr<PollingPolicy> DefaultInstanceAdminPollingPolicy() {
44  auto constexpr kBackoffScaling = 2.0;
45  return GenericPollingPolicy<>(
46  LimitedTimeRetryPolicy(std::chrono::minutes(30)),
47  ExponentialBackoffPolicy(std::chrono::seconds(10),
48  std::chrono::minutes(5), kBackoffScaling))
49  .clone();
50 }
51 
52 class InstanceAdminConnectionImpl : public InstanceAdminConnection {
53  public:
54  InstanceAdminConnectionImpl(std::shared_ptr<internal::InstanceAdminStub> stub,
55  std::unique_ptr<RetryPolicy> retry_policy,
56  std::unique_ptr<BackoffPolicy> backoff_policy,
57  std::unique_ptr<PollingPolicy> polling_policy)
58  : stub_(std::move(stub)),
59  retry_policy_prototype_(std::move(retry_policy)),
60  backoff_policy_prototype_(std::move(backoff_policy)),
61  polling_policy_prototype_(std::move(polling_policy)) {}
62 
63  explicit InstanceAdminConnectionImpl(
64  std::shared_ptr<internal::InstanceAdminStub> stub)
65  : InstanceAdminConnectionImpl(std::move(stub),
66  DefaultInstanceAdminRetryPolicy(),
67  DefaultInstanceAdminBackoffPolicy(),
68  DefaultInstanceAdminPollingPolicy()) {}
69 
70  ~InstanceAdminConnectionImpl() override = default;
71 
72  StatusOr<gcsa::Instance> GetInstance(GetInstanceParams gip) override {
73  gcsa::GetInstanceRequest request;
74  request.set_name(std::move(gip.instance_name));
75  return internal::RetryLoop(
76  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
77  true,
78  [this](grpc::ClientContext& context,
79  gcsa::GetInstanceRequest const& request) {
80  return stub_->GetInstance(context, request);
81  },
82  request, __func__);
83  }
84 
85  future<StatusOr<gcsa::Instance>> CreateInstance(
86  CreateInstanceParams p) override {
87  auto operation = RetryLoop(
88  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
89  false,
90  [this](grpc::ClientContext& context,
91  gcsa::CreateInstanceRequest const& request) {
92  return stub_->CreateInstance(context, request);
93  },
94  p.request, __func__);
95  if (!operation) {
96  return google::cloud::make_ready_future(
97  StatusOr<gcsa::Instance>(operation.status()));
98  }
99 
100  return AwaitCreateOrUpdateInstance(*std::move(operation));
101  }
102 
103  future<StatusOr<gcsa::Instance>> UpdateInstance(
104  UpdateInstanceParams p) override {
105  auto operation = RetryLoop(
106  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
107  true,
108  [this](grpc::ClientContext& context,
109  gcsa::UpdateInstanceRequest const& request) {
110  return stub_->UpdateInstance(context, request);
111  },
112  p.request, __func__);
113  if (!operation) {
114  return google::cloud::make_ready_future(
115  StatusOr<gcsa::Instance>(operation.status()));
116  }
117 
118  return AwaitCreateOrUpdateInstance(*std::move(operation));
119  }
120 
121  Status DeleteInstance(DeleteInstanceParams p) override {
122  gcsa::DeleteInstanceRequest request;
123  request.set_name(std::move(p.instance_name));
124  return internal::RetryLoop(
125  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
126  true,
127  [this](grpc::ClientContext& context,
128  gcsa::DeleteInstanceRequest const& request) {
129  return stub_->DeleteInstance(context, request);
130  },
131  request, __func__);
132  }
133 
134  StatusOr<gcsa::InstanceConfig> GetInstanceConfig(
135  GetInstanceConfigParams p) override {
136  gcsa::GetInstanceConfigRequest request;
137  request.set_name(std::move(p.instance_config_name));
138  return internal::RetryLoop(
139  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
140  true,
141  [this](grpc::ClientContext& context,
142  gcsa::GetInstanceConfigRequest const& request) {
143  return stub_->GetInstanceConfig(context, request);
144  },
145  request, __func__);
146  }
147 
148  ListInstanceConfigsRange ListInstanceConfigs(
149  ListInstanceConfigsParams params) override {
150  gcsa::ListInstanceConfigsRequest request;
151  request.set_parent("projects/" + params.project_id);
152  request.clear_page_token();
153  auto stub = stub_;
154  // Because we do not have C++14 generalized lambda captures we cannot just
155  // use the unique_ptr<> here, so convert to shared_ptr<> instead.
156  auto retry = std::shared_ptr<RetryPolicy>(retry_policy_prototype_->clone());
157  auto backoff =
158  std::shared_ptr<BackoffPolicy>(backoff_policy_prototype_->clone());
159 
160  char const* function_name = __func__;
162  std::move(request),
163  [stub, retry, backoff,
164  function_name](gcsa::ListInstanceConfigsRequest const& r) {
165  return RetryLoop(
166  retry->clone(), backoff->clone(), true,
167  [stub](grpc::ClientContext& context,
168  gcsa::ListInstanceConfigsRequest const& request) {
169  return stub->ListInstanceConfigs(context, request);
170  },
171  r, function_name);
172  },
173  [](gcsa::ListInstanceConfigsResponse r) {
174  std::vector<gcsa::InstanceConfig> result(r.instance_configs().size());
175  auto& configs = *r.mutable_instance_configs();
176  std::move(configs.begin(), configs.end(), result.begin());
177  return result;
178  });
179  }
180 
181  ListInstancesRange ListInstances(ListInstancesParams params) override {
182  gcsa::ListInstancesRequest request;
183  request.set_parent("projects/" + params.project_id);
184  request.set_filter(std::move(params.filter));
185  request.clear_page_token();
186  auto stub = stub_;
187  // Because we do not have C++14 generalized lambda captures we cannot just
188  // use the unique_ptr<> here, so convert to shared_ptr<> instead.
189  auto retry = std::shared_ptr<RetryPolicy>(retry_policy_prototype_->clone());
190  auto backoff =
191  std::shared_ptr<BackoffPolicy>(backoff_policy_prototype_->clone());
192 
193  char const* function_name = __func__;
194  return ListInstancesRange(
195  std::move(request),
196  [stub, retry, backoff,
197  function_name](gcsa::ListInstancesRequest const& r) {
198  return RetryLoop(
199  retry->clone(), backoff->clone(), true,
200  [stub](grpc::ClientContext& context,
201  gcsa::ListInstancesRequest const& request) {
202  return stub->ListInstances(context, request);
203  },
204  r, function_name);
205  },
206  [](gcsa::ListInstancesResponse r) {
207  std::vector<gcsa::Instance> result(r.instances().size());
208  auto& instances = *r.mutable_instances();
209  std::move(instances.begin(), instances.end(), result.begin());
210  return result;
211  });
212  }
213 
214  StatusOr<giam::Policy> GetIamPolicy(GetIamPolicyParams p) override {
215  google::iam::v1::GetIamPolicyRequest request;
216  request.set_resource(std::move(p.instance_name));
217  return RetryLoop(
218  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
219  true,
220  [this](grpc::ClientContext& context,
221  giam::GetIamPolicyRequest const& request) {
222  return stub_->GetIamPolicy(context, request);
223  },
224  request, __func__);
225  }
226 
227  StatusOr<giam::Policy> SetIamPolicy(SetIamPolicyParams p) override {
228  google::iam::v1::SetIamPolicyRequest request;
229  request.set_resource(std::move(p.instance_name));
230  *request.mutable_policy() = std::move(p.policy);
231  bool is_idempotent = !request.policy().etag().empty();
232  return RetryLoop(
233  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
234  is_idempotent,
235  [this](grpc::ClientContext& context,
236  giam::SetIamPolicyRequest const& request) {
237  return stub_->SetIamPolicy(context, request);
238  },
239  request, __func__);
240  }
241 
242  StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
243  TestIamPermissionsParams p) override {
244  google::iam::v1::TestIamPermissionsRequest request;
245  request.set_resource(std::move(p.instance_name));
246  for (auto& permission : p.permissions) {
247  request.add_permissions(std::move(permission));
248  }
249  return RetryLoop(
250  retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),
251  true,
252  [this](grpc::ClientContext& context,
253  giam::TestIamPermissionsRequest const& request) {
254  return stub_->TestIamPermissions(context, request);
255  },
256  request, __func__);
257  }
258 
259  private:
260  future<StatusOr<gcsa::Instance>> AwaitCreateOrUpdateInstance(
261  google::longrunning::Operation operation) {
262  promise<StatusOr<gcsa::Instance>> pr;
263  auto f = pr.get_future();
264 
265  // TODO(#127) - use the (implicit) completion queue to run this loop.
266  std::thread t(
267  [](std::shared_ptr<internal::InstanceAdminStub> stub,
268  google::longrunning::Operation operation,
269  std::unique_ptr<PollingPolicy> polling_policy,
270  google::cloud::promise<StatusOr<gcsa::Instance>> promise,
271  char const* location) mutable {
272  auto result = internal::PollingLoop<
273  internal::PollingLoopResponseExtractor<gcsa::Instance>>(
274  std::move(polling_policy),
275  [stub](grpc::ClientContext& context,
276  google::longrunning::GetOperationRequest const& request) {
277  return stub->GetOperation(context, request);
278  },
279  std::move(operation), location);
280 
281  // Drop our reference to stub; ideally we'd have std::moved into the
282  // lambda. Doing this also prevents a false leak from being reported
283  // when using googlemock.
284  stub.reset();
285  promise.set_value(std::move(result));
286  },
287  stub_, std::move(operation), polling_policy_prototype_->clone(),
288  std::move(pr), __func__);
289  t.detach();
290 
291  return f;
292  }
293  std::shared_ptr<internal::InstanceAdminStub> stub_;
294  std::unique_ptr<RetryPolicy const> retry_policy_prototype_;
295  std::unique_ptr<BackoffPolicy const> backoff_policy_prototype_;
296  std::unique_ptr<PollingPolicy const> polling_policy_prototype_;
297 };
298 } // namespace
299 
300 InstanceAdminConnection::~InstanceAdminConnection() = default;
301 
302 std::shared_ptr<InstanceAdminConnection> MakeInstanceAdminConnection(
303  ConnectionOptions const& options) {
304  return internal::MakeInstanceAdminConnection(
305  internal::CreateDefaultInstanceAdminStub(options), options);
306 }
307 
308 std::shared_ptr<InstanceAdminConnection> MakeInstanceAdminConnection(
309  ConnectionOptions const& options, std::unique_ptr<RetryPolicy> retry_policy,
310  std::unique_ptr<BackoffPolicy> backoff_policy,
311  std::unique_ptr<PollingPolicy> polling_policy) {
312  return internal::MakeInstanceAdminConnection(
313  internal::CreateDefaultInstanceAdminStub(options),
314  std::move(retry_policy), std::move(backoff_policy),
315  std::move(polling_policy));
316 }
317 
318 namespace internal {
319 
320 std::shared_ptr<InstanceAdminConnection> MakeInstanceAdminConnection(
321  std::shared_ptr<internal::InstanceAdminStub> base_stub,
322  ConnectionOptions const&) {
323  return std::make_shared<InstanceAdminConnectionImpl>(std::move(base_stub));
324 }
325 
326 std::shared_ptr<InstanceAdminConnection> MakeInstanceAdminConnection(
327  std::shared_ptr<internal::InstanceAdminStub> base_stub,
328  std::unique_ptr<RetryPolicy> retry_policy,
329  std::unique_ptr<BackoffPolicy> backoff_policy,
330  std::unique_ptr<PollingPolicy> polling_policy) {
331  return std::make_shared<InstanceAdminConnectionImpl>(
332  std::move(base_stub), std::move(retry_policy), std::move(backoff_policy),
333  std::move(polling_policy));
334 }
335 
336 } // namespace internal
337 } // namespace SPANNER_CLIENT_NS
338 } // namespace spanner
339 } // namespace cloud
340 } // namespace google
google::cloud::internal::PaginationRange< google::spanner::admin::instance::v1::InstanceConfig, google::spanner::admin::instance::v1::ListInstanceConfigsRequest, google::spanner::admin::instance::v1::ListInstanceConfigsResponse > ListInstanceConfigsRange
An input range to stream all the instance configs in a Cloud project.
Contains all the Cloud Spanner C++ client types and functions.
google::cloud::internal::PaginationRange< google::spanner::admin::instance::v1::Instance, google::spanner::admin::instance::v1::ListInstancesRequest, google::spanner::admin::instance::v1::ListInstancesResponse > ListInstancesRange
An input range to stream all the instances in a Cloud project.
google::cloud::ConnectionOptions< ConnectionOptionsTraits > ConnectionOptions
The options for Cloud Spanner connections.
google::cloud::internal::LimitedTimeRetryPolicy< google::cloud::Status, internal::SafeGrpcRetry > LimitedTimeRetryPolicy
A retry policy that limits based on time.
Definition: retry_policy.h:65
google::cloud::internal::ExponentialBackoffPolicy ExponentialBackoffPolicy
A truncated exponential backoff policy with randomized periods.
#define SPANNER_CLIENT_NS
Definition: version.h:22
std::shared_ptr< InstanceAdminConnection > MakeInstanceAdminConnection(ConnectionOptions const &options)
Returns an InstanceAdminConnection object that can be used for interacting with Cloud Spanner's admin...