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
@@ -18,6 +18,7 @@
18
18
  #include "range_scan_options.hxx"
19
19
  #include "range_scan_orchestrator_options.hxx"
20
20
  #include "scan_result.hxx"
21
+ #include "topology/configuration.hxx"
21
22
 
22
23
  #include <tl/expected.hpp>
23
24
 
@@ -34,15 +35,25 @@ namespace couchbase::core
34
35
  class agent;
35
36
  class range_scan_orchestrator_impl;
36
37
 
38
+ class scan_stream_manager
39
+ {
40
+ public:
41
+ virtual ~scan_stream_manager() = default;
42
+ virtual void stream_start_failed(std::int16_t node_id, bool fatal) = 0;
43
+ virtual void stream_start_failed_awaiting_retry(std::int16_t node_id, std::uint16_t vbucket_id) = 0;
44
+ virtual void stream_continue_failed(std::int16_t node_id, bool fatal) = 0;
45
+ virtual void stream_completed(std::int16_t node_id) = 0;
46
+ };
47
+
37
48
  class range_scan_orchestrator
38
49
  {
39
50
  public:
40
51
  range_scan_orchestrator(asio::io_context& io,
41
52
  agent kv_provider,
42
- std::size_t num_vbuckets,
53
+ topology::configuration::vbucket_map vbucket_map,
43
54
  std::string scope_name,
44
55
  std::string collection_name,
45
- std::variant<std::monostate, range_scan, sampling_scan> scan_type,
56
+ std::variant<std::monostate, range_scan, prefix_scan, sampling_scan> scan_type,
46
57
  range_scan_orchestrator_options options);
47
58
 
48
59
  auto scan() -> tl::expected<scan_result, std::error_code>;
@@ -16,6 +16,7 @@
16
16
  #pragma once
17
17
 
18
18
  #include "scan_options.hxx"
19
+ #include "timeout_defaults.hxx"
19
20
 
20
21
  #include <couchbase/mutation_token.hxx>
21
22
  #include <couchbase/retry_strategy.hxx>
@@ -40,15 +41,16 @@ namespace couchbase::core
40
41
  {
41
42
 
42
43
  struct range_scan_orchestrator_options {
44
+ static constexpr std::uint16_t default_concurrency{ 1 };
45
+
43
46
  bool ids_only{ false };
44
47
  std::optional<mutation_state> consistent_with{};
45
- scan_sort sort{ scan_sort::none };
46
48
  std::uint32_t batch_item_limit{ range_scan_continue_options::default_batch_item_limit };
47
49
  std::uint32_t batch_byte_limit{ range_scan_continue_options::default_batch_byte_limit };
48
- std::chrono::milliseconds batch_time_limit{ range_scan_continue_options::default_batch_time_limit };
50
+ std::uint16_t concurrency{ default_concurrency };
49
51
 
50
52
  std::shared_ptr<couchbase::retry_strategy> retry_strategy{ nullptr };
51
- std::chrono::milliseconds timeout{};
53
+ std::chrono::milliseconds timeout{ timeout_defaults::key_value_scan_timeout };
52
54
  std::shared_ptr<couchbase::tracing::request_span> parent_span{};
53
55
  };
54
56
  } // namespace couchbase::core
@@ -38,26 +38,7 @@ class request_span;
38
38
 
39
39
  namespace couchbase::core
40
40
  {
41
-
42
- enum class scan_sort {
43
- none,
44
- ascending,
45
- };
46
-
47
41
  struct mutation_state {
48
42
  std::vector<couchbase::mutation_token> tokens;
49
43
  };
50
-
51
- struct scan_options {
52
- bool ids_only{ false };
53
- std::optional<mutation_state> consistent_with{};
54
- scan_sort sort{ scan_sort::none };
55
- std::uint32_t batch_item_limit{ range_scan_continue_options::default_batch_item_limit };
56
- std::uint32_t batch_byte_limit{ range_scan_continue_options::default_batch_byte_limit };
57
- std::chrono::milliseconds batch_time_limit{ range_scan_continue_options::default_batch_time_limit };
58
-
59
- std::shared_ptr<couchbase::retry_strategy> retry_strategy{ nullptr };
60
- std::chrono::milliseconds timeout{};
61
- std::shared_ptr<couchbase::tracing::request_span> parent_span{};
62
- };
63
44
  } // namespace couchbase::core
@@ -32,10 +32,7 @@ class scan_result_impl
32
32
 
33
33
  [[nodiscard]] auto next() const -> tl::expected<range_scan_item, std::error_code>
34
34
  {
35
- if (auto item = iterator_->next().get(); item) {
36
- return item.value();
37
- }
38
- return tl::unexpected{ errc::key_value::range_scan_completed };
35
+ return iterator_->next().get();
39
36
  }
40
37
 
41
38
  void next(utils::movable_function<void(range_scan_item, std::error_code)> callback) const
@@ -43,6 +40,16 @@ class scan_result_impl
43
40
  return iterator_->next(std::move(callback));
44
41
  }
45
42
 
43
+ void cancel()
44
+ {
45
+ return iterator_->cancel();
46
+ }
47
+
48
+ [[nodiscard]] auto is_cancelled() -> bool
49
+ {
50
+ return iterator_->is_cancelled();
51
+ }
52
+
46
53
  private:
47
54
  std::shared_ptr<range_scan_item_iterator> iterator_;
48
55
  };
@@ -65,7 +72,14 @@ scan_result::next(utils::movable_function<void(range_scan_item, std::error_code)
65
72
  }
66
73
 
67
74
  void
68
- scan_result::cancel() const
75
+ scan_result::cancel()
76
+ {
77
+ return impl_->cancel();
78
+ }
79
+
80
+ auto
81
+ scan_result::is_cancelled() -> bool
69
82
  {
83
+ return impl_->is_cancelled();
70
84
  }
71
85
  } // namespace couchbase::core
@@ -41,8 +41,10 @@ class range_scan_item_iterator
41
41
  {
42
42
  public:
43
43
  virtual ~range_scan_item_iterator() = default;
44
- virtual auto next() -> std::future<std::optional<range_scan_item>> = 0;
44
+ virtual auto next() -> std::future<tl::expected<range_scan_item, std::error_code>> = 0;
45
45
  virtual void next(utils::movable_function<void(range_scan_item, std::error_code)> callback) = 0;
46
+ virtual void cancel() = 0;
47
+ virtual bool is_cancelled() = 0;
46
48
  };
47
49
 
48
50
  class scan_result
@@ -51,7 +53,8 @@ class scan_result
51
53
  explicit scan_result(std::shared_ptr<range_scan_item_iterator> iterator);
52
54
  [[nodiscard]] auto next() const -> tl::expected<range_scan_item, std::error_code>;
53
55
  void next(utils::movable_function<void(range_scan_item, std::error_code)> callback) const;
54
- void cancel() const;
56
+ void cancel();
57
+ [[nodiscard]] auto is_cancelled() -> bool;
55
58
 
56
59
  private:
57
60
  std::shared_ptr<scan_result_impl> impl_{};
@@ -1,6 +1,6 @@
1
1
  /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
2
  /*
3
- * Copyright 2020-2021 Couchbase, Inc.
3
+ * Copyright 2020-2023 Couchbase, Inc.
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License");
6
6
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ constexpr std::chrono::milliseconds resolve_timeout{ 2'000 };
27
27
  constexpr std::chrono::milliseconds connect_timeout{ 2'000 };
28
28
  constexpr std::chrono::milliseconds key_value_timeout{ 2'500 };
29
29
  constexpr std::chrono::milliseconds key_value_durable_timeout{ 10'000 };
30
+ constexpr std::chrono::milliseconds key_value_scan_timeout{ 75'000 };
30
31
  constexpr std::chrono::milliseconds view_timeout{ 75'000 };
31
32
  constexpr std::chrono::milliseconds query_timeout{ 75'000 };
32
33
  constexpr std::chrono::milliseconds analytics_timeout{ 75'000 };
@@ -34,8 +35,6 @@ constexpr std::chrono::milliseconds search_timeout{ 75'000 };
34
35
  constexpr std::chrono::milliseconds management_timeout{ 75'000 };
35
36
  constexpr std::chrono::milliseconds eventing_timeout{ 75'000 };
36
37
 
37
- constexpr std::chrono::milliseconds range_scan_timeout{ 75'000 };
38
-
39
38
  constexpr std::chrono::milliseconds dns_srv_timeout{ 500 };
40
39
  constexpr std::chrono::milliseconds tcp_keep_alive_interval{ 60'000 };
41
40
  constexpr std::chrono::milliseconds config_poll_interval{ 2'500 };
@@ -31,6 +31,8 @@ enum class bucket_capability {
31
31
  collections,
32
32
  durable_write,
33
33
  tombstoned_user_xattrs,
34
+ range_scan,
35
+ replica_read,
34
36
  };
35
37
 
36
38
  enum class cluster_capability {
@@ -39,5 +41,6 @@ enum class cluster_capability {
39
41
  n1ql_javascript_functions,
40
42
  n1ql_inline_functions,
41
43
  n1ql_enhanced_prepared_statements,
44
+ n1ql_read_from_replica,
42
45
  };
43
46
  } // namespace couchbase::core
@@ -67,6 +67,12 @@ struct fmt::formatter<couchbase::core::bucket_capability> {
67
67
  case couchbase::core::bucket_capability::tombstoned_user_xattrs:
68
68
  name = "tombstoned_user_xattrs";
69
69
  break;
70
+ case couchbase::core::bucket_capability::range_scan:
71
+ name = "range_scan";
72
+ break;
73
+ case couchbase::core::bucket_capability::replica_read:
74
+ name = "replica_read";
75
+ break;
70
76
  }
71
77
  return format_to(ctx.out(), "{}", name);
72
78
  }
@@ -100,6 +106,8 @@ struct fmt::formatter<couchbase::core::cluster_capability> {
100
106
  case couchbase::core::cluster_capability::n1ql_enhanced_prepared_statements:
101
107
  name = "n1ql_enhanced_prepared_statements";
102
108
  break;
109
+ case couchbase::core::cluster_capability::n1ql_read_from_replica:
110
+ name = "n1ql_read_from_replica";
103
111
  }
104
112
  return format_to(ctx.out(), "{}", name);
105
113
  }
@@ -47,6 +47,6 @@ struct fmt::formatter<couchbase::core::topology::collections_manifest> {
47
47
  couchbase::core::uuid::to_string(manifest.id),
48
48
  manifest.uid,
49
49
  collections.size(),
50
- utils::join_strings(collections, ", "));
50
+ couchbase::core::utils::join_strings(collections, ", "));
51
51
  }
52
52
  };
@@ -112,6 +112,16 @@ struct configuration {
112
112
  return cluster_capabilities.find(cluster_capability::n1ql_enhanced_prepared_statements) != cluster_capabilities.end();
113
113
  }
114
114
 
115
+ [[nodiscard]] bool supports_read_from_replica() const
116
+ {
117
+ return cluster_capabilities.find(cluster_capability::n1ql_read_from_replica) != cluster_capabilities.end();
118
+ }
119
+
120
+ [[nodiscard]] bool supports_range_scan() const
121
+ {
122
+ return bucket_capabilities.find(bucket_capability::range_scan) != bucket_capabilities.end();
123
+ }
124
+
115
125
  [[nodiscard]] bool ephemeral() const
116
126
  {
117
127
  // Use bucket capabilities to identify if couchapi is missing (then its ephemeral). If its null then
@@ -119,6 +129,11 @@ struct configuration {
119
129
  return bucket_capabilities.count(couchbase::core::bucket_capability::couchapi) == 0;
120
130
  }
121
131
 
132
+ [[nodiscard]] bool supports_subdoc_read_replica() const
133
+ {
134
+ return bucket_capabilities.find(bucket_capability::replica_read) != bucket_capabilities.end();
135
+ }
136
+
122
137
  [[nodiscard]] std::size_t index_for_this_node() const;
123
138
  [[nodiscard]] bool has_node(const std::string& network,
124
139
  service_type type,
@@ -52,7 +52,6 @@ struct traits<couchbase::core::topology::configuration> {
52
52
  }
53
53
  if (const auto& hostname = o.find("hostname"); hostname != o.end()) {
54
54
  n.hostname = hostname->second.get_string();
55
- n.hostname = n.hostname.substr(0, n.hostname.rfind(':'));
56
55
  }
57
56
  const auto& s = o.at("services");
58
57
  n.services_plain.key_value = s.template optional<std::uint16_t>("kv");
@@ -233,6 +232,10 @@ struct traits<couchbase::core::topology::configuration> {
233
232
  result.bucket_capabilities.insert(couchbase::core::bucket_capability::nodes_ext);
234
233
  } else if (name == "xattr") {
235
234
  result.bucket_capabilities.insert(couchbase::core::bucket_capability::xattr);
235
+ } else if (name == "rangeScan") {
236
+ result.bucket_capabilities.insert(couchbase::core::bucket_capability::range_scan);
237
+ } else if (name == "subdoc.ReplicaRead") {
238
+ result.bucket_capabilities.insert(couchbase::core::bucket_capability::replica_read);
236
239
  }
237
240
  }
238
241
  }
@@ -249,6 +252,8 @@ struct traits<couchbase::core::topology::configuration> {
249
252
  result.cluster_capabilities.insert(couchbase::core::cluster_capability::n1ql_inline_functions);
250
253
  } else if (name == "enhancedPreparedStatements") {
251
254
  result.cluster_capabilities.insert(couchbase::core::cluster_capability::n1ql_enhanced_prepared_statements);
255
+ } else if (name == "readFromReplica") {
256
+ result.cluster_capabilities.insert(couchbase::core::cluster_capability::n1ql_read_from_replica);
252
257
  }
253
258
  }
254
259
  }
@@ -182,33 +182,39 @@ struct action<bucket_name> {
182
182
  } // namespace priv
183
183
 
184
184
  void
185
- parse_option(std::string& receiver, const std::string& /* name */, const std::string& value)
185
+ parse_option(std::string& receiver, const std::string& /* name */, const std::string& value, std::vector<std::string>& /* warnings */)
186
186
  {
187
187
  receiver = string_codec::url_decode(value);
188
188
  }
189
189
 
190
190
  void
191
- parse_option(bool& receiver, const std::string& /* name */, const std::string& value)
191
+ parse_option(bool& receiver, const std::string& name, const std::string& value, std::vector<std::string>& warnings)
192
192
  {
193
193
  if (value == "true" || value == "yes" || value == "on") {
194
194
  receiver = true;
195
195
  } else if (value == "false" || value == "no" || value == "off") {
196
196
  receiver = false;
197
+ } else {
198
+ warnings.push_back(fmt::format(
199
+ R"(unable to parse "{}" parameter in connection string (value "{}" cannot be interpreted as a boolean))", name, value));
197
200
  }
198
201
  }
199
202
 
200
203
  void
201
- parse_option(tls_verify_mode& receiver, const std::string& /* name */, const std::string& value)
204
+ parse_option(tls_verify_mode& receiver, const std::string& name, const std::string& value, std::vector<std::string>& warnings)
202
205
  {
203
206
  if (value == "none") {
204
207
  receiver = tls_verify_mode::none;
205
208
  } else if (value == "peer") {
206
209
  receiver = tls_verify_mode::peer;
210
+ } else {
211
+ warnings.push_back(fmt::format(
212
+ R"(unable to parse "{}" parameter in connection string (value "{}" is not a valid TLS verification mode))", name, value));
207
213
  }
208
214
  }
209
215
 
210
216
  void
211
- parse_option(io::ip_protocol& receiver, const std::string& /* name */, const std::string& value)
217
+ parse_option(io::ip_protocol& receiver, const std::string& name, const std::string& value, std::vector<std::string>& warnings)
212
218
  {
213
219
  if (value == "any") {
214
220
  receiver = io::ip_protocol::any;
@@ -216,23 +222,28 @@ parse_option(io::ip_protocol& receiver, const std::string& /* name */, const std
216
222
  receiver = io::ip_protocol::force_ipv4;
217
223
  } else if (value == "force_ipv6") {
218
224
  receiver = io::ip_protocol::force_ipv6;
225
+ } else {
226
+ warnings.push_back(fmt::format(
227
+ R"(unable to parse "{}" parameter in connection string (value "{}" is not a valid IP protocol preference))", name, value));
219
228
  }
220
229
  }
221
230
 
222
231
  void
223
- parse_option(std::size_t& receiver, const std::string& name, const std::string& value)
232
+ parse_option(std::size_t& receiver, const std::string& name, const std::string& value, std::vector<std::string>& warnings)
224
233
  {
225
234
  try {
226
235
  receiver = std::stoull(value, nullptr, 10);
227
236
  } catch (const std::invalid_argument& ex1) {
228
- CB_LOG_WARNING(R"(unable to parse "{}" parameter in connection string (value "{}" is not a number): {})", name, value, ex1.what());
237
+ warnings.push_back(
238
+ fmt::format(R"(unable to parse "{}" parameter in connection string (value "{}" is not a number): {})", name, value, ex1.what()));
229
239
  } catch (const std::out_of_range& ex2) {
230
- CB_LOG_WARNING(R"(unable to parse "{}" parameter in connection string (value "{}" is out of range): {})", name, value, ex2.what());
240
+ warnings.push_back(
241
+ fmt::format(R"(unable to parse "{}" parameter in connection string (value "{}" is out of range): {})", name, value, ex2.what()));
231
242
  }
232
243
  }
233
244
 
234
245
  void
235
- parse_option(std::chrono::milliseconds& receiver, const std::string& name, const std::string& value)
246
+ parse_option(std::chrono::milliseconds& receiver, const std::string& name, const std::string& value, std::vector<std::string>& warnings)
236
247
  {
237
248
  try {
238
249
  receiver = std::chrono::duration_cast<std::chrono::milliseconds>(parse_duration(value));
@@ -240,11 +251,11 @@ parse_option(std::chrono::milliseconds& receiver, const std::string& name, const
240
251
  try {
241
252
  receiver = std::chrono::milliseconds(std::stoull(value, nullptr, 10));
242
253
  } catch (const std::invalid_argument& ex1) {
243
- CB_LOG_WARNING(
244
- R"(unable to parse "{}" parameter in connection string (value "{}" is not a number): {})", name, value, ex1.what());
254
+ warnings.push_back(fmt::format(
255
+ R"(unable to parse "{}" parameter in connection string (value "{}" is not a number): {})", name, value, ex1.what()));
245
256
  } catch (const std::out_of_range& ex2) {
246
- CB_LOG_WARNING(
247
- R"(unable to parse "{}" parameter in connection string (value "{}" is out of range): {})", name, value, ex2.what());
257
+ warnings.push_back(fmt::format(
258
+ R"(unable to parse "{}" parameter in connection string (value "{}" is out of range): {})", name, value, ex2.what()));
248
259
  }
249
260
  }
250
261
  }
@@ -262,68 +273,68 @@ extract_options(connection_string& connstr)
262
273
  * Number of seconds the client should wait while attempting to connect to a node’s KV service via a socket. Initial
263
274
  * connection, reconnecting, node added, etc.
264
275
  */
265
- parse_option(connstr.options.connect_timeout, name, value);
276
+ parse_option(connstr.options.connect_timeout, name, value, connstr.warnings);
266
277
  } else if (name == "kv_timeout" || name == "key_value_timeout") {
267
278
  /**
268
279
  * Number of milliseconds to wait before timing out a KV operation by the client.
269
280
  */
270
- parse_option(connstr.options.key_value_timeout, name, value);
281
+ parse_option(connstr.options.key_value_timeout, name, value, connstr.warnings);
271
282
  } else if (name == "kv_durable_timeout" || name == "key_value_durable_timeout") {
272
283
  /**
273
284
  * Number of milliseconds to wait before timing out a KV operation that is either using synchronous durability or
274
285
  * observe-based durability.
275
286
  */
276
- parse_option(connstr.options.key_value_durable_timeout, name, value);
287
+ parse_option(connstr.options.key_value_durable_timeout, name, value, connstr.warnings);
277
288
  } else if (name == "view_timeout") {
278
289
  /**
279
290
  * Number of seconds to wait before timing out a View request by the client..
280
291
  */
281
- parse_option(connstr.options.view_timeout, name, value);
292
+ parse_option(connstr.options.view_timeout, name, value, connstr.warnings);
282
293
  } else if (name == "query_timeout") {
283
294
  /**
284
295
  * Number of seconds to wait before timing out a Query or N1QL request by the client.
285
296
  */
286
- parse_option(connstr.options.query_timeout, name, value);
297
+ parse_option(connstr.options.query_timeout, name, value, connstr.warnings);
287
298
  } else if (name == "analytics_timeout") {
288
299
  /**
289
300
  * Number of seconds to wait before timing out an Analytics request by the client.
290
301
  */
291
- parse_option(connstr.options.analytics_timeout, name, value);
302
+ parse_option(connstr.options.analytics_timeout, name, value, connstr.warnings);
292
303
  } else if (name == "search_timeout") {
293
304
  /**
294
305
  * Number of seconds to wait before timing out a Search request by the client.
295
306
  */
296
- parse_option(connstr.options.search_timeout, name, value);
307
+ parse_option(connstr.options.search_timeout, name, value, connstr.warnings);
297
308
  } else if (name == "management_timeout") {
298
309
  /**
299
310
  * Number of seconds to wait before timing out a Management API request by the client.
300
311
  */
301
- parse_option(connstr.options.management_timeout, name, value);
312
+ parse_option(connstr.options.management_timeout, name, value, connstr.warnings);
302
313
  } else if (name == "trust_certificate") {
303
- parse_option(connstr.options.trust_certificate, name, value);
314
+ parse_option(connstr.options.trust_certificate, name, value, connstr.warnings);
304
315
  } else if (name == "enable_mutation_tokens") {
305
316
  /**
306
317
  * Request mutation tokens at connection negotiation time. Turning this off will save 16 bytes per operation response.
307
318
  */
308
- parse_option(connstr.options.enable_mutation_tokens, name, value);
319
+ parse_option(connstr.options.enable_mutation_tokens, name, value, connstr.warnings);
309
320
  } else if (name == "enable_tcp_keep_alive") {
310
321
  /**
311
322
  * Gets or sets a value indicating whether enable TCP keep-alive.
312
323
  */
313
- parse_option(connstr.options.enable_tcp_keep_alive, name, value);
324
+ parse_option(connstr.options.enable_tcp_keep_alive, name, value, connstr.warnings);
314
325
  } else if (name == "tcp_keep_alive_interval") {
315
326
  /**
316
327
  * Specifies the timeout, in milliseconds, with no activity until the first keep-alive packet is sent. This applies to all
317
328
  * services, but is advisory: if the underlying platform does not support this on all connections, it will be applied only
318
329
  * on those it can be.
319
330
  */
320
- parse_option(connstr.options.tcp_keep_alive_interval, name, value);
331
+ parse_option(connstr.options.tcp_keep_alive_interval, name, value, connstr.warnings);
321
332
  } else if (name == "force_ipv4") {
322
333
  /**
323
334
  * Sets the SDK configuration to do IPv4 Name Resolution
324
335
  */
325
336
  bool force_ipv4 = false;
326
- parse_option(force_ipv4, name, value);
337
+ parse_option(force_ipv4, name, value, connstr.warnings);
327
338
  if (force_ipv4) {
328
339
  connstr.options.use_ip_protocol = io::ip_protocol::force_ipv4;
329
340
  }
@@ -331,40 +342,40 @@ extract_options(connection_string& connstr)
331
342
  /**
332
343
  * Controls preference of IP protocol for name resolution
333
344
  */
334
- parse_option(connstr.options.use_ip_protocol, name, value);
345
+ parse_option(connstr.options.use_ip_protocol, name, value, connstr.warnings);
335
346
  } else if (name == "config_poll_interval") {
336
- parse_option(connstr.options.config_poll_interval, name, value);
347
+ parse_option(connstr.options.config_poll_interval, name, value, connstr.warnings);
337
348
  } else if (name == "config_poll_floor") {
338
- parse_option(connstr.options.config_poll_floor, name, value);
349
+ parse_option(connstr.options.config_poll_floor, name, value, connstr.warnings);
339
350
  } else if (name == "max_http_connections") {
340
351
  /**
341
352
  * The maximum number of HTTP connections allowed on a per-host and per-port basis. 0 indicates an unlimited number of
342
353
  * connections are permitted.
343
354
  */
344
- parse_option(connstr.options.max_http_connections, name, value);
355
+ parse_option(connstr.options.max_http_connections, name, value, connstr.warnings);
345
356
  } else if (name == "idle_http_connection_timeout") {
346
357
  /**
347
358
  * The period of time an HTTP connection can be idle before it is forcefully disconnected.
348
359
  */
349
- parse_option(connstr.options.idle_http_connection_timeout, name, value);
360
+ parse_option(connstr.options.idle_http_connection_timeout, name, value, connstr.warnings);
350
361
  } else if (name == "bootstrap_timeout") {
351
362
  /**
352
363
  * The period of time allocated to complete bootstrap
353
364
  */
354
- parse_option(connstr.options.bootstrap_timeout, name, value);
365
+ parse_option(connstr.options.bootstrap_timeout, name, value, connstr.warnings);
355
366
  } else if (name == "resolve_timeout") {
356
367
  /**
357
368
  * The period of time to resolve DNS name of the node to IP address
358
369
  */
359
- parse_option(connstr.options.resolve_timeout, name, value);
370
+ parse_option(connstr.options.resolve_timeout, name, value, connstr.warnings);
360
371
  } else if (name == "enable_dns_srv") {
361
372
  if (connstr.bootstrap_nodes.size() == 1) {
362
- parse_option(connstr.options.enable_dns_srv, name, value);
373
+ parse_option(connstr.options.enable_dns_srv, name, value, connstr.warnings);
363
374
  } else {
364
- CB_LOG_WARNING(
365
- R"(parameter "{}" require single entry in bootstrap nodes list of the connection string, ignoring (value "{}"))",
375
+ connstr.warnings.push_back(fmt::format(
376
+ R"(parameter "{}" requires single entry in bootstrap nodes list of the connection string, ignoring (value "{}"))",
366
377
  name,
367
- value);
378
+ value));
368
379
  }
369
380
  } else if (name == "network") {
370
381
  connstr.options.network = value; /* current known values are "auto", "default" and "external" */
@@ -372,51 +383,55 @@ extract_options(connection_string& connstr)
372
383
  /**
373
384
  * Whether to display N1QL, Analytics, Search queries on info level (default false)
374
385
  */
375
- parse_option(connstr.options.show_queries, name, value);
386
+ parse_option(connstr.options.show_queries, name, value, connstr.warnings);
376
387
  } else if (name == "enable_clustermap_notification") {
377
388
  /**
378
389
  * Allow the server to push configuration updates asynchronously.
379
390
  */
380
- parse_option(connstr.options.enable_clustermap_notification, name, value);
391
+ parse_option(connstr.options.enable_clustermap_notification, name, value, connstr.warnings);
381
392
  } else if (name == "enable_unordered_execution") {
382
393
  /**
383
394
  * Allow the server to reorder commands
384
395
  */
385
- parse_option(connstr.options.enable_unordered_execution, name, value);
396
+ parse_option(connstr.options.enable_unordered_execution, name, value, connstr.warnings);
386
397
  } else if (name == "enable_compression") {
387
398
  /**
388
399
  * Announce support of compression (snappy) to server
389
400
  */
390
- parse_option(connstr.options.enable_compression, name, value);
401
+ parse_option(connstr.options.enable_compression, name, value, connstr.warnings);
391
402
  } else if (name == "enable_tracing") {
392
403
  /**
393
404
  * true - use threshold_logging_tracer
394
405
  * false - use noop_tracer
395
406
  */
396
- parse_option(connstr.options.enable_tracing, name, value);
407
+ parse_option(connstr.options.enable_tracing, name, value, connstr.warnings);
397
408
  } else if (name == "enable_metrics") {
398
409
  /**
399
410
  * true - use logging_meter
400
411
  * false - use noop_meter
401
412
  */
402
- parse_option(connstr.options.enable_metrics, name, value);
413
+ parse_option(connstr.options.enable_metrics, name, value, connstr.warnings);
403
414
  } else if (name == "tls_verify") {
404
- parse_option(connstr.options.tls_verify, name, value);
415
+ parse_option(connstr.options.tls_verify, name, value, connstr.warnings);
405
416
  } else if (name == "disable_mozilla_ca_certificates") {
406
- parse_option(connstr.options.disable_mozilla_ca_certificates, name, value);
417
+ parse_option(connstr.options.disable_mozilla_ca_certificates, name, value, connstr.warnings);
418
+ } else if (name == "tls_disable_deprecated_protocols") {
419
+ parse_option(connstr.options.tls_disable_deprecated_protocols, name, value, connstr.warnings);
420
+ } else if (name == "tls_disable_v1_2") {
421
+ parse_option(connstr.options.tls_disable_v1_2, name, value, connstr.warnings);
407
422
  } else if (name == "user_agent_extra") {
408
423
  /**
409
424
  * string, that will be appended to identification fields of the server protocols (key in HELO packet for MCBP, "user-agent"
410
425
  * header for HTTP)
411
426
  */
412
- parse_option(connstr.options.user_agent_extra, name, value);
427
+ parse_option(connstr.options.user_agent_extra, name, value, connstr.warnings);
413
428
  } else if (name == "dump_configuration") {
414
429
  /**
415
430
  * Whether to dump every new configuration on TRACE level
416
431
  */
417
- parse_option(connstr.options.dump_configuration, name, value);
432
+ parse_option(connstr.options.dump_configuration, name, value, connstr.warnings);
418
433
  } else {
419
- CB_LOG_WARNING(R"(unknown parameter "{}" in connection string (value "{}"))", name, value);
434
+ connstr.warnings.push_back(fmt::format(R"(unknown parameter "{}" in connection string (value "{}"))", name, value));
420
435
  }
421
436
  }
422
437
  }
@@ -68,6 +68,7 @@ struct connection_string {
68
68
  bootstrap_mode default_mode{ connection_string::bootstrap_mode::gcccp };
69
69
  std::uint16_t default_port{ 11210 };
70
70
 
71
+ std::vector<std::string> warnings{};
71
72
  std::optional<std::string> error{};
72
73
  };
73
74
 
@@ -57,7 +57,7 @@ class analytics_error_context : public error_context
57
57
  std::string http_body,
58
58
  std::string hostname,
59
59
  std::uint16_t port)
60
- : error_context{ ec, std::move(last_dispatched_to), std::move(last_dispatched_from), retry_attempts, std::move(retry_reasons) }
60
+ : error_context{ {}, ec, std::move(last_dispatched_to), std::move(last_dispatched_from), retry_attempts, std::move(retry_reasons) }
61
61
  , first_error_code_{ first_error_code }
62
62
  , first_error_message_{ std::move(first_error_message) }
63
63
  , client_context_id_{ std::move(client_context_id) }
@@ -60,6 +60,21 @@ class behavior_options
60
60
  return *this;
61
61
  }
62
62
 
63
+ /**
64
+ * Selects network to use.
65
+ *
66
+ * @param name network name as it is exposed in the configuration.
67
+ * @return this object for chaining purposes.
68
+ *
69
+ * @see https://docs.couchbase.com/server/current/learn/clusters-and-availability/connectivity.html#alternate-addresses
70
+ * @see https://docs.couchbase.com/server/current/rest-api/rest-set-up-alternate-address.html
71
+ */
72
+ auto network(std::string name) -> behavior_options&
73
+ {
74
+ network_ = std::move(name);
75
+ return *this;
76
+ }
77
+
63
78
  struct built {
64
79
  std::string user_agent_extra;
65
80
  bool show_queries;
@@ -67,13 +82,14 @@ class behavior_options
67
82
  bool enable_mutation_tokens;
68
83
  bool enable_unordered_execution;
69
84
  bool dump_configuration;
85
+ std::string network;
70
86
  };
71
87
 
72
88
  [[nodiscard]] auto build() const -> built
73
89
  {
74
90
  return {
75
- user_agent_extra_, show_queries_, enable_clustermap_notification_, enable_mutation_tokens_,
76
- enable_unordered_execution_, dump_configuration_,
91
+ user_agent_extra_, show_queries_, enable_clustermap_notification_, enable_mutation_tokens_, enable_unordered_execution_,
92
+ dump_configuration_, network_,
77
93
  };
78
94
  }
79
95
 
@@ -84,5 +100,6 @@ class behavior_options
84
100
  bool enable_mutation_tokens_{ true };
85
101
  bool enable_unordered_execution_{ true };
86
102
  bool dump_configuration_{ false };
103
+ std::string network_{ "auto" };
87
104
  };
88
105
  } // namespace couchbase