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
@@ -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
  }