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
@@ -62,123 +62,131 @@ struct lookup_in_any_replica_request {
62
62
  template<typename Core, typename Handler>
63
63
  void execute(Core core, Handler handler)
64
64
  {
65
- core->with_bucket_configuration(
66
- id.bucket(),
67
- [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
68
- std::error_code ec, const topology::configuration& config) mutable {
69
- if (!config.capabilities.supports_subdoc_read_replica()) {
70
- ec = errc::common::feature_not_available;
71
- }
72
- if (specs.size() > 16) {
73
- ec = errc::common::invalid_argument;
74
- }
65
+ core->open_bucket(id.bucket(),
66
+ [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
67
+ std::error_code ec) mutable {
68
+ if (ec) {
69
+ std::optional<std::string> first_error_path{};
70
+ std::optional<std::size_t> first_error_index{};
71
+ h(response_type{ make_subdocument_error_context(
72
+ make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
73
+ return;
74
+ }
75
+ return core->with_bucket_configuration(
76
+ id.bucket(),
77
+ [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(h)](
78
+ std::error_code ec, const topology::configuration& config) mutable {
79
+ if (!config.capabilities.supports_subdoc_read_replica()) {
80
+ ec = errc::common::feature_not_available;
81
+ }
75
82
 
76
- if (ec) {
77
- std::optional<std::string> first_error_path{};
78
- std::optional<std::size_t> first_error_index{};
79
- return h(response_type{
80
- make_subdocument_error_context(make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
81
- }
82
- using handler_type = utils::movable_function<void(response_type)>;
83
+ if (ec) {
84
+ std::optional<std::string> first_error_path{};
85
+ std::optional<std::size_t> first_error_index{};
86
+ return h(response_type{ make_subdocument_error_context(
87
+ make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
88
+ }
89
+ using handler_type = utils::movable_function<void(response_type)>;
83
90
 
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
- }
91
+ struct replica_context {
92
+ replica_context(handler_type&& handler, std::uint32_t expected_responses)
93
+ : handler_(std::move(handler))
94
+ , expected_responses_(expected_responses)
95
+ {
96
+ }
90
97
 
91
- handler_type handler_;
92
- std::uint32_t expected_responses_;
93
- bool done_{ false };
94
- std::mutex mutex_{};
95
- };
96
- auto ctx = std::make_shared<replica_context>(std::move(h), config.num_replicas.value_or(0U) + 1U);
98
+ handler_type handler_;
99
+ std::uint32_t expected_responses_;
100
+ bool done_{ false };
101
+ std::mutex mutex_{};
102
+ };
103
+ auto ctx = std::make_shared<replica_context>(std::move(h), config.num_replicas.value_or(0U) + 1U);
97
104
 
98
- for (std::size_t idx = 1U; idx <= config.num_replicas.value_or(0U); ++idx) {
99
- document_id replica_id{ id };
100
- replica_id.node_index(idx);
101
- core->execute(impl::lookup_in_replica_request{ std::move(replica_id), specs, timeout, parent_span },
102
- [ctx](impl::lookup_in_replica_response&& resp) {
103
- handler_type local_handler;
104
- {
105
- std::scoped_lock lock(ctx->mutex_);
106
- if (ctx->done_) {
107
- return;
108
- }
109
- --ctx->expected_responses_;
110
- if (resp.ctx.ec()) {
111
- if (ctx->expected_responses_ > 0) {
112
- // just ignore the response
105
+ for (std::size_t idx = 1U; idx <= config.num_replicas.value_or(0U); ++idx) {
106
+ document_id replica_id{ id };
107
+ replica_id.node_index(idx);
108
+ core->execute(impl::lookup_in_replica_request{ std::move(replica_id), specs, timeout, parent_span },
109
+ [ctx](impl::lookup_in_replica_response&& resp) {
110
+ handler_type local_handler;
111
+ {
112
+ std::scoped_lock lock(ctx->mutex_);
113
+ if (ctx->done_) {
114
+ return;
115
+ }
116
+ --ctx->expected_responses_;
117
+ if (resp.ctx.ec()) {
118
+ if (ctx->expected_responses_ > 0) {
119
+ // just ignore the response
120
+ return;
121
+ }
122
+ // consider document irretrievable and give up
123
+ resp.ctx.override_ec(errc::key_value::document_irretrievable);
124
+ }
125
+ ctx->done_ = true;
126
+ std::swap(local_handler, ctx->handler_);
127
+ }
128
+ if (local_handler) {
129
+ response_type res{};
130
+ res.ctx = resp.ctx;
131
+ res.cas = resp.cas;
132
+ res.deleted = resp.deleted;
133
+ res.is_replica = true;
134
+ for (auto& field : resp.fields) {
135
+ auto lookup_in_entry = lookup_in_any_replica_response::entry{};
136
+ lookup_in_entry.path = field.path;
137
+ lookup_in_entry.value = field.value;
138
+ lookup_in_entry.status = field.status;
139
+ lookup_in_entry.ec = field.ec;
140
+ lookup_in_entry.exists = field.exists;
141
+ lookup_in_entry.original_index = field.original_index;
142
+ lookup_in_entry.opcode = field.opcode;
143
+ res.fields.emplace_back(lookup_in_entry);
144
+ }
145
+ return local_handler(res);
146
+ }
147
+ });
148
+ }
149
+ core->execute(lookup_in_request{ id, {}, {}, false, specs, timeout }, [ctx](lookup_in_response&& resp) {
150
+ handler_type local_handler{};
151
+ {
152
+ std::scoped_lock lock(ctx->mutex_);
153
+ if (ctx->done_) {
113
154
  return;
114
155
  }
115
- // consider document irretrievable and give up
116
- resp.ctx.override_ec(errc::key_value::document_irretrievable);
156
+ --ctx->expected_responses_;
157
+ if (resp.ctx.ec()) {
158
+ if (ctx->expected_responses_ > 0) {
159
+ // just ignore the response
160
+ return;
161
+ }
162
+ // consider document irretrievable and give up
163
+ resp.ctx.override_ec(errc::key_value::document_irretrievable);
164
+ }
165
+ ctx->done_ = true;
166
+ std::swap(local_handler, ctx->handler_);
117
167
  }
118
- ctx->done_ = true;
119
- std::swap(local_handler, ctx->handler_);
120
- }
121
- if (local_handler) {
122
- response_type res{};
123
- res.ctx = resp.ctx;
124
- res.cas = resp.cas;
125
- res.deleted = resp.deleted;
126
- res.is_replica = true;
127
- for (auto& field : resp.fields) {
128
- auto lookup_in_entry = lookup_in_any_replica_response::entry{};
129
- lookup_in_entry.path = field.path;
130
- lookup_in_entry.value = field.value;
131
- lookup_in_entry.status = field.status;
132
- lookup_in_entry.ec = field.ec;
133
- lookup_in_entry.exists = field.exists;
134
- lookup_in_entry.original_index = field.original_index;
135
- lookup_in_entry.opcode = field.opcode;
136
- res.fields.emplace_back(lookup_in_entry);
168
+ if (local_handler) {
169
+ auto res = response_type{};
170
+ res.ctx = resp.ctx;
171
+ res.cas = resp.cas;
172
+ res.deleted = resp.deleted;
173
+ res.is_replica = false;
174
+ for (auto& field : resp.fields) {
175
+ auto lookup_in_entry = lookup_in_any_replica_response::entry{};
176
+ lookup_in_entry.path = field.path;
177
+ lookup_in_entry.value = field.value;
178
+ lookup_in_entry.status = field.status;
179
+ lookup_in_entry.ec = field.ec;
180
+ lookup_in_entry.exists = field.exists;
181
+ lookup_in_entry.original_index = field.original_index;
182
+ lookup_in_entry.opcode = field.opcode;
183
+ res.fields.emplace_back(lookup_in_entry);
184
+ }
185
+ return local_handler(res);
137
186
  }
138
- return local_handler(res);
139
- }
187
+ });
140
188
  });
141
- }
142
- core->execute(lookup_in_request{ id, {}, {}, false, specs, timeout }, [ctx](lookup_in_response&& resp) {
143
- handler_type local_handler{};
144
- {
145
- std::scoped_lock lock(ctx->mutex_);
146
- if (ctx->done_) {
147
- return;
148
- }
149
- --ctx->expected_responses_;
150
- if (resp.ctx.ec()) {
151
- if (ctx->expected_responses_ > 0) {
152
- // just ignore the response
153
- return;
154
- }
155
- // consider document irretrievable and give up
156
- resp.ctx.override_ec(errc::key_value::document_irretrievable);
157
- }
158
- ctx->done_ = true;
159
- std::swap(local_handler, ctx->handler_);
160
- }
161
- if (local_handler) {
162
- auto res = response_type{};
163
- res.ctx = resp.ctx;
164
- res.cas = resp.cas;
165
- res.deleted = resp.deleted;
166
- res.is_replica = false;
167
- for (auto& field : resp.fields) {
168
- auto lookup_in_entry = lookup_in_any_replica_response::entry{};
169
- lookup_in_entry.path = field.path;
170
- lookup_in_entry.value = field.value;
171
- lookup_in_entry.status = field.status;
172
- lookup_in_entry.ec = field.ec;
173
- lookup_in_entry.exists = field.exists;
174
- lookup_in_entry.original_index = field.original_index;
175
- lookup_in_entry.opcode = field.opcode;
176
- res.fields.emplace_back(lookup_in_entry);
177
- }
178
- return local_handler(res);
179
- }
180
- });
181
- });
189
+ });
182
190
  }
183
191
  };
184
192
 
@@ -133,10 +133,10 @@ search_request::encode_to(search_request::encoded_request_type& encoded, http_co
133
133
  encoded.method = "POST";
134
134
  body_str = utils::json::generate(body);
135
135
  encoded.body = body_str;
136
- if (context.options.show_queries) {
137
- CB_LOG_INFO("SEARCH: {}", utils::json::generate(body["query"]));
136
+ if (context.options.show_queries || (log_request.has_value() && log_request.value())) {
137
+ CB_LOG_INFO("SEARCH: {}", utils::json::generate(body));
138
138
  } else {
139
- CB_LOG_DEBUG("SEARCH: {}", utils::json::generate(body["query"]));
139
+ CB_LOG_DEBUG("SEARCH: {}", utils::json::generate(body));
140
140
  }
141
141
  if (row_callback) {
142
142
  encoded.streaming.emplace(couchbase::core::io::streaming_settings{
@@ -165,6 +165,9 @@ search_request::make_response(error_context::search&& ctx, const encoded_respons
165
165
  response.ctx.ec = errc::common::parsing_failure;
166
166
  return response;
167
167
  }
168
+ if (log_response.has_value() && log_response.value()) {
169
+ CB_LOG_INFO("SEARCH RESPONSE: {}", utils::json::generate(payload));
170
+ }
168
171
  response.meta.metrics.took = std::chrono::nanoseconds(payload.at("took").get_unsigned());
169
172
  response.meta.metrics.max_score = payload.at("max_score").as<double>();
170
173
  response.meta.metrics.total_rows = payload.at("total_hits").get_unsigned();
@@ -187,106 +190,119 @@ search_request::make_response(error_context::search&& ctx, const encoded_respons
187
190
  return response;
188
191
  }
189
192
 
190
- if (const auto* rows = payload.find("hits"); rows != nullptr && rows->is_array()) {
191
- for (const auto& entry : rows->get_array()) {
192
- search_response::search_row row{};
193
- row.index = entry.at("index").get_string();
194
- row.id = entry.at("id").get_string();
195
- row.score = entry.at("score").as<double>();
196
- if (const auto* locations_map = entry.find("locations"); locations_map != nullptr && locations_map->is_object()) {
197
- for (const auto& [field, terms] : locations_map->get_object()) {
198
- for (const auto& [term, locations] : terms.get_object()) {
199
- for (const auto& loc : locations.get_array()) {
200
- search_response::search_location location{};
201
- location.field = field;
202
- location.term = term;
203
- location.position = loc.at("pos").get_unsigned();
204
- location.start_offset = loc.at("start").get_unsigned();
205
- location.end_offset = loc.at("end").get_unsigned();
206
- if (const auto* array_positions = loc.find("array_positions");
207
- array_positions != nullptr && array_positions->is_array()) {
208
- location.array_positions.emplace(array_positions->as<std::vector<std::uint64_t>>());
193
+ try {
194
+ if (const auto* rows = payload.find("hits"); rows != nullptr && rows->is_array()) {
195
+ for (const auto& entry : rows->get_array()) {
196
+ search_response::search_row row{};
197
+ row.index = entry.optional<std::string>("index").value_or(std::string());
198
+ row.id = entry.optional<std::string>("id").value_or(std::string());
199
+ row.score = entry.optional<double>("score").value_or(0);
200
+ if (const auto* locations_map = entry.find("locations"); locations_map != nullptr && locations_map->is_object()) {
201
+ for (const auto& [field, terms] : locations_map->get_object()) {
202
+ for (const auto& [term, locations] : terms.get_object()) {
203
+ for (const auto& loc : locations.get_array()) {
204
+ search_response::search_location location{};
205
+ location.field = field;
206
+ location.term = term;
207
+ location.position = loc.at("pos").get_unsigned();
208
+ location.start_offset = loc.at("start").get_unsigned();
209
+ location.end_offset = loc.at("end").get_unsigned();
210
+ if (const auto* array_positions = loc.find("array_positions");
211
+ array_positions != nullptr && array_positions->is_array()) {
212
+ location.array_positions.emplace(array_positions->as<std::vector<std::uint64_t>>());
213
+ }
214
+ row.locations.emplace_back(location);
209
215
  }
210
- row.locations.emplace_back(location);
211
216
  }
212
217
  }
213
218
  }
214
- }
215
219
 
216
- if (const auto* fragments_map = entry.find("fragments"); fragments_map != nullptr && fragments_map->is_object()) {
217
- for (const auto& [field, fragments] : fragments_map->get_object()) {
218
- row.fragments.try_emplace(field, fragments.as<std::vector<std::string>>());
220
+ if (const auto* fragments_map = entry.find("fragments"); fragments_map != nullptr && fragments_map->is_object()) {
221
+ for (const auto& [field, fragments] : fragments_map->get_object()) {
222
+ row.fragments.try_emplace(field, fragments.as<std::vector<std::string>>());
223
+ }
219
224
  }
225
+ if (const auto* response_fields = entry.find("fields");
226
+ response_fields != nullptr && response_fields->is_object()) {
227
+ row.fields = utils::json::generate(*response_fields);
228
+ }
229
+ if (const auto* explanation = entry.find("explanation"); explanation != nullptr && explanation->is_object()) {
230
+ row.explanation = utils::json::generate(*explanation);
231
+ }
232
+ response.rows.emplace_back(row);
220
233
  }
221
- if (const auto* response_fields = entry.find("fields"); response_fields != nullptr && response_fields->is_object()) {
222
- row.fields = utils::json::generate(*response_fields);
223
- }
224
- if (const auto* explanation = entry.find("explanation"); explanation != nullptr && explanation->is_object()) {
225
- row.explanation = utils::json::generate(*explanation);
226
- }
227
- response.rows.emplace_back(row);
228
234
  }
235
+ } catch (const std::out_of_range& e) {
236
+ CB_LOG_ERROR("Error parsing search results. Error: {}.", e.what());
237
+ response.ctx.ec = errc::common::parsing_failure;
238
+ return response;
229
239
  }
230
240
 
231
- if (const auto* response_facets = payload.find("facets"); response_facets != nullptr && response_facets->is_object()) {
232
- for (const auto& [name, object] : response_facets->get_object()) {
233
- search_response::search_facet facet;
234
- facet.name = name;
235
- facet.field = object.at("field").get_string();
236
- facet.total = object.at("total").get_unsigned();
237
- facet.missing = object.at("missing").get_unsigned();
238
- facet.other = object.at("other").get_unsigned();
241
+ try {
242
+ if (const auto* response_facets = payload.find("facets"); response_facets != nullptr && response_facets->is_object()) {
243
+ for (const auto& [name, object] : response_facets->get_object()) {
244
+ search_response::search_facet facet;
245
+ facet.name = name;
246
+ facet.field = object.at("field").get_string();
247
+ facet.total = object.at("total").get_unsigned();
248
+ facet.missing = object.at("missing").get_unsigned();
249
+ facet.other = object.at("other").get_unsigned();
239
250
 
240
- if (const auto* date_ranges = object.find("date_ranges"); date_ranges != nullptr && date_ranges->is_array()) {
241
- for (const auto& date_range : date_ranges->get_array()) {
242
- search_response::search_facet::date_range_facet date_range_facet;
243
- date_range_facet.name = date_range.at("name").get_string();
244
- date_range_facet.count = date_range.at("count").get_unsigned();
245
- if (const auto* start = date_range.find("start"); start != nullptr && start->is_string()) {
246
- date_range_facet.start = start->get_string();
247
- }
248
- if (const auto* end = date_range.find("end"); end != nullptr && end->is_string()) {
249
- date_range_facet.end = end->get_string();
251
+ if (const auto* date_ranges = object.find("date_ranges"); date_ranges != nullptr && date_ranges->is_array()) {
252
+ for (const auto& date_range : date_ranges->get_array()) {
253
+ search_response::search_facet::date_range_facet date_range_facet;
254
+ date_range_facet.name = date_range.at("name").get_string();
255
+ date_range_facet.count = date_range.at("count").get_unsigned();
256
+ if (const auto* start = date_range.find("start"); start != nullptr && start->is_string()) {
257
+ date_range_facet.start = start->get_string();
258
+ }
259
+ if (const auto* end = date_range.find("end"); end != nullptr && end->is_string()) {
260
+ date_range_facet.end = end->get_string();
261
+ }
262
+ facet.date_ranges.emplace_back(date_range_facet);
250
263
  }
251
- facet.date_ranges.emplace_back(date_range_facet);
252
264
  }
253
- }
254
265
 
255
- if (const auto& numeric_ranges = object.find("numeric_ranges");
256
- numeric_ranges != nullptr && numeric_ranges->is_array()) {
257
- for (const auto& numeric_range : numeric_ranges->get_array()) {
258
- search_response::search_facet::numeric_range_facet numeric_range_facet;
259
- numeric_range_facet.name = numeric_range.at("name").get_string();
260
- numeric_range_facet.count = numeric_range.at("count").get_unsigned();
261
- if (const auto* min = numeric_range.find("min"); min != nullptr) {
262
- if (min->is_double()) {
263
- numeric_range_facet.min = min->as<double>();
264
- } else if (min->is_integer()) {
265
- numeric_range_facet.min = min->get_unsigned();
266
+ if (const auto& numeric_ranges = object.find("numeric_ranges");
267
+ numeric_ranges != nullptr && numeric_ranges->is_array()) {
268
+ for (const auto& numeric_range : numeric_ranges->get_array()) {
269
+ search_response::search_facet::numeric_range_facet numeric_range_facet;
270
+ numeric_range_facet.name = numeric_range.at("name").get_string();
271
+ numeric_range_facet.count = numeric_range.at("count").get_unsigned();
272
+ if (const auto* min = numeric_range.find("min"); min != nullptr) {
273
+ if (min->is_double()) {
274
+ numeric_range_facet.min = min->as<double>();
275
+ } else if (min->is_integer()) {
276
+ numeric_range_facet.min = min->get_unsigned();
277
+ }
266
278
  }
267
- }
268
- if (const auto* max = numeric_range.find("max"); max != nullptr) {
269
- if (max->is_double()) {
270
- numeric_range_facet.max = max->as<double>();
271
- } else if (max->is_integer()) {
272
- numeric_range_facet.max = max->get_unsigned();
279
+ if (const auto* max = numeric_range.find("max"); max != nullptr) {
280
+ if (max->is_double()) {
281
+ numeric_range_facet.max = max->as<double>();
282
+ } else if (max->is_integer()) {
283
+ numeric_range_facet.max = max->get_unsigned();
284
+ }
273
285
  }
286
+ facet.numeric_ranges.emplace_back(numeric_range_facet);
274
287
  }
275
- facet.numeric_ranges.emplace_back(numeric_range_facet);
276
288
  }
277
- }
278
289
 
279
- if (const auto* terms = object.find("terms"); terms != nullptr && terms->is_array()) {
280
- for (const auto& term : terms->get_array()) {
281
- search_response::search_facet::term_facet term_facet;
282
- term_facet.term = term.at("term").get_string();
283
- term_facet.count = term.at("count").get_unsigned();
284
- facet.terms.emplace_back(term_facet);
290
+ if (const auto* terms = object.find("terms"); terms != nullptr && terms->is_array()) {
291
+ for (const auto& term : terms->get_array()) {
292
+ search_response::search_facet::term_facet term_facet;
293
+ term_facet.term = term.at("term").get_string();
294
+ term_facet.count = term.at("count").get_unsigned();
295
+ facet.terms.emplace_back(term_facet);
296
+ }
285
297
  }
286
- }
287
298
 
288
- response.facets.emplace_back(facet);
299
+ response.facets.emplace_back(facet);
300
+ }
289
301
  }
302
+ } catch (const std::out_of_range& e) {
303
+ CB_LOG_ERROR("Error parsing search facets. Error: {}.", e.what());
304
+ response.ctx.ec = errc::common::parsing_failure;
305
+ return response;
290
306
  }
291
307
  return response;
292
308
  }
@@ -155,6 +155,11 @@ struct search_request {
155
155
  std::optional<std::function<utils::json::stream_control(std::string)>> row_callback{};
156
156
  std::optional<std::string> client_context_id{};
157
157
  std::optional<std::chrono::milliseconds> timeout{};
158
+ /**
159
+ * UNCOMMITTED: If set to true, will log the request to and/or the response from the search service.
160
+ */
161
+ std::optional<bool> log_request{ false };
162
+ std::optional<bool> log_response{ false };
158
163
 
159
164
  [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, http_context& context);
160
165
 
@@ -18,6 +18,7 @@
18
18
  #include "range_scan_options.hxx"
19
19
  #include "timeout_defaults.hxx"
20
20
 
21
+ #include <couchbase/best_effort_retry_strategy.hxx>
21
22
  #include <couchbase/mutation_token.hxx>
22
23
  #include <couchbase/retry_strategy.hxx>
23
24
 
@@ -51,7 +52,7 @@ struct range_scan_orchestrator_options {
51
52
  std::uint32_t batch_byte_limit{ range_scan_continue_options::default_batch_byte_limit };
52
53
  std::uint16_t concurrency{ default_concurrency };
53
54
 
54
- std::shared_ptr<couchbase::retry_strategy> retry_strategy{ nullptr };
55
+ std::shared_ptr<couchbase::retry_strategy> retry_strategy{ make_best_effort_retry_strategy() };
55
56
  std::chrono::milliseconds timeout{ timeout_defaults::key_value_scan_timeout };
56
57
  std::shared_ptr<couchbase::tracing::request_span> parent_span{};
57
58
  };
@@ -131,12 +131,13 @@ atr_cleanup_entry::check_atr_and_cleanup(transactions_cleanup_attempt* result)
131
131
  throw *err;
132
132
  }
133
133
  cleanup_docs(durability_level);
134
- auto ec = cleanup_->config().cleanup_hooks->on_cleanup_docs_completed();
134
+ auto ec =
135
+ wait_for_hook([this](auto handler) { return cleanup_->config().cleanup_hooks->on_cleanup_docs_completed(std::move(handler)); });
135
136
  if (ec) {
136
137
  throw client_error(*ec, "on_cleanup_docs_completed hook threw error");
137
138
  }
138
139
  cleanup_entry(durability_level);
139
- ec = cleanup_->config().cleanup_hooks->on_cleanup_completed();
140
+ ec = wait_for_hook([this](auto handler) { return cleanup_->config().cleanup_hooks->on_cleanup_completed(std::move(handler)); });
140
141
  if (ec) {
141
142
  throw client_error(*ec, "on_cleanup_completed hook threw error");
142
143
  }
@@ -246,7 +247,9 @@ atr_cleanup_entry::commit_docs(std::optional<std::vector<doc_record>> docs, dura
246
247
  do_per_doc(*docs, true, [&](transaction_get_result& doc, bool) {
247
248
  if (doc.links().has_staged_content()) {
248
249
  auto content = doc.links().staged_content();
249
- auto ec = cleanup_->config().cleanup_hooks->before_commit_doc(doc.id().key());
250
+ auto ec = wait_for_hook([this, key = doc.id().key()](auto handler) {
251
+ return cleanup_->config().cleanup_hooks->before_commit_doc(key, std::move(handler));
252
+ });
250
253
  if (ec) {
251
254
  throw client_error(*ec, "before_commit_doc hook threw error");
252
255
  }
@@ -288,7 +291,9 @@ atr_cleanup_entry::remove_docs(std::optional<std::vector<doc_record>> docs, dura
288
291
  {
289
292
  if (docs) {
290
293
  do_per_doc(*docs, true, [&](transaction_get_result& doc, bool is_deleted) {
291
- auto ec = cleanup_->config().cleanup_hooks->before_remove_doc(doc.id().key());
294
+ auto ec = wait_for_hook([this, key = doc.id().key()](auto handler) mutable {
295
+ return cleanup_->config().cleanup_hooks->before_remove_doc(key, std::move(handler));
296
+ });
292
297
  if (ec) {
293
298
  throw client_error(*ec, "before_remove_doc hook threw error");
294
299
  }
@@ -330,7 +335,9 @@ atr_cleanup_entry::remove_docs_staged_for_removal(std::optional<std::vector<doc_
330
335
  if (docs) {
331
336
  do_per_doc(*docs, true, [&](transaction_get_result& doc, bool) {
332
337
  if (doc.links().is_document_being_removed()) {
333
- auto ec = cleanup_->config().cleanup_hooks->before_remove_doc_staged_for_removal(doc.id().key());
338
+ auto ec = wait_for_hook([this, key = doc.id().key()](auto handler) mutable {
339
+ return cleanup_->config().cleanup_hooks->before_remove_doc_staged_for_removal(key, std::move(handler));
340
+ });
334
341
  if (ec) {
335
342
  throw client_error(*ec, "before_remove_doc_staged_for_removal hook threw error");
336
343
  }
@@ -358,7 +365,9 @@ atr_cleanup_entry::remove_txn_links(std::optional<std::vector<doc_record>> docs,
358
365
  {
359
366
  if (docs) {
360
367
  do_per_doc(*docs, false, [&](transaction_get_result& doc, bool) {
361
- auto ec = cleanup_->config().cleanup_hooks->before_remove_links(doc.id().key());
368
+ auto ec = wait_for_hook([this, key = doc.id().key()](auto handler) mutable {
369
+ return cleanup_->config().cleanup_hooks->before_remove_links(key, std::move(handler));
370
+ });
362
371
  if (ec) {
363
372
  throw client_error(*ec, "before_remove_links hook threw error");
364
373
  }
@@ -385,7 +394,7 @@ void
385
394
  atr_cleanup_entry::cleanup_entry(durability_level dl)
386
395
  {
387
396
  try {
388
- auto ec = cleanup_->config().cleanup_hooks->before_atr_remove();
397
+ auto ec = wait_for_hook([this](auto handler) { return cleanup_->config().cleanup_hooks->before_atr_remove(std::move(handler)); });
389
398
  if (ec) {
390
399
  throw client_error(*ec, "before_atr_remove hook threw error");
391
400
  }