couchbase 3.4.3 → 3.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +15 -1
  4. data/ext/couchbase/core/bucket.cxx +183 -152
  5. data/ext/couchbase/core/bucket.hxx +17 -4
  6. data/ext/couchbase/core/cluster.hxx +34 -13
  7. data/ext/couchbase/core/cluster_options.hxx +3 -0
  8. data/ext/couchbase/core/crud_component.cxx +51 -22
  9. data/ext/couchbase/core/error_context/key_value.cxx +2 -1
  10. data/ext/couchbase/core/error_context/key_value.hxx +10 -12
  11. data/ext/couchbase/core/impl/build_deferred_query_indexes.cxx +115 -50
  12. data/ext/couchbase/core/impl/cluster.cxx +6 -0
  13. data/ext/couchbase/core/impl/create_bucket.cxx +155 -0
  14. data/ext/couchbase/core/impl/create_query_index.cxx +172 -59
  15. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +2 -1
  16. data/ext/couchbase/core/impl/drop_bucket.cxx +66 -0
  17. data/ext/couchbase/core/impl/drop_query_index.cxx +138 -59
  18. data/ext/couchbase/core/impl/flush_bucket.cxx +66 -0
  19. data/ext/couchbase/core/impl/get_all_buckets.cxx +163 -0
  20. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +67 -37
  21. data/ext/couchbase/core/impl/get_bucket.cxx +153 -0
  22. data/ext/couchbase/core/impl/internal_manager_error_context.cxx +113 -0
  23. data/ext/couchbase/core/impl/internal_manager_error_context.hxx +60 -0
  24. data/ext/couchbase/core/impl/key_value_error_category.cxx +2 -4
  25. data/ext/couchbase/core/impl/key_value_error_context.cxx +98 -0
  26. data/ext/couchbase/core/impl/lookup_in.cxx +1 -0
  27. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +176 -0
  28. data/ext/couchbase/core/impl/lookup_in_all_replicas.hxx +80 -0
  29. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +167 -0
  30. data/ext/couchbase/core/impl/lookup_in_any_replica.hxx +75 -0
  31. data/ext/couchbase/core/impl/lookup_in_replica.cxx +97 -0
  32. data/ext/couchbase/core/impl/lookup_in_replica.hxx +67 -0
  33. data/ext/couchbase/core/impl/manager_error_context.cxx +100 -0
  34. data/ext/couchbase/core/impl/query.cxx +1 -0
  35. data/ext/couchbase/core/impl/query_error_context.cxx +75 -0
  36. data/ext/couchbase/core/impl/update_bucket.cxx +130 -0
  37. data/ext/couchbase/core/impl/watch_query_indexes.cxx +53 -29
  38. data/ext/couchbase/core/io/dns_client.cxx +111 -40
  39. data/ext/couchbase/core/io/dns_config.cxx +5 -4
  40. data/ext/couchbase/core/io/http_session.hxx +24 -1
  41. data/ext/couchbase/core/io/mcbp_command.hxx +9 -2
  42. data/ext/couchbase/core/io/mcbp_session.cxx +80 -43
  43. data/ext/couchbase/core/io/mcbp_session.hxx +4 -3
  44. data/ext/couchbase/core/logger/custom_rotating_file_sink.cxx +1 -1
  45. data/ext/couchbase/core/logger/logger.cxx +80 -20
  46. data/ext/couchbase/core/logger/logger.hxx +31 -0
  47. data/ext/couchbase/core/meta/features.hxx +25 -0
  48. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  49. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +188 -0
  50. data/ext/couchbase/core/operations/document_query.cxx +11 -0
  51. data/ext/couchbase/core/operations/document_query.hxx +1 -0
  52. data/ext/couchbase/core/operations.hxx +2 -0
  53. data/ext/couchbase/core/origin.cxx +270 -0
  54. data/ext/couchbase/core/origin.hxx +2 -0
  55. data/ext/couchbase/core/protocol/client_response.hxx +1 -0
  56. data/ext/couchbase/core/protocol/cmd_hello.hxx +1 -0
  57. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  58. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  59. data/ext/couchbase/core/protocol/hello_feature.hxx +6 -0
  60. data/ext/couchbase/core/protocol/hello_feature_fmt.hxx +3 -0
  61. data/ext/couchbase/core/protocol/status.cxx +2 -2
  62. data/ext/couchbase/core/range_scan_options.cxx +3 -27
  63. data/ext/couchbase/core/range_scan_options.hxx +13 -17
  64. data/ext/couchbase/core/range_scan_orchestrator.cxx +388 -170
  65. data/ext/couchbase/core/range_scan_orchestrator.hxx +13 -2
  66. data/ext/couchbase/core/range_scan_orchestrator_options.hxx +5 -3
  67. data/ext/couchbase/core/scan_options.hxx +0 -19
  68. data/ext/couchbase/core/scan_result.cxx +19 -5
  69. data/ext/couchbase/core/scan_result.hxx +5 -2
  70. data/ext/couchbase/core/timeout_defaults.hxx +2 -3
  71. data/ext/couchbase/core/topology/capabilities.hxx +3 -0
  72. data/ext/couchbase/core/topology/capabilities_fmt.hxx +8 -0
  73. data/ext/couchbase/core/topology/collections_manifest_fmt.hxx +1 -1
  74. data/ext/couchbase/core/topology/configuration.hxx +15 -0
  75. data/ext/couchbase/core/topology/configuration_json.hxx +6 -1
  76. data/ext/couchbase/core/utils/connection_string.cxx +62 -47
  77. data/ext/couchbase/core/utils/connection_string.hxx +1 -0
  78. data/ext/couchbase/couchbase/analytics_error_context.hxx +1 -1
  79. data/ext/couchbase/couchbase/behavior_options.hxx +19 -2
  80. data/ext/couchbase/couchbase/bucket_manager.hxx +135 -0
  81. data/ext/couchbase/couchbase/build_query_index_options.hxx +0 -30
  82. data/ext/couchbase/couchbase/cluster.hxx +14 -0
  83. data/ext/couchbase/couchbase/collection.hxx +111 -0
  84. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +7 -48
  85. data/ext/couchbase/couchbase/create_bucket_options.hxx +41 -0
  86. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +0 -29
  87. data/ext/couchbase/couchbase/create_query_index_options.hxx +0 -33
  88. data/ext/couchbase/couchbase/drop_bucket_options.hxx +41 -0
  89. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +0 -30
  90. data/ext/couchbase/couchbase/drop_query_index_options.hxx +0 -31
  91. data/ext/couchbase/couchbase/error_codes.hxx +1 -2
  92. data/ext/couchbase/couchbase/error_context.hxx +10 -2
  93. data/ext/couchbase/couchbase/flush_bucket_options.hxx +41 -0
  94. data/ext/couchbase/{core/topology/error_map_fmt.hxx → couchbase/fmt/key_value_error_map_attribute.hxx} +21 -21
  95. data/ext/couchbase/couchbase/get_all_buckets_options.hxx +44 -0
  96. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +0 -30
  97. data/ext/couchbase/couchbase/get_and_lock_options.hxx +2 -2
  98. data/ext/couchbase/couchbase/get_and_touch_options.hxx +2 -2
  99. data/ext/couchbase/couchbase/get_bucket_options.hxx +43 -0
  100. data/ext/couchbase/couchbase/get_options.hxx +2 -2
  101. data/ext/couchbase/couchbase/insert_options.hxx +3 -3
  102. data/ext/couchbase/couchbase/key_value_error_context.hxx +7 -2
  103. data/ext/couchbase/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  104. data/ext/couchbase/couchbase/lookup_in_any_replica_options.hxx +101 -0
  105. data/ext/couchbase/couchbase/lookup_in_options.hxx +2 -2
  106. data/ext/couchbase/couchbase/lookup_in_replica_result.hxx +74 -0
  107. data/ext/couchbase/couchbase/lookup_in_result.hxx +26 -0
  108. data/ext/couchbase/couchbase/management/bucket_settings.hxx +116 -0
  109. data/ext/couchbase/couchbase/manager_error_context.hxx +29 -53
  110. data/ext/couchbase/couchbase/mutate_in_options.hxx +2 -2
  111. data/ext/couchbase/couchbase/query_error_context.hxx +3 -1
  112. data/ext/couchbase/couchbase/query_index_manager.hxx +16 -83
  113. data/ext/couchbase/couchbase/query_options.hxx +18 -0
  114. data/ext/couchbase/couchbase/remove_options.hxx +2 -2
  115. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  116. data/ext/couchbase/couchbase/security_options.hxx +15 -0
  117. data/ext/couchbase/couchbase/subdocument_error_context.hxx +4 -2
  118. data/ext/couchbase/couchbase/touch_options.hxx +2 -2
  119. data/ext/couchbase/couchbase/unlock_options.hxx +2 -2
  120. data/ext/couchbase/couchbase/update_bucket_options.hxx +41 -0
  121. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  122. data/ext/couchbase/couchbase/watch_query_indexes_options.hxx +0 -31
  123. data/ext/couchbase/test/CMakeLists.txt +1 -0
  124. data/ext/couchbase/test/test_integration_collections.cxx +6 -0
  125. data/ext/couchbase/test/test_integration_crud.cxx +5 -0
  126. data/ext/couchbase/test/test_integration_examples.cxx +137 -1
  127. data/ext/couchbase/test/test_integration_management.cxx +709 -266
  128. data/ext/couchbase/test/test_integration_query.cxx +19 -7
  129. data/ext/couchbase/test/test_integration_range_scan.cxx +351 -112
  130. data/ext/couchbase/test/test_integration_search.cxx +10 -1
  131. data/ext/couchbase/test/test_integration_subdoc.cxx +655 -0
  132. data/ext/couchbase/test/test_transaction_public_async_api.cxx +13 -12
  133. data/ext/couchbase/test/test_transaction_public_blocking_api.cxx +27 -21
  134. data/ext/couchbase/test/test_unit_connection_string.cxx +29 -0
  135. data/ext/couchbase/test/test_unit_query.cxx +75 -0
  136. data/ext/couchbase.cxx +583 -29
  137. data/ext/revisions.rb +3 -3
  138. data/lib/couchbase/cluster.rb +1 -1
  139. data/lib/couchbase/collection.rb +108 -0
  140. data/lib/couchbase/collection_options.rb +100 -0
  141. data/lib/couchbase/errors.rb +5 -0
  142. data/lib/couchbase/key_value_scan.rb +125 -0
  143. data/lib/couchbase/options.rb +151 -0
  144. data/lib/couchbase/scope.rb +1 -1
  145. data/lib/couchbase/utils/time.rb +14 -1
  146. data/lib/couchbase/version.rb +1 -1
  147. metadata +41 -7
  148. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +0 -93
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 573df37049b91d6dc00b674904dbda7883a4a20e7078191fc01f3b29f139df13
4
- data.tar.gz: ec07bdbe1a3cdd5caab78b7bc4af3e525d0dba25e54e8bfb13b5d07203c0361a
3
+ metadata.gz: b3c1e2edfe7f74eb3f3094dd526e7dc1896f3002caca935ddeb76923b593f341
4
+ data.tar.gz: b5409f7083156b38aee870037ce0c5d3a14dc7ffe3aa3ac64b5862245b47e4d5
5
5
  SHA512:
6
- metadata.gz: 3b945f9a1942520c04fb22810049bda99e4daecccbadab60c9df474ec9d606a92f253f56e815739e3d64c927727d83d74265973fa72a6493ab38cc27df5f0913
7
- data.tar.gz: 7cfa5628c2dc3a4c43741e139e064ac20f1085fd446f3b10cc480fc622464d0396cfa31d470cdca9c7c8323bb37d6b38ef1200c4b8d4b511fea0642a56f699d5
6
+ metadata.gz: 16705649dc3954db461a7f8a63ee1168a4b191cacb0744e4e5b5b7b110788491d42fab78611b4898078a0276bbe0d9930352e24b4f95db87ab79ed6b6e8f9101
7
+ data.tar.gz: 157af7be99dc745ef7d95ef45490c0025f16ead6abb2f75079188405e82fcec9a64abb681695720e9080fd36b3c17883a56f84bafd06062e56c22ad9286c7ed4
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![license](https://img.shields.io/github/license/couchbase/couchbase-ruby-client?color=brightgreen)](https://opensource.org/licenses/Apache-2.0)
4
4
  [![gem](https://img.shields.io/gem/v/couchbase?color=brightgreen)](https://rubygems.org/gems/couchbase)
5
- [![commits](https://img.shields.io/github/commits-since/couchbase/couchbase-ruby-client/latest?color=brightgreen)](https://github.com/couchbase/couchbase-ruby-client/commits/master)
5
+ [![commits](https://img.shields.io/github/commits-since/couchbase/couchbase-ruby-client/latest?color=brightgreen)](https://github.com/couchbase/couchbase-ruby-client/commits/main)
6
6
  [![linters](https://img.shields.io/github/actions/workflow/status/couchbase/couchbase-ruby-client/linters.yml?branch=main&label=linters)](https://github.com/couchbase/couchbase-ruby-client/actions?query=workflow%3Alinters)
7
7
 
8
8
  This repository contains the third generation of the official Couchbase SDK for Ruby (aka. SDKv3)
@@ -23,7 +23,7 @@ The library has been tested with MRI 3.0, 3.1 and 3.2. Supported platforms are L
23
23
  Add this line to your application's Gemfile:
24
24
 
25
25
  ```ruby
26
- gem "couchbase", "3.4.3"
26
+ gem "couchbase", "3.4.4"
27
27
  ```
28
28
 
29
29
  And then execute:
@@ -100,6 +100,7 @@ set(couchbase_cxx_client_FILES
100
100
  core/free_form_http_request.cxx
101
101
  core/key_value_config.cxx
102
102
  core/n1ql_query_options.cxx
103
+ core/origin.cxx
103
104
  core/range_scan_options.cxx
104
105
  core/range_scan_orchestrator.cxx
105
106
  core/retry_orchestrator.cxx
@@ -232,6 +233,7 @@ set(couchbase_cxx_client_FILES
232
233
  core/protocol/cmd_increment.cxx
233
234
  core/protocol/cmd_insert.cxx
234
235
  core/protocol/cmd_lookup_in.cxx
236
+ core/protocol/cmd_lookup_in_replica.cxx
235
237
  core/protocol/cmd_mutate_in.cxx
236
238
  core/protocol/cmd_noop.cxx
237
239
  core/protocol/cmd_observe_seqno.cxx
@@ -265,10 +267,10 @@ set(couchbase_cxx_client_FILES
265
267
  core/impl/boolean_query.cxx
266
268
  core/impl/build_deferred_query_indexes.cxx
267
269
  core/impl/cluster.cxx
268
- core/impl/collection_query_index_manager.cxx
269
270
  core/impl/common_error_category.cxx
270
271
  core/impl/configuration_profiles_registry.cxx
271
272
  core/impl/conjunction_query.cxx
273
+ core/impl/create_bucket.cxx
272
274
  core/impl/create_query_index.cxx
273
275
  core/impl/date_range.cxx
274
276
  core/impl/date_range_facet.cxx
@@ -277,24 +279,29 @@ set(couchbase_cxx_client_FILES
277
279
  core/impl/decrement.cxx
278
280
  core/impl/disjunction_query.cxx
279
281
  core/impl/dns_srv_tracker.cxx
282
+ core/impl/drop_bucket.cxx
280
283
  core/impl/drop_query_index.cxx
281
284
  core/impl/exists.cxx
282
285
  core/impl/expiry.cxx
283
286
  core/impl/fail_fast_retry_strategy.cxx
284
287
  core/impl/field_level_encryption_error_category.cxx
288
+ core/impl/flush_bucket.cxx
285
289
  core/impl/geo_bounding_box_query.cxx
286
290
  core/impl/geo_distance_query.cxx
287
291
  core/impl/geo_polygon_query.cxx
288
292
  core/impl/get.cxx
293
+ core/impl/get_all_buckets.cxx
289
294
  core/impl/get_all_query_indexes.cxx
290
295
  core/impl/get_all_replicas.cxx
291
296
  core/impl/get_and_lock.cxx
292
297
  core/impl/get_and_touch.cxx
293
298
  core/impl/get_any_replica.cxx
299
+ core/impl/get_bucket.cxx
294
300
  core/impl/get_replica.cxx
295
301
  core/impl/increment.cxx
296
302
  core/impl/insert.cxx
297
303
  core/impl/internal_date_range_facet_result.cxx
304
+ core/impl/internal_manager_error_context.cxx
298
305
  core/impl/internal_numeric_range_facet_result.cxx
299
306
  core/impl/internal_search_error_context.cxx
300
307
  core/impl/internal_search_meta_data.cxx
@@ -303,8 +310,13 @@ set(couchbase_cxx_client_FILES
303
310
  core/impl/internal_search_row_locations.cxx
304
311
  core/impl/internal_term_facet_result.cxx
305
312
  core/impl/key_value_error_category.cxx
313
+ core/impl/key_value_error_context.cxx
306
314
  core/impl/lookup_in.cxx
315
+ core/impl/lookup_in_replica.cxx
316
+ core/impl/lookup_in_all_replicas.cxx
317
+ core/impl/lookup_in_any_replica.cxx
307
318
  core/impl/management_error_category.cxx
319
+ core/impl/manager_error_context.cxx
308
320
  core/impl/match_all_query.cxx
309
321
  core/impl/match_none_query.cxx
310
322
  core/impl/match_phrase_query.cxx
@@ -322,6 +334,7 @@ set(couchbase_cxx_client_FILES
322
334
  core/impl/prepend.cxx
323
335
  core/impl/query.cxx
324
336
  core/impl/query_error_category.cxx
337
+ core/impl/query_error_context.cxx
325
338
  core/impl/query_string_query.cxx
326
339
  core/impl/regexp_query.cxx
327
340
  core/impl/remove.cxx
@@ -366,6 +379,7 @@ set(couchbase_cxx_client_FILES
366
379
  core/impl/transaction_get_result.cxx
367
380
  core/impl/transaction_op_error_category.cxx
368
381
  core/impl/unlock.cxx
382
+ core/impl/update_bucket.cxx
369
383
  core/impl/upsert.cxx
370
384
  core/impl/view_error_category.cxx
371
385
  core/impl/watch_query_indexes.cxx
@@ -22,7 +22,6 @@
22
22
  #include "core/mcbp/codec.hxx"
23
23
  #include "dispatcher.hxx"
24
24
  #include "impl/bootstrap_state_listener.hxx"
25
- #include "mcbp/completion_token.hxx"
26
25
  #include "mcbp/operation_queue.hxx"
27
26
  #include "mcbp/queue_request.hxx"
28
27
  #include "mcbp/queue_response.hxx"
@@ -41,28 +40,6 @@
41
40
 
42
41
  namespace couchbase::core
43
42
  {
44
- /**
45
- * copies nodes from rhs that are not in lhs to output vector
46
- */
47
- static void
48
- diff_nodes(const std::vector<topology::configuration::node>& lhs,
49
- const std::vector<topology::configuration::node>& rhs,
50
- std::vector<topology::configuration::node>& output)
51
- {
52
- for (const auto& re : rhs) {
53
- bool known = false;
54
- for (const auto& le : lhs) {
55
- if (le.hostname == re.hostname && le.services_plain.management.value_or(0) == re.services_plain.management.value_or(0)) {
56
- known = true;
57
- break;
58
- }
59
- }
60
- if (!known) {
61
- output.push_back(re);
62
- }
63
- }
64
- }
65
-
66
43
  class bucket_impl
67
44
  : public std::enable_shared_from_this<bucket_impl>
68
45
  , public config_listener
@@ -303,82 +280,111 @@ class bucket_impl
303
280
  return config_->map_key(key, node_index);
304
281
  }
305
282
 
306
- void restart_node(std::size_t index, const std::string& hostname, const std::string& port)
283
+ void restart_sessions()
307
284
  {
308
- if (closed_) {
309
- CB_LOG_DEBUG(R"({} requested to restart session, but the bucket has been closed already. idx={}, address="{}:{}")",
310
- log_prefix_,
311
- index,
312
- hostname,
313
- port);
285
+ const std::scoped_lock lock(config_mutex_, sessions_mutex_);
286
+ if (!config_.has_value()) {
314
287
  return;
315
288
  }
316
- {
317
- std::scoped_lock lock(config_mutex_);
318
- if (!config_->has_node(origin_.options().network, service_type::key_value, origin_.options().enable_tls, hostname, port)) {
319
- CB_LOG_TRACE(
320
- R"({} requested to restart session, but the node has been ejected from current configuration already. idx={}, network={}, address="{}:{}")",
321
- log_prefix_,
322
- index,
323
- origin_.options().network,
324
- hostname,
325
- port);
326
- return;
327
- }
328
- }
329
- couchbase::core::origin origin(origin_.credentials(), hostname, port, origin_.options());
330
289
 
331
- io::mcbp_session session = origin_.options().enable_tls
332
- ? io::mcbp_session(client_id_, ctx_, tls_, origin, state_listener_, name_, known_features_)
333
- : io::mcbp_session(client_id_, ctx_, origin, state_listener_, name_, known_features_);
290
+ std::size_t kv_node_index{ 0 };
291
+ for (std::size_t index = 0; index < config_->nodes.size(); ++index) {
292
+ const auto& node = config_->nodes[index];
334
293
 
335
- std::scoped_lock lock(sessions_mutex_);
336
- if (auto ptr = sessions_.find(index); ptr == sessions_.end()) {
337
- CB_LOG_DEBUG(R"({} requested to restart session idx={}, which does not exist yet, initiate new one id="{}", address="{}:{}")",
338
- log_prefix_,
339
- index,
340
- session.id(),
341
- hostname,
342
- port);
343
- } else {
344
- const auto& old_session = ptr->second;
345
- auto old_id = old_session.id();
346
- sessions_.erase(ptr);
347
- Expects(sessions_.count(index) == 0);
348
- CB_LOG_DEBUG(R"({} restarting session idx={}, id=("{}" -> "{}"), address="{}:{}")",
294
+ const auto& hostname = node.hostname_for(origin_.options().network);
295
+ auto port = node.port_or(origin_.options().network, service_type::key_value, origin_.options().enable_tls, 0);
296
+ if (port == 0) {
297
+ continue;
298
+ }
299
+
300
+ auto ptr = std::find_if(sessions_.begin(), sessions_.end(), [&hostname, &port](const auto& session) {
301
+ return session.second.bootstrap_hostname() == hostname && session.second.bootstrap_port_number() == port;
302
+ });
303
+ if (ptr != sessions_.end()) {
304
+
305
+ if (auto found_kv_node_index = ptr->first; found_kv_node_index != kv_node_index) {
306
+ if (auto current = sessions_.find(kv_node_index); current == sessions_.end()) {
307
+ CB_LOG_WARNING(R"({} KV node index mismatch: config rev={} states that address="{}:{}" should be at idx={}, )"
308
+ R"(but it is at idx={} ("{}"). Moving session to idx={}.)",
309
+ log_prefix_,
310
+ config_->rev_str(),
311
+ hostname,
312
+ port,
313
+ kv_node_index,
314
+ found_kv_node_index,
315
+ ptr->second.id(),
316
+ kv_node_index);
317
+ sessions_.insert_or_assign(kv_node_index, std::move(ptr->second));
318
+ sessions_.erase(ptr);
319
+ } else {
320
+ CB_LOG_WARNING(
321
+ R"({} KV node index mismatch: config rev={} states that address="{}:{}" should be at idx={}, )"
322
+ R"(but it is at idx={} ("{}"). Slot with idx={} is holds session with address="{}" ("{}"), swapping them.)",
323
+ log_prefix_,
324
+ config_->rev_str(),
325
+ hostname,
326
+ port,
327
+ kv_node_index,
328
+ found_kv_node_index,
329
+ ptr->second.id(),
330
+ kv_node_index,
331
+ current->second.bootstrap_address(),
332
+ current->second.id());
333
+ std::swap(current->second, ptr->second);
334
+ }
335
+ }
336
+ ++kv_node_index;
337
+ continue;
338
+ }
339
+ couchbase::core::origin origin(origin_.credentials(), hostname, port, origin_.options());
340
+ io::mcbp_session session = origin_.options().enable_tls
341
+ ? io::mcbp_session(client_id_, ctx_, tls_, origin, state_listener_, name_, known_features_)
342
+ : io::mcbp_session(client_id_, ctx_, origin, state_listener_, name_, known_features_);
343
+ CB_LOG_DEBUG(R"({} rev={}, restart idx={}, session="{}", address="{}:{}")",
349
344
  log_prefix_,
350
- index,
351
- old_id,
345
+ config_->rev_str(),
346
+ node.index,
352
347
  session.id(),
353
348
  hostname,
354
349
  port);
350
+ session.bootstrap(
351
+ [self = shared_from_this(), session](std::error_code err, topology::configuration cfg) mutable {
352
+ if (err) {
353
+ return self->remove_session(session.id());
354
+ }
355
+ self->update_config(std::move(cfg));
356
+ session.on_configuration_update(self);
357
+ session.on_stop([id = session.id(), self]() { self->remove_session(id); });
358
+ self->drain_deferred_queue();
359
+ },
360
+ true);
361
+ sessions_.insert_or_assign(index, std::move(session));
362
+ ++kv_node_index;
355
363
  }
364
+ }
356
365
 
357
- session.bootstrap(
358
- [self = shared_from_this(), session, this_index = index, hostname, port](std::error_code ec,
359
- const topology::configuration& config) mutable {
360
- if (self->closed_) {
361
- asio::post(asio::bind_executor(
362
- self->ctx_, [session = std::move(session)]() mutable { return session.stop(retry_reason::do_not_retry); }));
363
- return;
364
- }
365
- if (ec) {
366
- CB_LOG_WARNING(R"({} failed to restart session idx={}, ec={})", session.log_prefix(), this_index, ec.message());
367
- self->restart_node(this_index, hostname, port);
368
- return;
369
- }
370
- session.on_configuration_update(self);
371
- session.on_stop([this_index, hostname, port, self](retry_reason reason) {
372
- if (reason == retry_reason::socket_closed_while_in_flight) {
373
- self->restart_node(this_index, hostname, port);
374
- }
375
- });
366
+ void remove_session(const std::string& id)
367
+ {
368
+ bool found{ false };
369
+ const std::scoped_lock lock(sessions_mutex_);
370
+ for (auto ptr = sessions_.cbegin(); ptr != sessions_.cend();) {
371
+ if (ptr->second.id() == id) {
372
+ CB_LOG_DEBUG(R"({} removed session id="{}", address="{}", bootstrap_address="{}:{}")",
373
+ log_prefix_,
374
+ ptr->second.id(),
375
+ ptr->second.remote_address(),
376
+ ptr->second.bootstrap_hostname(),
377
+ ptr->second.bootstrap_port());
378
+ ptr = sessions_.erase(ptr);
379
+ found = true;
380
+ } else {
381
+ ptr = std::next(ptr);
382
+ }
383
+ }
376
384
 
377
- self->update_config(config);
378
- self->drain_deferred_queue();
379
- },
380
- true);
381
- sessions_.insert_or_assign(index, std::move(session));
385
+ if (found) {
386
+ asio::post(asio::bind_executor(ctx_, [self = shared_from_this()]() { return self->restart_sessions(); }));
387
+ }
382
388
  }
383
389
 
384
390
  void bootstrap(utils::movable_function<void(std::error_code, topology::configuration)>&& handler)
@@ -393,15 +399,11 @@ class bucket_impl
393
399
  topology::configuration cfg) mutable {
394
400
  if (ec) {
395
401
  CB_LOG_WARNING(R"({} failed to bootstrap session ec={}, bucket="{}")", new_session.log_prefix(), ec.message(), self->name_);
402
+ self->remove_session(new_session.id());
396
403
  } else {
397
404
  const std::size_t this_index = new_session.index();
398
405
  new_session.on_configuration_update(self);
399
- new_session.on_stop([this_index, hostname = new_session.bootstrap_hostname(), port = new_session.bootstrap_port(), self](
400
- retry_reason reason) {
401
- if (reason == retry_reason::socket_closed_while_in_flight) {
402
- self->restart_node(this_index, hostname, port);
403
- }
404
- });
406
+ new_session.on_stop([id = new_session.id(), self]() { self->remove_session(id); });
405
407
 
406
408
  {
407
409
  std::scoped_lock lock(self->sessions_mutex_);
@@ -455,6 +457,9 @@ class bucket_impl
455
457
  std::scoped_lock lock(deferred_commands_mutex_);
456
458
  std::swap(deferred_commands_, commands);
457
459
  }
460
+ if (!commands.empty()) {
461
+ CB_LOG_TRACE(R"({} draining deferred operation queue, size={})", log_prefix_, commands.size());
462
+ }
458
463
  while (!commands.empty()) {
459
464
  commands.front()();
460
465
  commands.pop();
@@ -489,9 +494,33 @@ class bucket_impl
489
494
  }
490
495
  }
491
496
 
497
+ /**
498
+ * copies nodes from rhs that are not in lhs to output vector
499
+ */
500
+ void diff_nodes(const std::vector<topology::configuration::node>& lhs,
501
+ const std::vector<topology::configuration::node>& rhs,
502
+ std::vector<topology::configuration::node>& output)
503
+ {
504
+ for (const auto& re : rhs) {
505
+ bool known = false;
506
+ const auto& rhost = re.hostname_for(origin_.options().network);
507
+ const auto rport = re.port_or(origin_.options().network, service_type::key_value, origin_.options().enable_tls, 0);
508
+ for (const auto& le : lhs) {
509
+ const auto& lhost = le.hostname_for(origin_.options().network);
510
+ const auto lport = le.port_or(origin_.options().network, service_type::key_value, origin_.options().enable_tls, 0);
511
+ if (rhost == lhost && rport == lport) {
512
+ known = true;
513
+ break;
514
+ }
515
+ }
516
+ if (!known) {
517
+ output.push_back(re);
518
+ }
519
+ }
520
+ }
521
+
492
522
  void update_config(topology::configuration config) override
493
523
  {
494
- bool forced_config = false;
495
524
  std::vector<topology::configuration::node> added{};
496
525
  std::vector<topology::configuration::node> removed{};
497
526
  {
@@ -500,7 +529,6 @@ class bucket_impl
500
529
  CB_LOG_DEBUG("{} initialize configuration rev={}", log_prefix_, config.rev_str());
501
530
  } else if (config.force) {
502
531
  CB_LOG_DEBUG("{} forced to accept configuration rev={}", log_prefix_, config.rev_str());
503
- forced_config = true;
504
532
  } else if (!config.vbmap) {
505
533
  CB_LOG_DEBUG("{} will not update the configuration old={} -> new={}, because new config does not have partition map",
506
534
  log_prefix_,
@@ -533,78 +561,81 @@ class bucket_impl
533
561
  std::scoped_lock lock(sessions_mutex_);
534
562
  std::map<size_t, io::mcbp_session> new_sessions{};
535
563
 
536
- for (auto& [index, session] : sessions_) {
537
- std::size_t new_index = config.nodes.size() + 1;
538
- for (const auto& node : config.nodes) {
539
- if (session.bootstrap_hostname() == node.hostname_for(origin_.options().network) &&
540
- session.bootstrap_port() ==
541
- std::to_string(
542
- node.port_or(origin_.options().network, service_type::key_value, origin_.options().enable_tls, 0))) {
543
- new_index = node.index;
544
- break;
545
- }
546
- }
547
- if (new_index < config.nodes.size()) {
548
- CB_LOG_DEBUG(R"({} rev={}, preserve session="{}", address="{}:{}", index={}->{})",
549
- log_prefix_,
550
- config.rev_str(),
551
- session.id(),
552
- session.bootstrap_hostname(),
553
- session.bootstrap_port(),
554
- index,
555
- new_index);
556
- new_sessions.insert_or_assign(new_index, std::move(session));
557
- } else {
558
- CB_LOG_DEBUG(R"({} rev={}, drop session="{}", address="{}:{}", index={})",
559
- log_prefix_,
560
- config.rev_str(),
561
- session.id(),
562
- session.bootstrap_hostname(),
563
- session.bootstrap_port(),
564
- index);
565
- asio::post(asio::bind_executor(
566
- ctx_, [session = std::move(session)]() mutable { return session.stop(retry_reason::do_not_retry); }));
567
- }
568
- }
569
-
564
+ std::size_t next_index{ 0 };
570
565
  for (const auto& node : config.nodes) {
571
- if (new_sessions.find(node.index) != new_sessions.end()) {
572
- continue;
573
- }
574
-
575
566
  const auto& hostname = node.hostname_for(origin_.options().network);
576
567
  auto port = node.port_or(origin_.options().network, service_type::key_value, origin_.options().enable_tls, 0);
577
568
  if (port == 0) {
578
569
  continue;
579
570
  }
571
+
572
+ bool reused_session{ false };
573
+ for (auto it = sessions_.begin(); it != sessions_.end(); ++it) {
574
+ if (it->second.bootstrap_hostname() == hostname && it->second.bootstrap_port_number() == port) {
575
+ CB_LOG_DEBUG(R"({} rev={}, preserve session="{}", address="{}:{}", index={}->{})",
576
+ log_prefix_,
577
+ config.rev_str(),
578
+ it->second.id(),
579
+ it->second.bootstrap_hostname(),
580
+ it->second.bootstrap_port(),
581
+ it->first,
582
+ next_index);
583
+ new_sessions.insert_or_assign(next_index, std::move(it->second));
584
+ reused_session = true;
585
+ ++next_index;
586
+ sessions_.erase(it);
587
+ break;
588
+ }
589
+ }
590
+ if (reused_session) {
591
+ continue;
592
+ }
593
+
580
594
  couchbase::core::origin origin(origin_.credentials(), hostname, port, origin_.options());
581
595
  io::mcbp_session session = origin_.options().enable_tls
582
596
  ? io::mcbp_session(client_id_, ctx_, tls_, origin, state_listener_, name_, known_features_)
583
597
  : io::mcbp_session(client_id_, ctx_, origin, state_listener_, name_, known_features_);
584
- CB_LOG_DEBUG(
585
- R"({} rev={}, add session="{}", address="{}:{}")", log_prefix_, config.rev_str(), session.id(), hostname, port);
598
+ CB_LOG_DEBUG(R"({} rev={}, add session="{}", address="{}:{}", index={})",
599
+ log_prefix_,
600
+ config.rev_str(),
601
+ session.id(),
602
+ hostname,
603
+ port,
604
+ node.index);
586
605
  session.bootstrap(
587
- [self = shared_from_this(), session, forced_config, idx = node.index](std::error_code err,
588
- topology::configuration cfg) mutable {
589
- if (!err) {
590
- self->update_config(std::move(cfg));
591
- session.on_configuration_update(self);
592
- session.on_stop(
593
- [index = session.index(), hostname = session.bootstrap_hostname(), port = session.bootstrap_port(), self](
594
- retry_reason reason) {
595
- if (reason == retry_reason::socket_closed_while_in_flight) {
596
- self->restart_node(index, hostname, port);
597
- }
598
- });
599
- self->drain_deferred_queue();
600
- } else if (err == errc::common::unambiguous_timeout && forced_config) {
601
- self->restart_node(idx, session.bootstrap_hostname(), session.bootstrap_port());
606
+ [self = shared_from_this(), session, idx = next_index](std::error_code err, topology::configuration cfg) mutable {
607
+ if (err) {
608
+ CB_LOG_WARNING(R"({} failed to bootstrap session="{}", address="{}:{}", index={}, ec={})",
609
+ session.log_prefix(),
610
+ session.id(),
611
+ session.bootstrap_hostname(),
612
+ session.bootstrap_port(),
613
+ idx,
614
+ err.message());
615
+ return self->remove_session(session.id());
602
616
  }
617
+ self->update_config(std::move(cfg));
618
+ session.on_configuration_update(self);
619
+ session.on_stop([id = session.id(), self]() { self->remove_session(id); });
620
+ self->drain_deferred_queue();
603
621
  },
604
622
  true);
605
- new_sessions.insert_or_assign(node.index, std::move(session));
623
+ new_sessions.insert_or_assign(next_index, std::move(session));
624
+ ++next_index;
625
+ }
626
+ std::swap(sessions_, new_sessions);
627
+
628
+ for (auto it = new_sessions.begin(); it != new_sessions.end(); ++it) {
629
+ CB_LOG_DEBUG(R"({} rev={}, drop session="{}", address="{}:{}", index={})",
630
+ log_prefix_,
631
+ config.rev_str(),
632
+ it->second.id(),
633
+ it->second.bootstrap_hostname(),
634
+ it->second.bootstrap_port(),
635
+ it->first);
636
+ asio::post(asio::bind_executor(
637
+ ctx_, [session = std::move(it->second)]() mutable { return session.stop(retry_reason::do_not_retry); }));
606
638
  }
607
- sessions_ = new_sessions;
608
639
  }
609
640
  }
610
641
 
@@ -83,7 +83,7 @@ class bucket
83
83
  auto cmd = std::make_shared<operations::mcbp_command<bucket, Request>>(ctx_, shared_from_this(), request, default_timeout());
84
84
  cmd->start([cmd, handler = std::forward<Handler>(handler)](std::error_code ec, std::optional<io::mcbp_message>&& msg) mutable {
85
85
  using encoded_response_type = typename Request::encoded_response_type;
86
- std::uint16_t status_code = msg ? msg->header.status() : 0U;
86
+ std::uint16_t status_code = msg ? msg->header.status() : 0xffffU;
87
87
  auto resp = msg ? encoded_response_type(std::move(*msg)) : encoded_response_type{};
88
88
  auto ctx = make_key_value_error_context(ec, status_code, cmd, resp);
89
89
  handler(cmd->request.make_response(std::move(ctx), std::move(resp)));
@@ -107,7 +107,7 @@ class bucket
107
107
  auto [partition, server] = map_id(cmd->request.id);
108
108
  if (!server.has_value()) {
109
109
  CB_LOG_TRACE(
110
- R"({} unable to map key=\"{}\" to the node, id={}, partition={})", log_prefix(), cmd->request.id, cmd->id_, partition);
110
+ R"({} unable to map key="{}" to the node, id={}, partition={})", log_prefix(), cmd->request.id, cmd->id_, partition);
111
111
  return io::retry_orchestrator::maybe_retry(
112
112
  cmd->manager_, cmd, retry_reason::node_not_available, errc::common::request_canceled);
113
113
  }
@@ -116,19 +116,32 @@ class bucket
116
116
  }
117
117
  auto session = find_session_by_index(index);
118
118
  if (!session || !session->has_config()) {
119
- CB_LOG_TRACE(R"({} defer operation id={}, session={}, has_config={})",
119
+ CB_LOG_TRACE(R"({} defer operation id={}, key="{}", partition={}, index={}, session={}, address="{}", has_config={})",
120
120
  log_prefix(),
121
121
  cmd->id_,
122
+ cmd->request.id,
123
+ cmd->request.partition,
124
+ index,
122
125
  session.has_value(),
126
+ session.has_value() ? session->bootstrap_address() : "",
123
127
  session.has_value() && session->has_config());
124
128
  return defer_command([self = shared_from_this(), cmd]() { self->map_and_send(cmd); });
125
129
  }
126
130
  if (session->is_stopped()) {
127
131
  CB_LOG_TRACE(
128
- R"({} the session has been found, but it is stopped, retrying id={}, session={})", log_prefix(), cmd->id_, session->id());
132
+ R"({} the session has been found for idx={}, but it is stopped, retrying id={}, key="{}", partition={}, session={}, address="{}")",
133
+ log_prefix(),
134
+ index,
135
+ cmd->id_,
136
+ cmd->request.id,
137
+ cmd->request.partition,
138
+ session->id(),
139
+ session->bootstrap_address());
129
140
  return io::retry_orchestrator::maybe_retry(
130
141
  cmd->manager_, cmd, retry_reason::node_not_available, errc::common::request_canceled);
131
142
  }
143
+ cmd->last_dispatched_from_ = session->local_address();
144
+ cmd->last_dispatched_to_ = session->bootstrap_address();
132
145
  cmd->send_to(session.value());
133
146
  }
134
147