couchbase 3.4.1 → 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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/couchbase/CMakeLists.txt +2 -0
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +4 -0
- data/ext/couchbase/core/cluster_options.hxx +0 -1
- data/ext/couchbase/core/config_profile.cxx +23 -1
- data/ext/couchbase/core/config_profile.hxx +2 -12
- data/ext/couchbase/core/impl/analytics.cxx +236 -0
- data/ext/couchbase/core/impl/cluster.cxx +0 -1
- data/ext/couchbase/core/impl/dns_srv_tracker.cxx +5 -3
- data/ext/couchbase/core/impl/query.cxx +5 -5
- data/ext/couchbase/core/io/dns_client.cxx +225 -0
- data/ext/couchbase/core/io/dns_client.hxx +19 -188
- data/ext/couchbase/core/transactions/active_transaction_record.hxx +2 -2
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +3 -0
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +1 -1
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +12 -12
- data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +7 -1
- data/ext/couchbase/core/transactions/transaction_context.cxx +1 -0
- data/ext/couchbase/core/transactions/transactions_cleanup.cxx +144 -155
- data/ext/couchbase/core/utils/connection_string.cxx +10 -3
- data/ext/couchbase/core/utils/connection_string.hxx +3 -3
- data/ext/couchbase/couchbase/analytics_error_context.hxx +143 -0
- data/ext/couchbase/couchbase/analytics_meta_data.hxx +155 -0
- data/ext/couchbase/couchbase/analytics_metrics.hxx +163 -0
- data/ext/couchbase/couchbase/analytics_options.hxx +359 -0
- data/ext/couchbase/couchbase/analytics_result.hxx +102 -0
- data/ext/couchbase/couchbase/analytics_scan_consistency.hxx +46 -0
- data/ext/couchbase/couchbase/analytics_status.hxx +41 -0
- data/ext/couchbase/couchbase/analytics_warning.hxx +85 -0
- data/ext/couchbase/couchbase/cluster.hxx +33 -0
- data/ext/couchbase/couchbase/fmt/analytics_status.hxx +76 -0
- data/ext/couchbase/couchbase/query_options.hxx +0 -1
- data/ext/couchbase/couchbase/scope.hxx +33 -0
- data/ext/couchbase/couchbase/transactions/attempt_context.hxx +1 -1
- data/ext/couchbase/test/CMakeLists.txt +1 -2
- data/ext/couchbase/test/test_helper.hxx +1 -1
- data/ext/couchbase/test/test_integration_analytics.cxx +289 -13
- data/ext/couchbase/test/test_integration_crud.cxx +8 -1
- data/ext/couchbase/test/test_integration_examples.cxx +41 -0
- data/ext/couchbase/test/test_integration_management.cxx +15 -3
- data/ext/couchbase/test/test_integration_search.cxx +601 -0
- data/ext/couchbase/test/test_transaction_transaction_simple.cxx +73 -0
- data/ext/couchbase/test/test_unit_config_profiles.cxx +12 -12
- data/ext/couchbase/test/test_unit_connection_string.cxx +35 -0
- data/ext/couchbase/third_party/snappy/CMakeLists.txt +150 -27
- data/ext/couchbase/third_party/snappy/cmake/config.h.in +28 -24
- data/ext/couchbase/third_party/snappy/snappy-internal.h +189 -25
- data/ext/couchbase/third_party/snappy/snappy-sinksource.cc +26 -9
- data/ext/couchbase/third_party/snappy/snappy-sinksource.h +11 -11
- data/ext/couchbase/third_party/snappy/snappy-stubs-internal.cc +1 -1
- data/ext/couchbase/third_party/snappy/snappy-stubs-internal.h +227 -308
- data/ext/couchbase/third_party/snappy/snappy-stubs-public.h.in +0 -11
- data/ext/couchbase/third_party/snappy/snappy.cc +1176 -410
- data/ext/couchbase/third_party/snappy/snappy.h +19 -4
- data/ext/couchbase.cxx +27 -6
- data/ext/revisions.rb +3 -3
- data/lib/couchbase/cluster.rb +13 -9
- data/lib/couchbase/cluster_registry.rb +7 -2
- data/lib/couchbase/configuration.rb +3 -4
- data/lib/couchbase/options.rb +85 -2
- data/lib/couchbase/search_options.rb +158 -240
- data/lib/couchbase/version.rb +1 -1
- metadata +17 -6
- data/ext/couchbase/core/CMakeLists.txt +0 -0
@@ -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_(
|
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 <<=
|
70
|
-
ret |= val &
|
71
|
-
val >>=
|
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_
|
116
|
+
if (!running_) {
|
118
117
|
return false;
|
119
118
|
}
|
120
|
-
cv_.wait_for(lock, delay, [&]() { return !running_
|
121
|
-
return running_
|
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 (
|
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 (!
|
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
|
-
|
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
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
-
//
|
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 (!
|
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
|
-
|
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()
|
@@ -51,18 +51,22 @@ using opt_bucket_name = opt_must<one<'/'>, bucket_name>;
|
|
51
51
|
using opt_params = opt_must<one<'?'>, list_must<param, one<'&'>>>;
|
52
52
|
using opt_nodes = seq<list_must<node, one<',', ';'>>, opt_bucket_name>;
|
53
53
|
|
54
|
-
|
54
|
+
struct scheme : seq<uri::scheme, one<':'>, uri::dslash> {
|
55
|
+
};
|
56
|
+
using opt_scheme = opt<scheme>;
|
57
|
+
|
58
|
+
using grammar = must<seq<opt_scheme, opt_nodes, opt_params, tao::pegtl::eof>>;
|
55
59
|
|
56
60
|
template<typename Rule>
|
57
61
|
struct action {
|
58
62
|
};
|
59
63
|
|
60
64
|
template<>
|
61
|
-
struct action<
|
65
|
+
struct action<scheme> {
|
62
66
|
template<typename ActionInput>
|
63
67
|
static void apply(const ActionInput& in, connection_string& cs, connection_string::node& /* cur_node */)
|
64
68
|
{
|
65
|
-
cs.scheme = in.string();
|
69
|
+
cs.scheme = in.string().substr(0, in.string().rfind(':'));
|
66
70
|
if (cs.scheme == "couchbase") {
|
67
71
|
cs.default_port = 11210;
|
68
72
|
cs.default_mode = connection_string::bootstrap_mode::gcccp;
|
@@ -79,6 +83,9 @@ struct action<uri::scheme> {
|
|
79
83
|
cs.default_port = 18091;
|
80
84
|
cs.default_mode = connection_string::bootstrap_mode::http;
|
81
85
|
cs.tls = true;
|
86
|
+
} else {
|
87
|
+
cs.default_mode = connection_string::bootstrap_mode::unspecified;
|
88
|
+
cs.default_port = 0;
|
82
89
|
}
|
83
90
|
}
|
84
91
|
};
|
@@ -57,7 +57,7 @@ struct connection_string {
|
|
57
57
|
}
|
58
58
|
};
|
59
59
|
|
60
|
-
std::string scheme{};
|
60
|
+
std::string scheme{ "couchbase" };
|
61
61
|
bool tls{ false };
|
62
62
|
std::map<std::string, std::string> params{};
|
63
63
|
cluster_options options{};
|
@@ -65,8 +65,8 @@ struct connection_string {
|
|
65
65
|
std::vector<node> bootstrap_nodes{};
|
66
66
|
|
67
67
|
std::optional<std::string> default_bucket_name{};
|
68
|
-
bootstrap_mode default_mode{ bootstrap_mode::
|
69
|
-
std::uint16_t default_port{
|
68
|
+
bootstrap_mode default_mode{ connection_string::bootstrap_mode::gcccp };
|
69
|
+
std::uint16_t default_port{ 11210 };
|
70
70
|
|
71
71
|
std::optional<std::string> error{};
|
72
72
|
};
|
@@ -0,0 +1,143 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020-Present Couchbase, Inc.
|
4
|
+
*
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
*
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
*
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#pragma once
|
19
|
+
|
20
|
+
#include <couchbase/error_context.hxx>
|
21
|
+
|
22
|
+
#include <cstdint>
|
23
|
+
#include <optional>
|
24
|
+
|
25
|
+
namespace couchbase
|
26
|
+
{
|
27
|
+
/**
|
28
|
+
* The error context returned with Query operations.
|
29
|
+
*
|
30
|
+
* @since 1.0.0
|
31
|
+
* @committed
|
32
|
+
*/
|
33
|
+
class analytics_error_context : public error_context
|
34
|
+
{
|
35
|
+
public:
|
36
|
+
/**
|
37
|
+
* Creates empty error context
|
38
|
+
*
|
39
|
+
* @since 1.0.0
|
40
|
+
* @committed
|
41
|
+
*/
|
42
|
+
analytics_error_context() = default;
|
43
|
+
|
44
|
+
analytics_error_context(std::error_code ec,
|
45
|
+
std::optional<std::string> last_dispatched_to,
|
46
|
+
std::optional<std::string> last_dispatched_from,
|
47
|
+
std::size_t retry_attempts,
|
48
|
+
std::set<retry_reason> retry_reasons,
|
49
|
+
std::uint64_t first_error_code,
|
50
|
+
std::string first_error_message,
|
51
|
+
std::string client_context_id,
|
52
|
+
std::string statement,
|
53
|
+
std::optional<std::string> parameters,
|
54
|
+
std::string method,
|
55
|
+
std::string path,
|
56
|
+
std::uint32_t http_status,
|
57
|
+
std::string http_body,
|
58
|
+
std::string hostname,
|
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) }
|
61
|
+
, first_error_code_{ first_error_code }
|
62
|
+
, first_error_message_{ std::move(first_error_message) }
|
63
|
+
, client_context_id_{ std::move(client_context_id) }
|
64
|
+
, statement_{ std::move(statement) }
|
65
|
+
, parameters_{ std::move(parameters) }
|
66
|
+
, method_{ std::move(method) }
|
67
|
+
, path_{ std::move(path) }
|
68
|
+
, http_status_{ http_status }
|
69
|
+
, http_body_{ std::move(http_body) }
|
70
|
+
, hostname_{ std::move(hostname) }
|
71
|
+
, port_{ port }
|
72
|
+
{
|
73
|
+
}
|
74
|
+
|
75
|
+
[[nodiscard]] auto first_error_code() const -> std::uint64_t
|
76
|
+
{
|
77
|
+
return first_error_code_;
|
78
|
+
}
|
79
|
+
|
80
|
+
[[nodiscard]] auto first_error_message() const -> const std::string&
|
81
|
+
{
|
82
|
+
return first_error_message_;
|
83
|
+
}
|
84
|
+
|
85
|
+
[[nodiscard]] auto client_context_id() const -> const std::string&
|
86
|
+
{
|
87
|
+
return client_context_id_;
|
88
|
+
}
|
89
|
+
|
90
|
+
[[nodiscard]] auto statement() const -> const std::string&
|
91
|
+
{
|
92
|
+
return statement_;
|
93
|
+
}
|
94
|
+
|
95
|
+
[[nodiscard]] auto parameters() const -> const std::optional<std::string>&
|
96
|
+
{
|
97
|
+
return parameters_;
|
98
|
+
}
|
99
|
+
|
100
|
+
[[nodiscard]] auto method() const -> const std::string&
|
101
|
+
{
|
102
|
+
return method_;
|
103
|
+
}
|
104
|
+
|
105
|
+
[[nodiscard]] auto path() const -> const std::string&
|
106
|
+
{
|
107
|
+
return path_;
|
108
|
+
}
|
109
|
+
|
110
|
+
[[nodiscard]] auto http_status() const -> std::uint32_t
|
111
|
+
{
|
112
|
+
return http_status_;
|
113
|
+
}
|
114
|
+
|
115
|
+
[[nodiscard]] auto http_body() const -> const std::string&
|
116
|
+
{
|
117
|
+
return http_body_;
|
118
|
+
}
|
119
|
+
|
120
|
+
[[nodiscard]] auto hostname() const -> const std::string&
|
121
|
+
{
|
122
|
+
return hostname_;
|
123
|
+
}
|
124
|
+
|
125
|
+
[[nodiscard]] auto port() const -> std::uint16_t
|
126
|
+
{
|
127
|
+
return port_;
|
128
|
+
}
|
129
|
+
|
130
|
+
private:
|
131
|
+
std::uint64_t first_error_code_{};
|
132
|
+
std::string first_error_message_{};
|
133
|
+
std::string client_context_id_{};
|
134
|
+
std::string statement_{};
|
135
|
+
std::optional<std::string> parameters_{};
|
136
|
+
std::string method_{};
|
137
|
+
std::string path_{};
|
138
|
+
std::uint32_t http_status_{};
|
139
|
+
std::string http_body_{};
|
140
|
+
std::string hostname_{};
|
141
|
+
std::uint16_t port_{};
|
142
|
+
};
|
143
|
+
} // namespace couchbase
|