couchbase 3.4.0 → 3.4.2

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +10 -3
  4. data/ext/couchbase/cmake/CompilerWarnings.cmake +12 -4
  5. data/ext/couchbase/cmake/Documentation.cmake +4 -3
  6. data/ext/couchbase/cmake/OpenSSL.cmake +52 -7
  7. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +4 -0
  8. data/ext/couchbase/cmake/VersionInfo.cmake +39 -3
  9. data/ext/couchbase/cmake/test_openssl.cxx +7 -0
  10. data/ext/couchbase/core/cluster_options.hxx +0 -1
  11. data/ext/couchbase/core/config_profile.cxx +23 -1
  12. data/ext/couchbase/core/config_profile.hxx +2 -12
  13. data/ext/couchbase/core/crypto/CMakeLists.txt +5 -1
  14. data/ext/couchbase/core/impl/analytics.cxx +236 -0
  15. data/ext/couchbase/core/impl/cluster.cxx +0 -1
  16. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +3 -3
  17. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +5 -3
  18. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +3 -3
  19. data/ext/couchbase/core/impl/query.cxx +5 -5
  20. data/ext/couchbase/core/impl/transaction_get_result.cxx +54 -0
  21. data/ext/couchbase/core/io/dns_client.cxx +225 -0
  22. data/ext/couchbase/core/io/dns_client.hxx +19 -188
  23. data/ext/couchbase/core/meta/CMakeLists.txt +7 -5
  24. data/ext/couchbase/core/meta/version.cxx +19 -0
  25. data/ext/couchbase/core/operations/document_search.cxx +5 -2
  26. data/ext/couchbase/core/operations/document_search.hxx +0 -1
  27. data/ext/couchbase/core/transactions/active_transaction_record.hxx +2 -2
  28. data/ext/couchbase/core/transactions/atr_cleanup_entry.cxx +1 -0
  29. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +65 -31
  30. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +44 -23
  31. data/ext/couchbase/core/transactions/forward_compat.hxx +2 -2
  32. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +13 -13
  33. data/ext/couchbase/core/transactions/internal/transaction_fields.hxx +1 -0
  34. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +7 -1
  35. data/ext/couchbase/core/transactions/staged_mutation.cxx +1 -1
  36. data/ext/couchbase/core/transactions/staged_mutation.hxx +12 -2
  37. data/ext/couchbase/core/transactions/transaction_context.cxx +9 -11
  38. data/ext/couchbase/core/transactions/transaction_get_result.cxx +41 -31
  39. data/ext/couchbase/core/transactions/transaction_get_result.hxx +7 -3
  40. data/ext/couchbase/core/transactions/transaction_links.hxx +13 -1
  41. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +144 -155
  42. data/ext/couchbase/core/transactions/waitable_op_list.hxx +1 -0
  43. data/ext/couchbase/core/utils/connection_string.cxx +10 -3
  44. data/ext/couchbase/core/utils/connection_string.hxx +3 -3
  45. data/ext/couchbase/couchbase/analytics_error_context.hxx +143 -0
  46. data/ext/couchbase/couchbase/analytics_meta_data.hxx +155 -0
  47. data/ext/couchbase/couchbase/analytics_metrics.hxx +163 -0
  48. data/ext/couchbase/couchbase/analytics_options.hxx +359 -0
  49. data/ext/couchbase/couchbase/analytics_result.hxx +102 -0
  50. data/ext/couchbase/couchbase/analytics_scan_consistency.hxx +46 -0
  51. data/ext/couchbase/couchbase/analytics_status.hxx +41 -0
  52. data/ext/couchbase/couchbase/analytics_warning.hxx +85 -0
  53. data/ext/couchbase/couchbase/cluster.hxx +35 -2
  54. data/ext/couchbase/couchbase/cluster_options.hxx +10 -10
  55. data/ext/couchbase/couchbase/collection.hxx +22 -17
  56. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +1 -1
  57. data/ext/couchbase/couchbase/common_options.hxx +1 -1
  58. data/ext/couchbase/couchbase/configuration_profile.hxx +1 -1
  59. data/ext/couchbase/couchbase/configuration_profiles_registry.hxx +0 -1
  60. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +1 -1
  61. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +1 -1
  62. data/ext/couchbase/couchbase/drop_query_index_options.hxx +1 -1
  63. data/ext/couchbase/couchbase/fmt/analytics_status.hxx +76 -0
  64. data/ext/couchbase/couchbase/fmt/cas.hxx +12 -0
  65. data/ext/couchbase/couchbase/fmt/durability_level.hxx +6 -0
  66. data/ext/couchbase/couchbase/fmt/key_value_extended_error_info.hxx +6 -0
  67. data/ext/couchbase/couchbase/fmt/key_value_status_code.hxx +6 -0
  68. data/ext/couchbase/couchbase/fmt/mutation_token.hxx +6 -0
  69. data/ext/couchbase/couchbase/fmt/query_scan_consistency.hxx +6 -0
  70. data/ext/couchbase/couchbase/fmt/query_status.hxx +6 -0
  71. data/ext/couchbase/couchbase/fmt/retry_reason.hxx +6 -0
  72. data/ext/couchbase/couchbase/fmt/tls_verify_mode.hxx +6 -0
  73. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +5 -4
  74. data/ext/couchbase/couchbase/query_index_manager.hxx +4 -2
  75. data/ext/couchbase/couchbase/query_options.hxx +0 -1
  76. data/ext/couchbase/couchbase/scope.hxx +34 -1
  77. data/ext/couchbase/couchbase/subdoc/array_add_unique.hxx +2 -0
  78. data/ext/couchbase/couchbase/subdoc/array_append.hxx +2 -0
  79. data/ext/couchbase/couchbase/subdoc/array_insert.hxx +2 -0
  80. data/ext/couchbase/couchbase/subdoc/array_prepend.hxx +2 -0
  81. data/ext/couchbase/couchbase/subdoc/count.hxx +2 -0
  82. data/ext/couchbase/couchbase/subdoc/counter.hxx +2 -0
  83. data/ext/couchbase/couchbase/subdoc/exists.hxx +2 -0
  84. data/ext/couchbase/couchbase/subdoc/get.hxx +2 -0
  85. data/ext/couchbase/couchbase/subdoc/insert.hxx +2 -0
  86. data/ext/couchbase/couchbase/subdoc/remove.hxx +2 -0
  87. data/ext/couchbase/couchbase/subdoc/replace.hxx +3 -1
  88. data/ext/couchbase/couchbase/subdoc/upsert.hxx +2 -0
  89. data/ext/couchbase/couchbase/transaction_op_error_context.hxx +4 -4
  90. data/ext/couchbase/couchbase/transactions/attempt_context.hxx +1 -1
  91. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +36 -51
  92. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +1 -1
  93. data/ext/couchbase/test/CMakeLists.txt +3 -2
  94. data/ext/couchbase/test/test_helper.hxx +1 -1
  95. data/ext/couchbase/test/test_integration_analytics.cxx +289 -13
  96. data/ext/couchbase/test/test_integration_crud.cxx +8 -1
  97. data/ext/couchbase/test/test_integration_examples.cxx +182 -0
  98. data/ext/couchbase/test/test_integration_management.cxx +15 -3
  99. data/ext/couchbase/test/test_integration_search.cxx +601 -0
  100. data/ext/couchbase/test/test_transaction_transaction_simple.cxx +73 -0
  101. data/ext/couchbase/test/test_unit_config_profiles.cxx +12 -12
  102. data/ext/couchbase/test/test_unit_connection_string.cxx +35 -0
  103. data/ext/couchbase/test/test_unit_transaction_utils.cxx +76 -19
  104. data/ext/couchbase/third_party/snappy/CMakeLists.txt +150 -27
  105. data/ext/couchbase/third_party/snappy/cmake/config.h.in +28 -24
  106. data/ext/couchbase/third_party/snappy/snappy-internal.h +189 -25
  107. data/ext/couchbase/third_party/snappy/snappy-sinksource.cc +26 -9
  108. data/ext/couchbase/third_party/snappy/snappy-sinksource.h +11 -11
  109. data/ext/couchbase/third_party/snappy/snappy-stubs-internal.cc +1 -1
  110. data/ext/couchbase/third_party/snappy/snappy-stubs-internal.h +227 -308
  111. data/ext/couchbase/third_party/snappy/snappy-stubs-public.h.in +0 -11
  112. data/ext/couchbase/third_party/snappy/snappy.cc +1176 -410
  113. data/ext/couchbase/third_party/snappy/snappy.h +19 -4
  114. data/ext/couchbase.cxx +506 -26
  115. data/ext/extconf.rb +2 -1
  116. data/ext/revisions.rb +3 -2
  117. data/lib/couchbase/binary_collection.rb +4 -4
  118. data/lib/couchbase/cluster.rb +13 -9
  119. data/lib/couchbase/cluster_registry.rb +7 -2
  120. data/lib/couchbase/collection.rb +5 -0
  121. data/lib/couchbase/configuration.rb +3 -4
  122. data/lib/couchbase/errors.rb +10 -0
  123. data/lib/couchbase/management/collection_query_index_manager.rb +183 -0
  124. data/lib/couchbase/management/query_index_manager.rb +35 -3
  125. data/lib/couchbase/management.rb +1 -0
  126. data/lib/couchbase/options.rb +87 -5
  127. data/lib/couchbase/search_options.rb +158 -240
  128. data/lib/couchbase/version.rb +1 -1
  129. metadata +21 -6
  130. data/ext/couchbase/core/CMakeLists.txt +0 -0
@@ -25,6 +25,7 @@ transaction_get_result::create_from(const core::operations::lookup_in_response&
25
25
  std::optional<std::string> atr_id;
26
26
  std::optional<std::string> transaction_id;
27
27
  std::optional<std::string> attempt_id;
28
+ std::optional<std::string> operation_id;
28
29
  std::optional<std::vector<std::byte>> staged_content;
29
30
  std::optional<std::string> atr_bucket_name;
30
31
  std::optional<std::string> atr_scope_name;
@@ -56,46 +57,49 @@ transaction_get_result::create_from(const core::operations::lookup_in_response&
56
57
  attempt_id = codec::tao_json_serializer::deserialize<std::string>(resp.fields[2].value);
57
58
  }
58
59
  if (resp.fields[3].status == key_value_status_code::success) {
59
- staged_content = resp.fields[3].value;
60
+ operation_id = codec::tao_json_serializer::deserialize<std::string>(resp.fields[3].value);
60
61
  }
61
62
  if (resp.fields[4].status == key_value_status_code::success) {
62
- atr_bucket_name = codec::tao_json_serializer::deserialize<std::string>(resp.fields[4].value);
63
+ staged_content = resp.fields[4].value;
63
64
  }
64
65
  if (resp.fields[5].status == key_value_status_code::success) {
65
- atr_scope_name = codec::tao_json_serializer::deserialize<std::string>(resp.fields[5].value);
66
+ atr_bucket_name = codec::tao_json_serializer::deserialize<std::string>(resp.fields[5].value);
66
67
  }
67
68
  if (resp.fields[6].status == key_value_status_code::success) {
68
- atr_collection_name = codec::tao_json_serializer::deserialize<std::string>(resp.fields[6].value);
69
+ atr_scope_name = codec::tao_json_serializer::deserialize<std::string>(resp.fields[6].value);
69
70
  }
70
-
71
71
  if (resp.fields[7].status == key_value_status_code::success) {
72
- auto restore = core::utils::json::parse_binary(resp.fields[7].value);
72
+ atr_collection_name = codec::tao_json_serializer::deserialize<std::string>(resp.fields[7].value);
73
+ }
74
+
75
+ if (resp.fields[8].status == key_value_status_code::success) {
76
+ auto restore = core::utils::json::parse_binary(resp.fields[8].value);
73
77
  cas_pre_txn = restore["CAS"].as<std::string>();
74
78
  // only present in 6.5+
75
79
  revid_pre_txn = restore["revid"].as<std::string>();
76
80
  exptime_pre_txn = restore["exptime"].as<std::uint32_t>();
77
81
  }
78
- if (resp.fields[8].status == key_value_status_code::success) {
79
- op = codec::tao_json_serializer::deserialize<std::string>(resp.fields[8].value);
80
- }
81
82
  if (resp.fields[9].status == key_value_status_code::success) {
82
- auto doc = core::utils::json::parse_binary(resp.fields[9].value);
83
+ op = codec::tao_json_serializer::deserialize<std::string>(resp.fields[9].value);
84
+ }
85
+ if (resp.fields[10].status == key_value_status_code::success) {
86
+ auto doc = core::utils::json::parse_binary(resp.fields[10].value);
83
87
  cas_from_doc = doc["CAS"].as<std::string>();
84
88
  // only present in 6.5+
85
89
  revid_from_doc = doc["revid"].as<std::string>();
86
90
  exptime_from_doc = doc["exptime"].as<std::uint32_t>();
87
91
  crc32_from_doc = doc["value_crc32c"].as<std::string>();
88
92
  }
89
- if (resp.fields[10].status == key_value_status_code::success) {
90
- crc32_of_staging = codec::tao_json_serializer::deserialize<std::string>(resp.fields[10].value);
91
- }
92
93
  if (resp.fields[11].status == key_value_status_code::success) {
93
- forward_compat = core::utils::json::parse_binary(resp.fields[11].value);
94
+ crc32_of_staging = codec::tao_json_serializer::deserialize<std::string>(resp.fields[11].value);
95
+ }
96
+ if (resp.fields[12].status == key_value_status_code::success) {
97
+ forward_compat = core::utils::json::parse_binary(resp.fields[12].value);
94
98
  } else {
95
99
  forward_compat = tao::json::empty_object;
96
100
  }
97
- if (resp.fields[12].status == key_value_status_code::success) {
98
- content = resp.fields[12].value;
101
+ if (resp.fields[13].status == key_value_status_code::success) {
102
+ content = resp.fields[13].value;
99
103
  }
100
104
 
101
105
  transaction_links links(atr_id,
@@ -104,6 +108,7 @@ transaction_get_result::create_from(const core::operations::lookup_in_response&
104
108
  atr_collection_name,
105
109
  transaction_id,
106
110
  attempt_id,
111
+ operation_id,
107
112
  staged_content,
108
113
  cas_pre_txn,
109
114
  revid_pre_txn,
@@ -126,6 +131,7 @@ transaction_get_result::create_from(const core::document_id& id, const result& r
126
131
  std::optional<std::string> atr_id;
127
132
  std::optional<std::string> transaction_id;
128
133
  std::optional<std::string> attempt_id;
134
+ std::optional<std::string> operation_id;
129
135
  std::optional<std::vector<std::byte>> staged_content;
130
136
  std::optional<std::string> atr_bucket_name;
131
137
  std::optional<std::string> atr_scope_name;
@@ -157,45 +163,48 @@ transaction_get_result::create_from(const core::document_id& id, const result& r
157
163
  attempt_id = res.values[2].content_as<std::string>();
158
164
  }
159
165
  if (res.values[3].has_value()) {
160
- staged_content = res.values[3].raw_value;
166
+ operation_id = res.values[3].content_as<std::string>();
161
167
  }
162
168
  if (res.values[4].has_value()) {
163
- atr_bucket_name = res.values[4].content_as<std::string>();
169
+ staged_content = res.values[4].raw_value;
164
170
  }
165
171
  if (res.values[5].has_value()) {
166
- atr_scope_name = res.values[5].content_as<std::string>();
172
+ atr_bucket_name = res.values[5].content_as<std::string>();
167
173
  }
168
174
  if (res.values[6].has_value()) {
169
- atr_collection_name = res.values[6].content_as<std::string>();
175
+ atr_scope_name = res.values[6].content_as<std::string>();
170
176
  }
171
177
  if (res.values[7].has_value()) {
172
- auto restore = res.values[7].content_as();
178
+ atr_collection_name = res.values[7].content_as<std::string>();
179
+ }
180
+ if (res.values[8].has_value()) {
181
+ auto restore = res.values[8].content_as();
173
182
  cas_pre_txn = restore["CAS"].as<std::string>();
174
183
  // only present in 6.5+
175
184
  revid_pre_txn = restore["revid"].as<std::string>();
176
185
  exptime_pre_txn = restore["exptime"].as<std::uint32_t>();
177
186
  }
178
- if (res.values[8].has_value()) {
179
- op = res.values[8].content_as<std::string>();
180
- }
181
187
  if (res.values[9].has_value()) {
182
- auto doc = res.values[9].content_as();
188
+ op = res.values[9].content_as<std::string>();
189
+ }
190
+ if (res.values[10].has_value()) {
191
+ auto doc = res.values[10].content_as();
183
192
  cas_from_doc = doc["CAS"].as<std::string>();
184
193
  // only present in 6.5+
185
194
  revid_from_doc = doc["revid"].as<std::string>();
186
195
  exptime_from_doc = doc["exptime"].as<std::uint32_t>();
187
196
  crc32_from_doc = doc["value_crc32c"].as<std::string>();
188
197
  }
189
- if (res.values[10].has_value()) {
190
- crc32_of_staging = res.values[10].content_as<std::string>();
191
- }
192
198
  if (res.values[11].has_value()) {
193
- forward_compat = res.values[11].content_as();
199
+ crc32_of_staging = res.values[11].content_as<std::string>();
200
+ }
201
+ if (res.values[12].has_value()) {
202
+ forward_compat = res.values[12].content_as();
194
203
  } else {
195
204
  forward_compat = tao::json::empty_object;
196
205
  }
197
- if (res.values[12].has_value()) {
198
- content = res.values[12].raw_value;
206
+ if (res.values[13].has_value()) {
207
+ content = res.values[13].raw_value;
199
208
  }
200
209
 
201
210
  transaction_links links(atr_id,
@@ -204,6 +213,7 @@ transaction_get_result::create_from(const core::document_id& id, const result& r
204
213
  atr_collection_name,
205
214
  transaction_id,
206
215
  attempt_id,
216
+ operation_id,
207
217
  staged_content,
208
218
  cas_pre_txn,
209
219
  revid_pre_txn,
@@ -77,16 +77,19 @@ class transaction_get_result
77
77
  {
78
78
  }
79
79
 
80
- transaction_get_result(const couchbase::transactions::transaction_get_result& res)
80
+ explicit transaction_get_result(const couchbase::transactions::transaction_get_result& res)
81
81
  : cas_(res.cas())
82
82
  , document_id_(res.bucket(), res.scope(), res.collection(), res.key())
83
- , content_(res.content())
83
+ , links_(res.base_->links())
84
+ , content_(std::move(res.content()))
85
+ , metadata_(res.base_->metadata_)
84
86
  {
85
87
  }
86
88
 
87
89
  couchbase::transactions::transaction_get_result to_public_result()
88
90
  {
89
- return { document_id_.bucket(), document_id_.scope(), document_id_.collection(), document_id_.key(), cas_, std::move(content_) };
91
+ return couchbase::transactions::transaction_get_result(
92
+ std::make_shared<transaction_get_result>(document_id_, std::move(content_), cas_.value(), links_, metadata_));
90
93
  }
91
94
 
92
95
  transaction_get_result(core::document_id id, const tao::json::value& json)
@@ -126,6 +129,7 @@ class transaction_get_result
126
129
  document.links().atr_collection_name(),
127
130
  document.links().staged_transaction_id(),
128
131
  document.links().staged_attempt_id(),
132
+ document.links().staged_operation_id(),
129
133
  document.links().staged_content(),
130
134
  document.links().cas_pre_txn(),
131
135
  document.links().revid_pre_txn(),
@@ -35,6 +35,7 @@ class transaction_links
35
35
  // id of the transaction that has staged content
36
36
  std::optional<std::string> staged_transaction_id_;
37
37
  std::optional<std::string> staged_attempt_id_;
38
+ std::optional<std::string> staged_operation_id_;
38
39
  std::optional<std::vector<std::byte>> staged_content_;
39
40
 
40
41
  // for {BACKUP_FIELDS}
@@ -54,6 +55,7 @@ class transaction_links
54
55
  std::optional<std::string> atr_collection_name,
55
56
  std::optional<std::string> staged_transaction_id,
56
57
  std::optional<std::string> staged_attempt_id,
58
+ std::optional<std::string> staged_operation_id,
57
59
  std::optional<std::vector<std::byte>> staged_content,
58
60
  std::optional<std::string> cas_pre_txn,
59
61
  std::optional<std::string> revid_pre_txn,
@@ -68,6 +70,7 @@ class transaction_links
68
70
  , atr_collection_name_(std::move(atr_collection_name))
69
71
  , staged_transaction_id_(std::move(staged_transaction_id))
70
72
  , staged_attempt_id_(std::move(staged_attempt_id))
73
+ , staged_operation_id_(std::move(staged_operation_id))
71
74
  , staged_content_(std::move(staged_content))
72
75
  , cas_pre_txn_(std::move(cas_pre_txn))
73
76
  , revid_pre_txn_(std::move(revid_pre_txn))
@@ -111,6 +114,9 @@ class transaction_links
111
114
  if (staged_transaction_id_) {
112
115
  obj["txnMeta"]["txn"] = staged_transaction_id_.value();
113
116
  }
117
+ if (staged_operation_id_) {
118
+ obj["txnMeta"]["txn"] = staged_operation_id_.value();
119
+ }
114
120
  if (atr_id_) {
115
121
  obj["txnMeta"]["atr"]["key"] = atr_id_.value();
116
122
  }
@@ -181,6 +187,11 @@ class transaction_links
181
187
  return staged_attempt_id_;
182
188
  }
183
189
 
190
+ [[nodiscard]] std::optional<std::string> staged_operation_id() const
191
+ {
192
+ return staged_operation_id_;
193
+ }
194
+
184
195
  [[nodiscard]] std::optional<std::string> cas_pre_txn() const
185
196
  {
186
197
  return cas_pre_txn_;
@@ -241,12 +252,13 @@ struct fmt::formatter<couchbase::core::transactions::transaction_links> {
241
252
  constexpr auto format(const couchbase::core::transactions::transaction_links& r, FormatContext& ctx) const
242
253
  {
243
254
  return format_to(ctx.out(),
244
- "transaction_links:{{ atr: {}.{}.{}.{}, txn_id: {}, attempt_id: {}, crc32_of_staging: {} }}",
255
+ "transaction_links:{{ atr: {}.{}.{}.{}, txn_id: {}, attempt_id: {}, operation_id: {}, crc32_of_staging: {} }}",
245
256
  r.atr_bucket_name().value_or("none"),
246
257
  r.atr_scope_name().value_or("none"),
247
258
  r.atr_collection_name().value_or("none"),
248
259
  r.atr_id().value_or("none"),
249
260
  r.staged_attempt_id().value_or("none"),
261
+ r.staged_operation_id().value_or("none"),
250
262
  r.crc32_of_staging().value_or("none"));
251
263
  }
252
264
  };
@@ -46,9 +46,8 @@ transactions_cleanup::transactions_cleanup(std::shared_ptr<core::cluster> cluste
46
46
  : cluster_(cluster)
47
47
  , config_(config)
48
48
  , client_uuid_(uid_generator::next())
49
- , running_(false)
49
+ , running_(config.cleanup_config.cleanup_client_attempts || config.cleanup_config.cleanup_lost_attempts)
50
50
  {
51
- running_ = config.cleanup_config.cleanup_client_attempts || config.cleanup_config.cleanup_lost_attempts;
52
51
  if (config.cleanup_config.cleanup_client_attempts) {
53
52
  cleanup_thr_ = std::thread(std::bind(&transactions_cleanup::attempts_loop, this));
54
53
  }
@@ -61,14 +60,14 @@ transactions_cleanup::transactions_cleanup(std::shared_ptr<core::cluster> cluste
61
60
  }
62
61
  }
63
62
 
64
- static uint64_t
65
- byteswap64(uint64_t val)
63
+ static std::uint64_t
64
+ byteswap64(std::uint64_t val)
66
65
  {
67
- uint64_t ret = 0;
68
- for (std::size_t ii = 0; ii < sizeof(uint64_t); ii++) {
69
- ret <<= 8ull;
70
- ret |= val & 0xffull;
71
- val >>= 8ull;
66
+ std::uint64_t ret = 0;
67
+ for (std::size_t ii = 0; ii < sizeof(std::uint64_t); ii++) {
68
+ ret <<= 8ULL;
69
+ ret |= val & 0xffULL;
70
+ val >>= 8ULL;
72
71
  }
73
72
  return ret;
74
73
  }
@@ -86,7 +85,7 @@ byteswap64(uint64_t val)
86
85
  * Want: 0x155CD21DA7580000 (1539336197457313792 in base10, an epoch
87
86
  * time in millionths of a second)
88
87
  */
89
- static uint64_t
88
+ static std::uint64_t
90
89
  parse_mutation_cas(const std::string& cas)
91
90
  {
92
91
  if (cas.empty()) {
@@ -114,18 +113,18 @@ transactions_cleanup::interruptable_wait(std::chrono::duration<R, P> delay)
114
113
  {
115
114
  // wait for specified time, _or_ until the condition variable changes
116
115
  std::unique_lock<std::mutex> lock(mutex_);
117
- if (!running_.load()) {
116
+ if (!running_) {
118
117
  return false;
119
118
  }
120
- cv_.wait_for(lock, delay, [&]() { return !running_.load(); });
121
- return running_.load();
119
+ cv_.wait_for(lock, delay, [&]() { return !running_; });
120
+ return running_;
122
121
  }
123
122
 
124
123
  void
125
124
  transactions_cleanup::clean_collection(const couchbase::transactions::transaction_keyspace& keyspace)
126
125
  {
127
126
  // first make sure the collection is in the list
128
- while (running_.load()) {
127
+ while (is_running()) {
129
128
  {
130
129
  std::lock_guard<std::mutex> lock(mutex_);
131
130
  if (collections_.end() == std::find(collections_.begin(), collections_.end(), keyspace)) {
@@ -158,7 +157,7 @@ transactions_cleanup::clean_collection(const couchbase::transactions::transactio
158
157
  remaining_in_cleanup_window.count() / std::max<decltype(it)::difference_type>(1, atrs_left_for_this_client));
159
158
  // clean the ATR entry
160
159
  std::string atr_id = *it;
161
- if (!running_.load()) {
160
+ if (!is_running()) {
162
161
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup of {} complete", keyspace);
163
162
  return;
164
163
  }
@@ -186,7 +185,9 @@ transactions_cleanup::clean_collection(const couchbase::transactions::transactio
186
185
  atr_left.count());*/
187
186
 
188
187
  if (atr_left.count() > 0 && atr_left.count() < 1000000000) { // safety check protects against bugs
189
- std::this_thread::sleep_for(atr_left);
188
+ if (!interruptable_wait(atr_left)) {
189
+ return;
190
+ }
190
191
  }
191
192
  }
192
193
  } catch (const std::exception& ex) {
@@ -212,16 +213,16 @@ transactions_cleanup::handle_atr_cleanup(const core::document_id& atr_id, std::v
212
213
  // check_if_expired to false.
213
214
  atr_cleanup_entry cleanup_entry(entry, atr_id, *this, results == nullptr);
214
215
  try {
215
- if (results) {
216
+ if (results != nullptr) {
216
217
  results->emplace_back(cleanup_entry);
217
218
  }
218
- cleanup_entry.clean(results ? &results->back() : nullptr);
219
- if (results) {
219
+ cleanup_entry.clean(results != nullptr ? &results->back() : nullptr);
220
+ if (results != nullptr) {
220
221
  results->back().success(true);
221
222
  }
222
223
  } catch (const std::exception& e) {
223
224
  CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR("cleanup of {} failed: {}, moving on", cleanup_entry, e.what());
224
- if (results) {
225
+ if (results != nullptr) {
225
226
  results->back().success(false);
226
227
  }
227
228
  }
@@ -273,135 +274,127 @@ transactions_cleanup::create_client_record(const couchbase::transactions::transa
273
274
  const client_record_details
274
275
  transactions_cleanup::get_active_clients(const couchbase::transactions::transaction_keyspace& keyspace, const std::string& uuid)
275
276
  {
276
- std::chrono::milliseconds min_retry(1000);
277
- if (config_.cleanup_config.cleanup_window < min_retry) {
278
- min_retry = config_.cleanup_config.cleanup_window;
279
- }
280
- return retry_op_exponential_backoff_timeout<client_record_details>(
281
- min_retry, std::chrono::seconds(1), config_.cleanup_config.cleanup_window, [this, keyspace, uuid]() -> client_record_details {
282
- client_record_details details;
283
- // Write our client record, return details.
284
- try {
285
- auto id = document_id{ keyspace.bucket, keyspace.scope, keyspace.collection, CLIENT_RECORD_DOC_ID };
286
- core::operations::lookup_in_request req{ id };
287
- req.specs =
288
- lookup_in_specs{
289
- lookup_in_specs::get(FIELD_RECORDS).xattr(),
290
- lookup_in_specs::get(subdoc::lookup_in_macro::vbucket).xattr(),
291
- }
292
- .specs();
293
- wrap_request(req, config_);
294
- auto barrier = std::make_shared<std::promise<result>>();
295
- auto f = barrier->get_future();
296
- auto ec = config_.cleanup_hooks->client_record_before_get(keyspace.bucket);
297
- if (ec) {
298
- throw client_error(*ec, "client_record_before_get hook raised error");
299
- }
300
- cluster_->execute(req, [barrier](core::operations::lookup_in_response resp) {
301
- barrier->set_value(result::create_from_subdoc_response(resp));
302
- });
303
- auto res = wrap_operation_future(f);
304
- std::vector<std::string> active_client_uids;
305
- auto hlc = res.values[1].content_as();
306
- auto now_ms = now_ns_from_vbucket(hlc) / 1000000;
307
- details.override_enabled = false;
308
- details.override_expires = 0;
309
- if (res.values[0].status == subdoc_result::status_type::success) {
310
- auto records = res.values[0].content_as();
311
- CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("client records: {}", core::utils::json::generate(records));
312
- for (const auto& [key, value] : records.get_object()) {
313
- if (key == FIELD_OVERRIDE) {
314
- for (const auto& [override, param] : value.get_object()) {
315
- if (override == FIELD_OVERRIDE_ENABLED) {
316
- details.override_enabled = param.get_boolean();
317
- } else if (override == FIELD_OVERRIDE_EXPIRES) {
318
- details.override_expires = param.as<std::uint64_t>();
319
- }
320
- }
321
- } else if (key == FIELD_CLIENTS_ONLY) {
322
- for (const auto& [other_client_uuid, cl] : value.get_object()) {
323
- uint64_t heartbeat_ms = parse_mutation_cas(cl.at(FIELD_HEARTBEAT).get_string());
324
- auto expires_ms = cl.at(FIELD_EXPIRES).as<std::uint64_t>();
325
- auto expired_period = static_cast<int64_t>(now_ms) - static_cast<int64_t>(heartbeat_ms);
326
- bool has_expired = expired_period >= static_cast<int64_t>(expires_ms) && now_ms > heartbeat_ms;
327
- if (has_expired && other_client_uuid != uuid) {
328
- details.expired_client_ids.push_back(other_client_uuid);
329
- } else {
330
- active_client_uids.push_back(other_client_uuid);
331
- }
332
- }
333
- }
334
- }
335
- }
336
- if (std::find(active_client_uids.begin(), active_client_uids.end(), uuid) == active_client_uids.end()) {
337
- active_client_uids.push_back(uuid);
338
- }
339
- std::sort(active_client_uids.begin(), active_client_uids.end());
340
- auto this_idx =
341
- std::distance(active_client_uids.begin(), std::find(active_client_uids.begin(), active_client_uids.end(), uuid));
342
- details.num_active_clients = static_cast<std::uint32_t>(active_client_uids.size());
343
- details.index_of_this_client = static_cast<std::uint32_t>(this_idx);
344
- details.num_active_clients = static_cast<std::uint32_t>(active_client_uids.size());
345
- details.num_expired_clients = static_cast<std::uint32_t>(details.expired_client_ids.size());
346
- details.num_existing_clients = details.num_expired_clients + details.num_active_clients;
347
- details.client_uuid = uuid;
348
- details.cas_now_nanos = now_ms * 1000000;
349
- details.override_active = (details.override_enabled && details.override_expires > details.cas_now_nanos);
350
- CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("client details {}", details);
351
- if (details.override_active) {
352
- CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("override enabled, will not update record");
353
- return details;
354
- }
355
-
356
- // update client record, maybe cleanup some as well...
357
- core::operations::mutate_in_request mutate_req{ id };
358
- auto mut_specs = couchbase::mutate_in_specs{
359
- couchbase::mutate_in_specs::upsert(FIELD_CLIENTS + "." + uuid + "." + FIELD_HEARTBEAT, subdoc::mutate_in_macro::cas)
360
- .xattr()
361
- .create_path(),
362
- couchbase::mutate_in_specs::upsert(FIELD_CLIENTS + "." + uuid + "." + FIELD_EXPIRES,
363
- config_.cleanup_config.cleanup_window.count() / 2 + SAFETY_MARGIN_EXPIRY_MS)
364
- .xattr()
365
- .create_path(),
366
- couchbase::mutate_in_specs::upsert(FIELD_CLIENTS + "." + uuid + "." + FIELD_NUM_ATRS, atr_ids::all().size())
367
- .xattr()
368
- .create_path(),
369
- };
370
- for (std::size_t idx = 0; idx < std::min(details.expired_client_ids.size(), static_cast<std::size_t>(12)); idx++) {
371
- CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("adding {} to list of clients to be removed when updating this client",
372
- details.expired_client_ids[idx]);
373
- mut_specs.push_back(couchbase::mutate_in_specs::remove(FIELD_CLIENTS + "." + details.expired_client_ids[idx]).xattr());
374
- }
375
- mutate_req.specs = mut_specs.specs();
376
- ec = config_.cleanup_hooks->client_record_before_update(keyspace.bucket);
377
- if (ec) {
378
- throw client_error(*ec, "client_record_before_update hook raised error");
379
- }
380
- wrap_durable_request(mutate_req, config_);
381
- auto mutate_barrier = std::make_shared<std::promise<result>>();
382
- auto mutate_f = mutate_barrier->get_future();
383
- CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("updating record");
384
- cluster_->execute(mutate_req, [mutate_barrier](core::operations::mutate_in_response resp) {
385
- mutate_barrier->set_value(result::create_from_subdoc_response(resp));
386
- });
387
- res = wrap_operation_future(mutate_f);
388
-
389
- // just update the cas, and return the details
390
- details.cas_now_nanos = res.cas;
391
- CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("get_active_clients found {}", details);
392
- return details;
393
- } catch (const client_error& e) {
394
- auto ec = e.ec();
395
- switch (ec) {
396
- case FAIL_DOC_NOT_FOUND:
397
- CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("client record not found, creating new one");
398
- create_client_record(keyspace);
399
- throw retry_operation("Client record didn't exist. Creating and retrying");
400
- default:
401
- throw;
402
- }
277
+ client_record_details details;
278
+ // Write our client record, return details.
279
+ try {
280
+ auto id = document_id{ keyspace.bucket, keyspace.scope, keyspace.collection, CLIENT_RECORD_DOC_ID };
281
+ core::operations::lookup_in_request req{ id };
282
+ req.specs =
283
+ lookup_in_specs{
284
+ lookup_in_specs::get(FIELD_RECORDS).xattr(),
285
+ lookup_in_specs::get(subdoc::lookup_in_macro::vbucket).xattr(),
403
286
  }
404
- });
287
+ .specs();
288
+ wrap_request(req, config_);
289
+ auto barrier = std::make_shared<std::promise<result>>();
290
+ auto f = barrier->get_future();
291
+ auto ec = config_.cleanup_hooks->client_record_before_get(keyspace.bucket);
292
+ if (ec) {
293
+ throw client_error(*ec, "client_record_before_get hook raised error");
294
+ }
295
+ cluster_->execute(
296
+ req, [barrier](core::operations::lookup_in_response resp) { barrier->set_value(result::create_from_subdoc_response(resp)); });
297
+ auto res = wrap_operation_future(f);
298
+
299
+ std::vector<std::string> active_client_uids;
300
+ auto hlc = res.values[1].content_as();
301
+ auto now_ms = now_ns_from_vbucket(hlc) / 1000000;
302
+ details.override_enabled = false;
303
+ details.override_expires = 0;
304
+ if (res.values[0].status == subdoc_result::status_type::success) {
305
+ auto records = res.values[0].content_as();
306
+ CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("client records: {}", core::utils::json::generate(records));
307
+ for (const auto& [key, value] : records.get_object()) {
308
+ if (key == FIELD_OVERRIDE) {
309
+ for (const auto& [over_ride, param] : value.get_object()) {
310
+ if (over_ride == FIELD_OVERRIDE_ENABLED) {
311
+ details.override_enabled = param.get_boolean();
312
+ } else if (over_ride == FIELD_OVERRIDE_EXPIRES) {
313
+ details.override_expires = param.as<std::uint64_t>();
314
+ }
315
+ }
316
+ } else if (key == FIELD_CLIENTS_ONLY) {
317
+ for (const auto& [other_client_uuid, cl] : value.get_object()) {
318
+ std::uint64_t heartbeat_ms = parse_mutation_cas(cl.at(FIELD_HEARTBEAT).get_string());
319
+ auto expires_ms = cl.at(FIELD_EXPIRES).as<std::uint64_t>();
320
+ auto expired_period = static_cast<std::int64_t>(now_ms) - static_cast<std::int64_t>(heartbeat_ms);
321
+ bool has_expired = expired_period >= static_cast<std::int64_t>(expires_ms) && now_ms > heartbeat_ms;
322
+ if (has_expired && other_client_uuid != uuid) {
323
+ details.expired_client_ids.push_back(other_client_uuid);
324
+ } else {
325
+ active_client_uids.push_back(other_client_uuid);
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+ if (std::find(active_client_uids.begin(), active_client_uids.end(), uuid) == active_client_uids.end()) {
332
+ active_client_uids.push_back(uuid);
333
+ }
334
+ std::sort(active_client_uids.begin(), active_client_uids.end());
335
+ auto this_idx = std::distance(active_client_uids.begin(), std::find(active_client_uids.begin(), active_client_uids.end(), uuid));
336
+ details.num_active_clients = static_cast<std::uint32_t>(active_client_uids.size());
337
+ details.index_of_this_client = static_cast<std::uint32_t>(this_idx);
338
+ details.num_active_clients = static_cast<std::uint32_t>(active_client_uids.size());
339
+ details.num_expired_clients = static_cast<std::uint32_t>(details.expired_client_ids.size());
340
+ details.num_existing_clients = details.num_expired_clients + details.num_active_clients;
341
+ details.client_uuid = uuid;
342
+ details.cas_now_nanos = now_ms * 1000000;
343
+ details.override_active = (details.override_enabled && details.override_expires > details.cas_now_nanos);
344
+ CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("client details {}", details);
345
+ if (details.override_active) {
346
+ CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("override enabled, will not update record");
347
+ return details;
348
+ }
349
+
350
+ // update client record, maybe cleanup some as well...
351
+ core::operations::mutate_in_request mutate_req{ id };
352
+ auto mut_specs = couchbase::mutate_in_specs{
353
+ couchbase::mutate_in_specs::upsert(FIELD_CLIENTS + "." + uuid + "." + FIELD_HEARTBEAT, subdoc::mutate_in_macro::cas)
354
+ .xattr()
355
+ .create_path(),
356
+ couchbase::mutate_in_specs::upsert(FIELD_CLIENTS + "." + uuid + "." + FIELD_EXPIRES,
357
+ config_.cleanup_config.cleanup_window.count() / 2 + SAFETY_MARGIN_EXPIRY_MS)
358
+ .xattr()
359
+ .create_path(),
360
+ couchbase::mutate_in_specs::upsert(FIELD_CLIENTS + "." + uuid + "." + FIELD_NUM_ATRS, atr_ids::all().size())
361
+ .xattr()
362
+ .create_path(),
363
+ };
364
+ for (std::size_t idx = 0; idx < std::min(details.expired_client_ids.size(), static_cast<std::size_t>(12)); idx++) {
365
+ CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("adding {} to list of clients to be removed when updating this client",
366
+ details.expired_client_ids[idx]);
367
+ mut_specs.push_back(couchbase::mutate_in_specs::remove(FIELD_CLIENTS + "." + details.expired_client_ids[idx]).xattr());
368
+ }
369
+ mutate_req.specs = mut_specs.specs();
370
+ ec = config_.cleanup_hooks->client_record_before_update(keyspace.bucket);
371
+ if (ec) {
372
+ throw client_error(*ec, "client_record_before_update hook raised error");
373
+ }
374
+ wrap_durable_request(mutate_req, config_);
375
+ auto mutate_barrier = std::make_shared<std::promise<result>>();
376
+ auto mutate_f = mutate_barrier->get_future();
377
+ CB_LOST_ATTEMPT_CLEANUP_LOG_TRACE("updating record");
378
+ cluster_->execute(mutate_req, [mutate_barrier](core::operations::mutate_in_response resp) {
379
+ mutate_barrier->set_value(result::create_from_subdoc_response(resp));
380
+ });
381
+ res = wrap_operation_future(mutate_f);
382
+
383
+ // just update the cas, and return the details
384
+ details.cas_now_nanos = res.cas;
385
+ CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("get_active_clients found {}", details);
386
+ return details;
387
+ } catch (const client_error& e) {
388
+ auto ec = e.ec();
389
+ switch (ec) {
390
+ case FAIL_DOC_NOT_FOUND:
391
+ CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("client record not found, creating new one");
392
+ create_client_record(keyspace);
393
+ return get_active_clients(keyspace, uuid);
394
+ default:
395
+ throw;
396
+ }
397
+ }
405
398
  }
406
399
 
407
400
  void
@@ -412,9 +405,7 @@ transactions_cleanup::remove_client_record_from_all_buckets(const std::string& u
412
405
  retry_op_exponential_backoff_timeout<void>(
413
406
  std::chrono::milliseconds(10), std::chrono::milliseconds(250), std::chrono::milliseconds(500), [this, keyspace, uuid]() {
414
407
  try {
415
- // insure a client record document exists...
416
- create_client_record(keyspace);
417
- // now, proceed to remove the client uuid if it exists
408
+ // proceed to remove the client uuid if it exists
418
409
  auto ec = config_.cleanup_hooks->client_record_before_remove_client(keyspace.bucket);
419
410
  if (ec) {
420
411
  throw client_error(*ec, "client_record_before_remove_client hook raised error");
@@ -502,7 +493,7 @@ transactions_cleanup::attempts_loop()
502
493
  CB_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup attempts loop starting...");
503
494
  while (interruptable_wait(cleanup_loop_delay_)) {
504
495
  while (auto entry = atr_queue_.pop()) {
505
- if (!running_.load()) {
496
+ if (!is_running()) {
506
497
  CB_ATTEMPT_CLEANUP_LOG_DEBUG("loop stopping - {} entries on queue", atr_queue_.size());
507
498
  return;
508
499
  }
@@ -580,9 +571,7 @@ transactions_cleanup::close()
580
571
  }
581
572
  }
582
573
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("all lost attempt cleanup threads closed");
583
- if (true) {
584
- remove_client_record_from_all_buckets(client_uuid_);
585
- }
574
+ remove_client_record_from_all_buckets(client_uuid_);
586
575
  }
587
576
 
588
577
  transactions_cleanup::~transactions_cleanup()
@@ -119,6 +119,7 @@ class waitable_op_list
119
119
  // calling the callback). So wait for that.
120
120
  CB_TXN_LOG_TRACE("set_query_mode: mode already query, waiting for node to be set...");
121
121
  cv_query_.wait(lock, [this]() { return !mode_.query_node.empty(); });
122
+ cv_in_flight_.wait(lock, [this]() { return 0 == in_flight_; });
122
123
  in_flight_++;
123
124
  CB_TXN_LOG_TRACE("set_query_mode: node set, continuing...");
124
125
  lock.unlock();