couchbase 4.2.11 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/deps/couchbase-cxx-cache/mozilla-ca-bundle.crt +49 -2
  2. package/deps/couchbase-cxx-cache/mozilla-ca-bundle.sha256 +1 -1
  3. package/deps/couchbase-cxx-client/core/impl/cluster.cxx +51 -5
  4. package/deps/couchbase-cxx-client/core/impl/collection.cxx +224 -209
  5. package/deps/couchbase-cxx-client/core/impl/query_error_context.cxx +2 -2
  6. package/deps/couchbase-cxx-client/core/impl/query_index_manager.cxx +1 -0
  7. package/deps/couchbase-cxx-client/core/io/dns_client.cxx +4 -0
  8. package/deps/couchbase-cxx-client/core/io/dns_config.cxx +15 -4
  9. package/deps/couchbase-cxx-client/core/io/dns_config.hxx +1 -1
  10. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +84 -53
  11. package/deps/couchbase-cxx-client/core/mcbp/operation_queue.cxx +1 -0
  12. package/deps/couchbase-cxx-client/core/meta/features.hxx +5 -0
  13. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +116 -105
  14. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +116 -108
  15. package/deps/couchbase-cxx-client/core/operations/document_search.cxx +97 -81
  16. package/deps/couchbase-cxx-client/core/operations/document_search.hxx +5 -0
  17. package/deps/couchbase-cxx-client/core/range_scan_orchestrator_options.hxx +2 -1
  18. package/deps/couchbase-cxx-client/core/transactions/atr_cleanup_entry.cxx +16 -7
  19. package/deps/couchbase-cxx-client/core/transactions/attempt_context_impl.cxx +578 -483
  20. package/deps/couchbase-cxx-client/core/transactions/attempt_context_testing_hooks.cxx +51 -50
  21. package/deps/couchbase-cxx-client/core/transactions/attempt_context_testing_hooks.hxx +4 -2
  22. package/deps/couchbase-cxx-client/core/transactions/cleanup_testing_hooks.cxx +6 -6
  23. package/deps/couchbase-cxx-client/core/transactions/cleanup_testing_hooks.hxx +3 -2
  24. package/deps/couchbase-cxx-client/core/transactions/internal/transactions_cleanup.hxx +2 -0
  25. package/deps/couchbase-cxx-client/core/transactions/internal/utils.hxx +5 -1
  26. package/deps/couchbase-cxx-client/core/transactions/staged_mutation.cxx +222 -179
  27. package/deps/couchbase-cxx-client/core/transactions/staged_mutation.hxx +23 -12
  28. package/deps/couchbase-cxx-client/core/transactions/transactions.cxx +61 -24
  29. package/deps/couchbase-cxx-client/core/transactions/transactions_cleanup.cxx +36 -16
  30. package/deps/couchbase-cxx-client/core/transactions/utils.cxx +9 -0
  31. package/deps/couchbase-cxx-client/core/transactions.hxx +40 -7
  32. package/deps/couchbase-cxx-client/couchbase/cluster.hxx +19 -0
  33. package/deps/couchbase-cxx-client/couchbase/fork_event.hxx +39 -0
  34. package/dist/binding.d.ts +8 -0
  35. package/dist/bindingutilities.d.ts +6 -1
  36. package/dist/bindingutilities.js +15 -1
  37. package/dist/bucketmanager.d.ts +0 -12
  38. package/dist/cluster.d.ts +0 -2
  39. package/dist/cluster.js +0 -2
  40. package/dist/collection.d.ts +3 -3
  41. package/dist/collection.js +3 -1
  42. package/dist/querytypes.d.ts +0 -2
  43. package/dist/rangeScan.d.ts +0 -8
  44. package/dist/rangeScan.js +0 -8
  45. package/dist/scope.d.ts +0 -5
  46. package/dist/scope.js +0 -5
  47. package/dist/scopesearchindexmanager.d.ts +0 -2
  48. package/dist/scopesearchindexmanager.js +0 -2
  49. package/dist/searchexecutor.js +3 -1
  50. package/dist/searchtypes.d.ts +16 -6
  51. package/dist/searchtypes.js +2 -6
  52. package/dist/transactions.d.ts +23 -0
  53. package/dist/transactions.js +16 -10
  54. package/dist/vectorsearch.d.ts +8 -8
  55. package/dist/vectorsearch.js +7 -7
  56. package/package.json +7 -7
  57. package/src/instance.cpp +11 -1
  58. package/src/instance.hpp +1 -0
  59. package/src/jstocbpp_autogen.hpp +8 -0
  60. package/src/jstocbpp_transactions.hpp +40 -3
  61. package/src/transactions.cpp +12 -1
  62. package/tools/gen-bindings-json.py +0 -1
@@ -38,8 +38,8 @@ query_error_context::to_json() const -> std::string
38
38
  { "retry_attempts", retry_attempts() },
39
39
  { "client_context_id", client_context_id_ },
40
40
  { "statement", statement_ },
41
- { "method", statement_ },
42
- { "path", statement_ },
41
+ { "method", method_ },
42
+ { "path", path_ },
43
43
  { "http_status", http_status_ },
44
44
  { "http_body", http_body_ },
45
45
  { "hostname", hostname_ },
@@ -30,6 +30,7 @@
30
30
 
31
31
  #include <asio/steady_timer.hpp>
32
32
 
33
+ #include <algorithm>
33
34
  #include <utility>
34
35
 
35
36
  namespace couchbase
@@ -285,6 +285,10 @@ dns_client::query_srv(const std::string& name,
285
285
  const dns_config& config,
286
286
  utils::movable_function<void(dns_srv_response&&)>&& handler)
287
287
  {
288
+ if (config.nameserver().empty()) {
289
+ return handler({ {} });
290
+ }
291
+
288
292
  std::error_code ec;
289
293
  auto address = asio::ip::make_address(config.nameserver(), ec);
290
294
  if (ec) {
@@ -53,6 +53,7 @@ load_resolv_conf()
53
53
  fixed_info = (FIXED_INFO*)malloc(sizeof(FIXED_INFO));
54
54
  if (fixed_info == NULL) {
55
55
  CB_LOG_WARNING("Error allocating memory needed to call GetNetworkParams");
56
+ return {};
56
57
  }
57
58
  buf = sizeof(FIXED_INFO);
58
59
 
@@ -63,6 +64,7 @@ load_resolv_conf()
63
64
  fixed_info = (FIXED_INFO*)malloc(buf);
64
65
  if (fixed_info == NULL) {
65
66
  CB_LOG_WARNING("Error allocating memory needed to call GetNetworkParams");
67
+ return {};
66
68
  }
67
69
  }
68
70
 
@@ -86,10 +88,12 @@ load_resolv_conf()
86
88
  }
87
89
  } else {
88
90
  CB_LOG_WARNING("GetNetworkParams failed with error: {}", ret);
91
+ return {};
89
92
  }
90
93
 
91
- if (fixed_info)
94
+ if (fixed_info) {
92
95
  free(fixed_info);
96
+ }
93
97
 
94
98
  if (dns_servers.size() > 0) {
95
99
  CB_LOG_DEBUG(
@@ -153,10 +157,17 @@ dns_config::system_config()
153
157
  std::error_code ec;
154
158
  asio::ip::make_address(nameserver, ec);
155
159
  if (ec) {
156
- CB_LOG_DEBUG("Unable to parse \"{}\" as a network address, fall back to \"{}\"", nameserver, default_nameserver);
157
- nameserver = default_nameserver;
160
+ std::string extra_info{};
161
+ #ifndef _WIN32
162
+ extra_info = fmt::format(" in \"{}\"", default_resolv_conf_path);
163
+ #endif
164
+ CB_LOG_WARNING("System DNS detection failed: unable to parse \"{}\" as a network address{}. DNS-SRV will not work "
165
+ "unless nameserver is specified explicitly in the options.",
166
+ nameserver,
167
+ extra_info);
168
+ } else {
169
+ instance.nameserver_ = nameserver;
158
170
  }
159
- instance.nameserver_ = nameserver;
160
171
  });
161
172
 
162
173
  return instance;
@@ -40,7 +40,7 @@ class dns_config
40
40
  [[nodiscard]] std::chrono::milliseconds timeout() const;
41
41
 
42
42
  private:
43
- std::string nameserver_{ default_nameserver };
43
+ std::string nameserver_{};
44
44
  std::uint16_t port_{ default_port };
45
45
  std::chrono::milliseconds timeout_{ timeout_defaults::dns_srv_timeout };
46
46
  };
@@ -210,6 +210,7 @@ class mcbp_session_impl
210
210
  std::shared_ptr<mcbp_session_impl> session_;
211
211
  sasl::ClientContext sasl_;
212
212
  std::atomic_bool stopped_{ false };
213
+ std::string last_error_message_;
213
214
 
214
215
  public:
215
216
  ~bootstrap_handler()
@@ -234,6 +235,16 @@ class mcbp_session_impl
234
235
  return { "SCRAM-SHA512", "SCRAM-SHA256", "SCRAM-SHA1" };
235
236
  }
236
237
 
238
+ std::string last_error_message() &&
239
+ {
240
+ return std::move(last_error_message_);
241
+ }
242
+
243
+ [[nodiscard]] const std::string& last_error_message() const&
244
+ {
245
+ return last_error_message_;
246
+ }
247
+
237
248
  explicit bootstrap_handler(std::shared_ptr<mcbp_session_impl> session)
238
249
  : session_(std::move(session))
239
250
  , sasl_([origin = session_->origin_]() { return origin.username(); },
@@ -322,21 +333,21 @@ class mcbp_session_impl
322
333
  case key_value_status_code::rate_limited_max_connections:
323
334
  case key_value_status_code::rate_limited_network_egress:
324
335
  case key_value_status_code::rate_limited_network_ingress:
325
- CB_LOG_DEBUG(
326
- "{} unable to bootstrap MCBP session (bucket={}, opcode={}, status={}), the user has reached rate limit",
327
- session_->log_prefix_,
336
+ last_error_message_ = fmt::format(
337
+ "unable to bootstrap MCBP session (bucket={}, opcode={}, status={}), the user has reached rate limit",
328
338
  session_->bucket_name_.value_or(""),
329
339
  protocol::client_opcode(msg.header.opcode),
330
340
  status);
341
+ CB_LOG_DEBUG("{} {}", session_->log_prefix_, last_error_message_);
331
342
  return complete(errc::common::rate_limited);
332
343
 
333
344
  case key_value_status_code::scope_size_limit_exceeded:
334
- CB_LOG_DEBUG(
335
- "{} unable to bootstrap MCBP session (bucket={}, opcode={}, status={}), the user has reached quota limit",
336
- session_->log_prefix_,
345
+ last_error_message_ = fmt::format(
346
+ "unable to bootstrap MCBP session (bucket={}, opcode={}, status={}), the user has reached quota limit",
337
347
  session_->bucket_name_.value_or(""),
338
348
  protocol::client_opcode(msg.header.opcode),
339
349
  status);
350
+ CB_LOG_DEBUG("{} {}", session_->log_prefix_, last_error_message_);
340
351
  return complete(errc::common::quota_limited);
341
352
 
342
353
  default:
@@ -356,20 +367,18 @@ class mcbp_session_impl
356
367
  return auth_success();
357
368
  }
358
369
  } else {
359
- CB_LOG_WARNING("{} unexpected message status during bootstrap: {} (opaque={})",
360
- session_->log_prefix_,
361
- resp.error_message(),
362
- resp.opaque());
370
+ last_error_message_ = fmt::format(
371
+ "unexpected message status during bootstrap: {} (opaque={})", resp.error_message(), resp.opaque());
372
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
363
373
  return complete(errc::network::handshake_failure);
364
374
  }
365
375
  } break;
366
376
  case protocol::client_opcode::sasl_list_mechs: {
367
377
  protocol::client_response<protocol::sasl_list_mechs_response_body> resp(std::move(msg));
368
378
  if (resp.status() != key_value_status_code::success) {
369
- CB_LOG_WARNING("{} unexpected message status during bootstrap: {} (opaque={})",
370
- session_->log_prefix_,
371
- resp.error_message(),
372
- resp.opaque());
379
+ last_error_message_ = fmt::format(
380
+ "unexpected message status during bootstrap: {} (opaque={})", resp.error_message(), resp.opaque());
381
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
373
382
  return complete(errc::common::authentication_failure);
374
383
  }
375
384
  } break;
@@ -390,17 +399,17 @@ class mcbp_session_impl
390
399
  req.body().sasl_data(sasl_payload);
391
400
  session_->write_and_flush(req.data());
392
401
  } else {
393
- CB_LOG_ERROR("{} unable to authenticate: (sasl_code={}, opaque={})",
394
- session_->log_prefix_,
395
- sasl_code,
396
- resp.opaque());
402
+ last_error_message_ =
403
+ fmt::format("unable to authenticate: (sasl_code={}, opaque={})", sasl_code, resp.opaque());
404
+ CB_LOG_ERROR("{} {}", session_->log_prefix_, last_error_message_);
397
405
  return complete(errc::common::authentication_failure);
398
406
  }
399
407
  } else {
400
- CB_LOG_WARNING("{} unexpected message status during bootstrap: {} (opaque={})",
401
- session_->log_prefix_,
402
- resp.error_message(),
403
- resp.opaque());
408
+ last_error_message_ = fmt::format("{} unexpected message status during bootstrap: {} (opaque={})",
409
+ session_->log_prefix_,
410
+ resp.error_message(),
411
+ resp.opaque());
412
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
404
413
  return complete(errc::common::authentication_failure);
405
414
  }
406
415
  } break;
@@ -409,6 +418,9 @@ class mcbp_session_impl
409
418
  if (resp.status() == key_value_status_code::success) {
410
419
  return auth_success();
411
420
  }
421
+ last_error_message_ =
422
+ fmt::format("unable to authenticate (opcode={}, status={}, opaque={})", opcode, resp.status(), resp.opaque());
423
+ CB_LOG_ERROR("{} {}", session_->log_prefix_, last_error_message_);
412
424
  return complete(errc::common::authentication_failure);
413
425
  }
414
426
  case protocol::client_opcode::get_error_map: {
@@ -416,11 +428,11 @@ class mcbp_session_impl
416
428
  if (resp.status() == key_value_status_code::success) {
417
429
  session_->error_map_.emplace(resp.body().errmap());
418
430
  } else {
419
- CB_LOG_WARNING("{} unexpected message status during bootstrap: {} (opaque={}, {:n})",
420
- session_->log_prefix_,
421
- resp.error_message(),
422
- resp.opaque(),
423
- spdlog::to_hex(resp.header()));
431
+ last_error_message_ = fmt::format("unexpected message status during bootstrap: {} (opaque={}, {:n})",
432
+ resp.error_message(),
433
+ resp.opaque(),
434
+ spdlog::to_hex(resp.header()));
435
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
424
436
  return complete(errc::network::protocol_error);
425
437
  }
426
438
  } break;
@@ -430,25 +442,25 @@ class mcbp_session_impl
430
442
  CB_LOG_DEBUG("{} selected bucket: {}", session_->log_prefix_, session_->bucket_name_.value_or(""));
431
443
  session_->bucket_selected_ = true;
432
444
  } else if (resp.status() == key_value_status_code::not_found) {
433
- CB_LOG_DEBUG(
434
- "{} kv_engine node does not have configuration propagated yet (opcode={}, status={}, opaque={})",
435
- session_->log_prefix_,
436
- opcode,
437
- resp.status(),
438
- resp.opaque());
445
+ last_error_message_ =
446
+ fmt::format("kv_engine node does not have configuration propagated yet (opcode={}, status={}, opaque={})",
447
+ opcode,
448
+ resp.status(),
449
+ resp.opaque());
450
+ CB_LOG_DEBUG("{} {}", session_->log_prefix_, last_error_message_);
439
451
  return complete(errc::network::configuration_not_available);
440
452
  } else if (resp.status() == key_value_status_code::no_access) {
441
- CB_LOG_DEBUG("{} unable to select bucket: {}, probably the bucket does not exist",
442
- session_->log_prefix_,
443
- session_->bucket_name_.value_or(""));
453
+ last_error_message_ = fmt::format("unable to select bucket: {}, probably the bucket does not exist",
454
+ session_->bucket_name_.value_or(""));
455
+ CB_LOG_DEBUG("{} {}", session_->log_prefix_, last_error_message_);
444
456
  session_->bucket_selected_ = false;
445
457
  return complete(errc::common::bucket_not_found);
446
458
  } else {
447
- CB_LOG_WARNING("{} unexpected message status during bootstrap: {} (opaque={}, {:n})",
448
- session_->log_prefix_,
449
- resp.error_message(),
450
- resp.opaque(),
451
- spdlog::to_hex(resp.header()));
459
+ last_error_message_ = fmt::format("unexpected message status during bootstrap: {} (opaque={}, {:n})",
460
+ resp.error_message(),
461
+ resp.opaque(),
462
+ spdlog::to_hex(resp.header()));
463
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
452
464
  return complete(errc::common::bucket_not_found);
453
465
  }
454
466
  } break;
@@ -470,12 +482,12 @@ class mcbp_session_impl
470
482
  session_->update_configuration(resp.body().config());
471
483
  complete({});
472
484
  } else if (resp.status() == key_value_status_code::not_found) {
473
- CB_LOG_DEBUG(
474
- "{} kv_engine node does not have configuration propagated yet (opcode={}, status={}, opaque={})",
475
- session_->log_prefix_,
476
- opcode,
477
- resp.status(),
478
- resp.opaque());
485
+ last_error_message_ =
486
+ fmt::format("kv_engine node does not have configuration propagated yet (opcode={}, status={}, opaque={})",
487
+ opcode,
488
+ resp.status(),
489
+ resp.opaque());
490
+ CB_LOG_DEBUG("{} {}", session_->log_prefix_, last_error_message_);
479
491
  return complete(errc::network::configuration_not_available);
480
492
  } else if (resp.status() == key_value_status_code::no_bucket && !session_->bucket_name_) {
481
493
  // bucket-less session, but the server wants bucket
@@ -486,16 +498,17 @@ class mcbp_session_impl
486
498
  session_->connection_endpoints_.remote_address, session_->connection_endpoints_.remote.port(), 0));
487
499
  complete({});
488
500
  } else {
489
- CB_LOG_WARNING("{} unexpected message status during bootstrap: {} (opaque={}, {:n})",
490
- session_->log_prefix_,
491
- resp.error_message(),
492
- resp.opaque(),
493
- spdlog::to_hex(resp.header()));
501
+ last_error_message_ = fmt::format("unexpected message status during bootstrap: {} (opaque={}, {:n})",
502
+ resp.error_message(),
503
+ resp.opaque(),
504
+ spdlog::to_hex(resp.header()));
505
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
494
506
  return complete(errc::network::protocol_error);
495
507
  }
496
508
  } break;
497
509
  default:
498
- CB_LOG_WARNING("{} unexpected message during bootstrap: {}", session_->log_prefix_, opcode);
510
+ last_error_message_ = fmt::format("unexpected message during bootstrap: {}", opcode);
511
+ CB_LOG_WARNING("{} {}", session_->log_prefix_, last_error_message_);
499
512
  return complete(errc::network::protocol_error);
500
513
  }
501
514
  break;
@@ -796,6 +809,20 @@ class mcbp_session_impl
796
809
 
797
810
  void ping(std::shared_ptr<diag::ping_reporter> handler, std::optional<std::chrono::milliseconds> timeout)
798
811
  {
812
+ if (!bootstrapped_) {
813
+ handler->report({
814
+ service_type::key_value,
815
+ id_,
816
+ std::chrono::microseconds(0),
817
+ remote_address(),
818
+ local_address(),
819
+ diag::ping_state::error,
820
+ bucket_name_,
821
+ last_bootstrap_error_message_.has_value() ? last_bootstrap_error_message_.value()
822
+ : "Bootstrap incomplete, cannot perform ping.",
823
+ });
824
+ return;
825
+ }
799
826
  protocol::client_request<protocol::mcbp_noop_request_body> req;
800
827
  req.opaque(next_opaque());
801
828
  write_and_subscribe(req.opaque(),
@@ -871,6 +898,9 @@ class mcbp_session_impl
871
898
  return;
872
899
  }
873
900
  bootstrapped_ = false;
901
+ if (bootstrap_handler_) {
902
+ last_bootstrap_error_message_ = std::move(bootstrap_handler_)->last_error_message();
903
+ }
874
904
  bootstrap_handler_ = nullptr;
875
905
  state_ = diag::endpoint_state::connecting;
876
906
  if (stream_->is_open()) {
@@ -1644,6 +1674,7 @@ class mcbp_session_impl
1644
1674
  std::optional<std::string> bucket_name_;
1645
1675
  mcbp_parser parser_;
1646
1676
  std::shared_ptr<bootstrap_handler> bootstrap_handler_{ nullptr };
1677
+ std::optional<std::string> last_bootstrap_error_message_;
1647
1678
  std::shared_ptr<message_handler> handler_{ nullptr };
1648
1679
  utils::movable_function<void(std::error_code, const topology::configuration&)> bootstrap_callback_{};
1649
1680
  std::mutex command_handlers_mutex_{};
@@ -25,6 +25,7 @@
25
25
 
26
26
  #include <fmt/core.h>
27
27
 
28
+ #include <algorithm>
28
29
  #include <memory>
29
30
 
30
31
  namespace couchbase::core::mcbp
@@ -126,3 +126,8 @@
126
126
  * Transaction's transaction_operation_failed has a public getter for its final_error
127
127
  */
128
128
  #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_CAN_FETCH_TO_RAISE 1
129
+
130
+ /**
131
+ * Hooks in the transactions core are asynchronous (they have a callback parameter)
132
+ */
133
+ #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_CORE_ASYNC_HOOKS
@@ -65,123 +65,134 @@ struct lookup_in_all_replicas_request {
65
65
  template<typename Core, typename Handler>
66
66
  void execute(Core core, Handler handler)
67
67
  {
68
- core->with_bucket_configuration(
68
+ core->open_bucket(
69
69
  id.bucket(),
70
70
  [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
71
- std::error_code ec, const topology::configuration& config) mutable {
72
- if (!config.capabilities.supports_subdoc_read_replica()) {
73
- ec = errc::common::feature_not_available;
74
- }
75
-
71
+ std::error_code ec) mutable {
76
72
  if (ec) {
77
73
  std::optional<std::string> first_error_path{};
78
74
  std::optional<std::size_t> first_error_index{};
79
75
  return h(response_type{
80
76
  make_subdocument_error_context(make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
81
77
  }
82
- using handler_type = utils::movable_function<void(response_type)>;
78
+ core->with_bucket_configuration(
79
+ id.bucket(),
80
+ [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(h)](
81
+ std::error_code ec, const topology::configuration& config) mutable {
82
+ if (!config.capabilities.supports_subdoc_read_replica()) {
83
+ ec = errc::common::feature_not_available;
84
+ }
83
85
 
84
- struct replica_context {
85
- replica_context(handler_type handler, std::uint32_t expected_responses)
86
- : handler_(std::move(handler))
87
- , expected_responses_(expected_responses)
88
- {
89
- }
86
+ if (ec) {
87
+ std::optional<std::string> first_error_path{};
88
+ std::optional<std::size_t> first_error_index{};
89
+ return h(response_type{ make_subdocument_error_context(
90
+ make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
91
+ }
92
+ using handler_type = utils::movable_function<void(response_type)>;
90
93
 
91
- handler_type handler_;
92
- std::uint32_t expected_responses_;
93
- bool done_{ false };
94
- std::mutex mutex_{};
95
- std::vector<lookup_in_all_replicas_response::entry> result_{};
96
- };
97
- auto ctx = std::make_shared<replica_context>(std::move(h), config.num_replicas.value_or(0U) + 1U);
94
+ struct replica_context {
95
+ replica_context(handler_type handler, std::uint32_t expected_responses)
96
+ : handler_(std::move(handler))
97
+ , expected_responses_(expected_responses)
98
+ {
99
+ }
98
100
 
99
- for (std::size_t idx = 1U; idx <= config.num_replicas.value_or(0U); ++idx) {
100
- document_id replica_id{ id };
101
- replica_id.node_index(idx);
102
- core->execute(impl::lookup_in_replica_request{ std::move(replica_id), specs, timeout, parent_span },
103
- [ctx](impl::lookup_in_replica_response&& resp) {
104
- handler_type local_handler{};
105
- {
106
- std::scoped_lock lock(ctx->mutex_);
107
- if (ctx->done_) {
108
- return;
109
- }
110
- --ctx->expected_responses_;
111
- if (resp.ctx.ec()) {
112
- if (ctx->expected_responses_ > 0) {
113
- // just ignore the response
114
- return;
115
- }
116
- } else {
117
- lookup_in_all_replicas_response::entry top_entry{};
118
- top_entry.cas = resp.cas;
119
- top_entry.deleted = resp.deleted;
120
- top_entry.is_replica = true;
121
- for (auto& field : resp.fields) {
122
- lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
123
- lookup_in_entry.path = field.path;
124
- lookup_in_entry.value = field.value;
125
- lookup_in_entry.status = field.status;
126
- lookup_in_entry.ec = field.ec;
127
- lookup_in_entry.exists = field.exists;
128
- lookup_in_entry.original_index = field.original_index;
129
- lookup_in_entry.opcode = field.opcode;
130
- top_entry.fields.emplace_back(lookup_in_entry);
131
- }
132
- ctx->result_.emplace_back(lookup_in_all_replicas_response::entry{ top_entry });
133
- }
134
- if (ctx->expected_responses_ == 0) {
135
- ctx->done_ = true;
136
- std::swap(local_handler, ctx->handler_);
137
- }
138
- }
139
- if (local_handler) {
140
- return local_handler({ std::move(resp.ctx), std::move(ctx->result_) });
141
- }
142
- });
143
- }
101
+ handler_type handler_;
102
+ std::uint32_t expected_responses_;
103
+ bool done_{ false };
104
+ std::mutex mutex_{};
105
+ std::vector<lookup_in_all_replicas_response::entry> result_{};
106
+ };
107
+ auto ctx = std::make_shared<replica_context>(std::move(h), config.num_replicas.value_or(0U) + 1U);
108
+
109
+ for (std::size_t idx = 1U; idx <= config.num_replicas.value_or(0U); ++idx) {
110
+ document_id replica_id{ id };
111
+ replica_id.node_index(idx);
112
+ core->execute(impl::lookup_in_replica_request{ std::move(replica_id), specs, timeout, parent_span },
113
+ [ctx](impl::lookup_in_replica_response&& resp) {
114
+ handler_type local_handler{};
115
+ {
116
+ std::scoped_lock lock(ctx->mutex_);
117
+ if (ctx->done_) {
118
+ return;
119
+ }
120
+ --ctx->expected_responses_;
121
+ if (resp.ctx.ec()) {
122
+ if (ctx->expected_responses_ > 0) {
123
+ // just ignore the response
124
+ return;
125
+ }
126
+ } else {
127
+ lookup_in_all_replicas_response::entry top_entry{};
128
+ top_entry.cas = resp.cas;
129
+ top_entry.deleted = resp.deleted;
130
+ top_entry.is_replica = true;
131
+ for (auto& field : resp.fields) {
132
+ lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
133
+ lookup_in_entry.path = field.path;
134
+ lookup_in_entry.value = field.value;
135
+ lookup_in_entry.status = field.status;
136
+ lookup_in_entry.ec = field.ec;
137
+ lookup_in_entry.exists = field.exists;
138
+ lookup_in_entry.original_index = field.original_index;
139
+ lookup_in_entry.opcode = field.opcode;
140
+ top_entry.fields.emplace_back(lookup_in_entry);
141
+ }
142
+ ctx->result_.emplace_back(lookup_in_all_replicas_response::entry{ top_entry });
143
+ }
144
+ if (ctx->expected_responses_ == 0) {
145
+ ctx->done_ = true;
146
+ std::swap(local_handler, ctx->handler_);
147
+ }
148
+ }
149
+ if (local_handler) {
150
+ return local_handler({ std::move(resp.ctx), std::move(ctx->result_) });
151
+ }
152
+ });
153
+ }
144
154
 
145
- core->execute(lookup_in_request{ document_id{ id }, {}, {}, false, specs, timeout }, [ctx](lookup_in_response&& resp) {
146
- handler_type local_handler{};
147
- {
148
- std::scoped_lock lock(ctx->mutex_);
149
- if (ctx->done_) {
150
- return;
151
- }
152
- --ctx->expected_responses_;
153
- if (resp.ctx.ec()) {
154
- if (ctx->expected_responses_ > 0) {
155
- // just ignore the response
156
- return;
157
- }
158
- } else {
159
- lookup_in_all_replicas_response::entry top_entry{};
160
- top_entry.cas = resp.cas;
161
- top_entry.deleted = resp.deleted;
162
- top_entry.is_replica = false;
163
- for (auto& field : resp.fields) {
164
- lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
165
- lookup_in_entry.path = field.path;
166
- lookup_in_entry.value = field.value;
167
- lookup_in_entry.status = field.status;
168
- lookup_in_entry.ec = field.ec;
169
- lookup_in_entry.exists = field.exists;
170
- lookup_in_entry.original_index = field.original_index;
171
- lookup_in_entry.opcode = field.opcode;
172
- top_entry.fields.emplace_back(lookup_in_entry);
173
- }
174
- ctx->result_.emplace_back(lookup_in_all_replicas_response::entry{ top_entry });
175
- }
176
- if (ctx->expected_responses_ == 0) {
177
- ctx->done_ = true;
178
- std::swap(local_handler, ctx->handler_);
179
- }
180
- }
181
- if (local_handler) {
182
- return local_handler({ std::move(resp.ctx), std::move(ctx->result_) });
183
- }
184
- });
155
+ core->execute(lookup_in_request{ document_id{ id }, {}, {}, false, specs, timeout }, [ctx](lookup_in_response&& resp) {
156
+ handler_type local_handler{};
157
+ {
158
+ std::scoped_lock lock(ctx->mutex_);
159
+ if (ctx->done_) {
160
+ return;
161
+ }
162
+ --ctx->expected_responses_;
163
+ if (resp.ctx.ec()) {
164
+ if (ctx->expected_responses_ > 0) {
165
+ // just ignore the response
166
+ return;
167
+ }
168
+ } else {
169
+ lookup_in_all_replicas_response::entry top_entry{};
170
+ top_entry.cas = resp.cas;
171
+ top_entry.deleted = resp.deleted;
172
+ top_entry.is_replica = false;
173
+ for (auto& field : resp.fields) {
174
+ lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
175
+ lookup_in_entry.path = field.path;
176
+ lookup_in_entry.value = field.value;
177
+ lookup_in_entry.status = field.status;
178
+ lookup_in_entry.ec = field.ec;
179
+ lookup_in_entry.exists = field.exists;
180
+ lookup_in_entry.original_index = field.original_index;
181
+ lookup_in_entry.opcode = field.opcode;
182
+ top_entry.fields.emplace_back(lookup_in_entry);
183
+ }
184
+ ctx->result_.emplace_back(lookup_in_all_replicas_response::entry{ top_entry });
185
+ }
186
+ if (ctx->expected_responses_ == 0) {
187
+ ctx->done_ = true;
188
+ std::swap(local_handler, ctx->handler_);
189
+ }
190
+ }
191
+ if (local_handler) {
192
+ return local_handler({ std::move(resp.ctx), std::move(ctx->result_) });
193
+ }
194
+ });
195
+ });
185
196
  });
186
197
  }
187
198
  };