couchbase 3.4.3 → 3.4.4

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