couchbase 4.2.11 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  };