couchbase 3.4.0 → 3.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/couchbase/CMakeLists.txt +10 -3
- data/ext/couchbase/cmake/CompilerWarnings.cmake +12 -4
- data/ext/couchbase/cmake/Documentation.cmake +4 -3
- data/ext/couchbase/cmake/OpenSSL.cmake +52 -7
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +4 -0
- data/ext/couchbase/cmake/VersionInfo.cmake +39 -3
- data/ext/couchbase/cmake/test_openssl.cxx +7 -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/crypto/CMakeLists.txt +5 -1
- data/ext/couchbase/core/impl/analytics.cxx +236 -0
- data/ext/couchbase/core/impl/cluster.cxx +0 -1
- data/ext/couchbase/core/impl/collection_query_index_manager.cxx +3 -3
- data/ext/couchbase/core/impl/dns_srv_tracker.cxx +5 -3
- data/ext/couchbase/core/impl/get_all_query_indexes.cxx +3 -3
- data/ext/couchbase/core/impl/query.cxx +5 -5
- data/ext/couchbase/core/impl/transaction_get_result.cxx +54 -0
- data/ext/couchbase/core/io/dns_client.cxx +225 -0
- data/ext/couchbase/core/io/dns_client.hxx +19 -188
- data/ext/couchbase/core/meta/CMakeLists.txt +7 -5
- data/ext/couchbase/core/meta/version.cxx +19 -0
- data/ext/couchbase/core/operations/document_search.cxx +5 -2
- data/ext/couchbase/core/operations/document_search.hxx +0 -1
- data/ext/couchbase/core/transactions/active_transaction_record.hxx +2 -2
- data/ext/couchbase/core/transactions/atr_cleanup_entry.cxx +1 -0
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +65 -31
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +44 -23
- data/ext/couchbase/core/transactions/forward_compat.hxx +2 -2
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +13 -13
- data/ext/couchbase/core/transactions/internal/transaction_fields.hxx +1 -0
- data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +7 -1
- data/ext/couchbase/core/transactions/staged_mutation.cxx +1 -1
- data/ext/couchbase/core/transactions/staged_mutation.hxx +12 -2
- data/ext/couchbase/core/transactions/transaction_context.cxx +9 -11
- data/ext/couchbase/core/transactions/transaction_get_result.cxx +41 -31
- data/ext/couchbase/core/transactions/transaction_get_result.hxx +7 -3
- data/ext/couchbase/core/transactions/transaction_links.hxx +13 -1
- data/ext/couchbase/core/transactions/transactions_cleanup.cxx +144 -155
- data/ext/couchbase/core/transactions/waitable_op_list.hxx +1 -0
- 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 +35 -2
- data/ext/couchbase/couchbase/cluster_options.hxx +10 -10
- data/ext/couchbase/couchbase/collection.hxx +22 -17
- data/ext/couchbase/couchbase/collection_query_index_manager.hxx +1 -1
- data/ext/couchbase/couchbase/common_options.hxx +1 -1
- data/ext/couchbase/couchbase/configuration_profile.hxx +1 -1
- data/ext/couchbase/couchbase/configuration_profiles_registry.hxx +0 -1
- data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +1 -1
- data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +1 -1
- data/ext/couchbase/couchbase/drop_query_index_options.hxx +1 -1
- data/ext/couchbase/couchbase/fmt/analytics_status.hxx +76 -0
- data/ext/couchbase/couchbase/fmt/cas.hxx +12 -0
- data/ext/couchbase/couchbase/fmt/durability_level.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/key_value_extended_error_info.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/key_value_status_code.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/mutation_token.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/query_scan_consistency.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/query_status.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/retry_reason.hxx +6 -0
- data/ext/couchbase/couchbase/fmt/tls_verify_mode.hxx +6 -0
- data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +5 -4
- data/ext/couchbase/couchbase/query_index_manager.hxx +4 -2
- data/ext/couchbase/couchbase/query_options.hxx +0 -1
- data/ext/couchbase/couchbase/scope.hxx +34 -1
- data/ext/couchbase/couchbase/subdoc/array_add_unique.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/array_append.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/array_insert.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/array_prepend.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/count.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/counter.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/exists.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/get.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/insert.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/remove.hxx +2 -0
- data/ext/couchbase/couchbase/subdoc/replace.hxx +3 -1
- data/ext/couchbase/couchbase/subdoc/upsert.hxx +2 -0
- data/ext/couchbase/couchbase/transaction_op_error_context.hxx +4 -4
- data/ext/couchbase/couchbase/transactions/attempt_context.hxx +1 -1
- data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +36 -51
- data/ext/couchbase/couchbase/transactions/transactions_config.hxx +1 -1
- data/ext/couchbase/test/CMakeLists.txt +3 -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 +182 -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/test/test_unit_transaction_utils.cxx +76 -19
- 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 +506 -26
- data/ext/extconf.rb +2 -1
- data/ext/revisions.rb +3 -2
- data/lib/couchbase/binary_collection.rb +4 -4
- data/lib/couchbase/cluster.rb +13 -9
- data/lib/couchbase/cluster_registry.rb +7 -2
- data/lib/couchbase/collection.rb +5 -0
- data/lib/couchbase/configuration.rb +3 -4
- data/lib/couchbase/errors.rb +10 -0
- data/lib/couchbase/management/collection_query_index_manager.rb +183 -0
- data/lib/couchbase/management/query_index_manager.rb +35 -3
- data/lib/couchbase/management.rb +1 -0
- data/lib/couchbase/options.rb +87 -5
- data/lib/couchbase/search_options.rb +158 -240
- data/lib/couchbase/version.rb +1 -1
- metadata +21 -6
- data/ext/couchbase/core/CMakeLists.txt +0 -0
@@ -174,6 +174,7 @@ atr_cleanup_entry::do_per_doc(std::vector<doc_record> docs,
|
|
174
174
|
lookup_in_specs::get(ATR_ID).xattr(),
|
175
175
|
lookup_in_specs::get(TRANSACTION_ID).xattr(),
|
176
176
|
lookup_in_specs::get(ATTEMPT_ID).xattr(),
|
177
|
+
lookup_in_specs::get(OPERATION_ID).xattr(),
|
177
178
|
lookup_in_specs::get(STAGED_DATA).xattr(),
|
178
179
|
lookup_in_specs::get(ATR_BUCKET_NAME).xattr(),
|
179
180
|
lookup_in_specs::get(ATR_SCOPE_NAME).xattr(),
|
@@ -243,6 +243,7 @@ core::operations::mutate_in_request
|
|
243
243
|
attempt_context_impl::create_staging_request(const core::document_id& id,
|
244
244
|
const transaction_get_result* document,
|
245
245
|
const std::string type,
|
246
|
+
const std::string op_id,
|
246
247
|
std::optional<std::vector<std::byte>> content)
|
247
248
|
{
|
248
249
|
core::operations::mutate_in_request req{ id };
|
@@ -250,6 +251,7 @@ attempt_context_impl::create_staging_request(const core::document_id& id,
|
|
250
251
|
txn["id"] = tao::json::empty_object;
|
251
252
|
txn["id"]["txn"] = transaction_id();
|
252
253
|
txn["id"]["atmpt"] = this->id();
|
254
|
+
txn["id"]["op"] = op_id;
|
253
255
|
txn["atr"] = tao::json::empty_object;
|
254
256
|
txn["atr"]["id"] = atr_id();
|
255
257
|
txn["atr"]["bkt"] = atr_id_->bucket();
|
@@ -296,6 +298,7 @@ attempt_context_impl::replace_raw(const transaction_get_result& document, const
|
|
296
298
|
return op_completed_with_error(std::move(cb), transaction_operation_failed(FAIL_OTHER, ec.message()));
|
297
299
|
}
|
298
300
|
try {
|
301
|
+
auto op_id = uid_generator::next();
|
299
302
|
// a get can return a 'empty' doc, so check for that and short-circuit the eventual error that will occur...
|
300
303
|
if (document.key().empty() || document.bucket().empty()) {
|
301
304
|
return op_completed_with_error(std::move(cb),
|
@@ -321,7 +324,7 @@ attempt_context_impl::replace_raw(const transaction_get_result& document, const
|
|
321
324
|
check_and_handle_blocking_transactions(
|
322
325
|
document,
|
323
326
|
forward_compat_stage::WWC_REPLACING,
|
324
|
-
[this, existing_sm = std::move(existing_sm), document = std::move(document), cb = std::move(cb), content](
|
327
|
+
[this, existing_sm = std::move(existing_sm), document = std::move(document), cb = std::move(cb), op_id, content](
|
325
328
|
std::optional<transaction_operation_failed> e1) mutable {
|
326
329
|
if (e1) {
|
327
330
|
return op_completed_with_error(std::move(cb), *e1);
|
@@ -330,7 +333,7 @@ attempt_context_impl::replace_raw(const transaction_get_result& document, const
|
|
330
333
|
document_id{ document.id().bucket(), document.id().scope(), document.id().collection(), document.id().key() };
|
331
334
|
select_atr_if_needed_unlocked(
|
332
335
|
tmp_doc,
|
333
|
-
[this, existing_sm = std::move(existing_sm), document = std::move(document), cb = std::move(cb), content](
|
336
|
+
[this, existing_sm = std::move(existing_sm), document = std::move(document), cb = std::move(cb), op_id, content](
|
334
337
|
std::optional<transaction_operation_failed> e2) mutable {
|
335
338
|
if (e2) {
|
336
339
|
return op_completed_with_error(std::move(cb), *e2);
|
@@ -339,10 +342,10 @@ attempt_context_impl::replace_raw(const transaction_get_result& document, const
|
|
339
342
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "found existing INSERT of {} while replacing", document);
|
340
343
|
exp_delay delay(
|
341
344
|
std::chrono::milliseconds(5), std::chrono::milliseconds(300), overall_.config().expiration_time);
|
342
|
-
create_staged_insert(document.id(), content, existing_sm->doc().cas().value(), delay, std::move(cb));
|
345
|
+
create_staged_insert(document.id(), content, existing_sm->doc().cas().value(), delay, op_id, std::move(cb));
|
343
346
|
return;
|
344
347
|
}
|
345
|
-
create_staged_replace(document, content, std::move(cb));
|
348
|
+
create_staged_replace(document, content, op_id, std::move(cb));
|
346
349
|
});
|
347
350
|
});
|
348
351
|
} catch (const client_error& e) {
|
@@ -361,9 +364,12 @@ attempt_context_impl::replace_raw(const transaction_get_result& document, const
|
|
361
364
|
|
362
365
|
template<typename Handler>
|
363
366
|
void
|
364
|
-
attempt_context_impl::create_staged_replace(const transaction_get_result& document,
|
367
|
+
attempt_context_impl::create_staged_replace(const transaction_get_result& document,
|
368
|
+
const std::vector<std::byte>& content,
|
369
|
+
const std::string& op_id,
|
370
|
+
Handler&& cb)
|
365
371
|
{
|
366
|
-
auto req = create_staging_request(document.id(), &document, "replace", content);
|
372
|
+
auto req = create_staging_request(document.id(), &document, "replace", op_id, content);
|
367
373
|
req.cas = document.cas();
|
368
374
|
req.access_deleted = true;
|
369
375
|
auto error_handler = [this](error_class ec, const std::string& msg, Handler&& cb) {
|
@@ -449,6 +455,7 @@ attempt_context_impl::insert_raw(const core::document_id& id, const std::vector<
|
|
449
455
|
}
|
450
456
|
try {
|
451
457
|
check_if_done(cb);
|
458
|
+
auto op_id = uid_generator::next();
|
452
459
|
staged_mutation* existing_sm = staged_mutations_->find_any(id);
|
453
460
|
if ((existing_sm != nullptr) &&
|
454
461
|
(existing_sm->type() == staged_mutation_type::INSERT || existing_sm->type() == staged_mutation_type::REPLACE)) {
|
@@ -462,17 +469,17 @@ attempt_context_impl::insert_raw(const core::document_id& id, const std::vector<
|
|
462
469
|
transaction_operation_failed(FAIL_EXPIRY, "transaction expired").expired());
|
463
470
|
}
|
464
471
|
select_atr_if_needed_unlocked(
|
465
|
-
id, [this, existing_sm, cb = std::move(cb), id, content](std::optional<transaction_operation_failed> err) mutable {
|
472
|
+
id, [this, existing_sm, cb = std::move(cb), id, op_id, content](std::optional<transaction_operation_failed> err) mutable {
|
466
473
|
if (err) {
|
467
474
|
return op_completed_with_error(std::move(cb), *err);
|
468
475
|
}
|
469
476
|
if (existing_sm != nullptr && existing_sm->type() == staged_mutation_type::REMOVE) {
|
470
477
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "found existing remove of {} while inserting", id);
|
471
|
-
return create_staged_replace(existing_sm->doc(), content, std::move(cb));
|
478
|
+
return create_staged_replace(existing_sm->doc(), content, op_id, std::move(cb));
|
472
479
|
}
|
473
480
|
uint64_t cas = 0;
|
474
481
|
exp_delay delay(std::chrono::milliseconds(5), std::chrono::milliseconds(300), overall_.config().expiration_time);
|
475
|
-
create_staged_insert(id, content, cas, delay, std::move(cb));
|
482
|
+
create_staged_insert(id, content, cas, delay, op_id, std::move(cb));
|
476
483
|
});
|
477
484
|
} catch (const std::exception& e) {
|
478
485
|
return op_completed_with_error(std::move(cb), transaction_operation_failed(FAIL_OTHER, e.what()));
|
@@ -599,6 +606,7 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
599
606
|
return error_handler(FAIL_EXPIRY, "transaction expired", std::move(cb));
|
600
607
|
}
|
601
608
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "removing {}", document);
|
609
|
+
auto op_id = uid_generator::next();
|
602
610
|
if (existing_sm != nullptr) {
|
603
611
|
if (existing_sm->type() == staged_mutation_type::REMOVE) {
|
604
612
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "found existing REMOVE of {} while removing", document);
|
@@ -616,7 +624,7 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
616
624
|
check_and_handle_blocking_transactions(
|
617
625
|
document,
|
618
626
|
forward_compat_stage::WWC_REMOVING,
|
619
|
-
[this, document = std::move(document), cb = std::move(cb), error_handler = std::move(error_handler)](
|
627
|
+
[this, document = std::move(document), cb = std::move(cb), op_id, error_handler = std::move(error_handler)](
|
620
628
|
std::optional<transaction_operation_failed> err1) mutable {
|
621
629
|
if (err1) {
|
622
630
|
return op_completed_with_error(std::move(cb), *err1);
|
@@ -625,7 +633,7 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
625
633
|
document_id{ document.id().bucket(), document.id().scope(), document.id().collection(), document.id().key() };
|
626
634
|
select_atr_if_needed_unlocked(
|
627
635
|
tmp_doc,
|
628
|
-
[document = std::move(document), cb = std::move(cb), this, error_handler = std::move(error_handler)](
|
636
|
+
[document = std::move(document), cb = std::move(cb), this, op_id, error_handler = std::move(error_handler)](
|
629
637
|
std::optional<transaction_operation_failed> err2) mutable {
|
630
638
|
if (err2) {
|
631
639
|
return op_completed_with_error(std::move(cb), *err2);
|
@@ -634,7 +642,7 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
634
642
|
return error_handler(*ec, "before_staged_remove hook raised error", std::move(cb));
|
635
643
|
}
|
636
644
|
CB_ATTEMPT_CTX_LOG_TRACE(this, "about to remove doc {} with cas {}", document.id(), document.cas().value());
|
637
|
-
auto req = create_staging_request(document.id(), &document, "remove");
|
645
|
+
auto req = create_staging_request(document.id(), &document, "remove", op_id);
|
638
646
|
req.cas = document.cas();
|
639
647
|
req.access_deleted = document.links().is_deleted();
|
640
648
|
overall_.cluster_ref()->execute(
|
@@ -1139,6 +1147,9 @@ attempt_context_impl::get_with_query(const core::document_id& id, bool optional,
|
|
1139
1147
|
// make a transaction_get_result from the row...
|
1140
1148
|
try {
|
1141
1149
|
if (resp.rows.empty()) {
|
1150
|
+
if (optional) {
|
1151
|
+
return op_completed_with_callback(std::move(cb), std::optional<transaction_get_result>());
|
1152
|
+
}
|
1142
1153
|
return op_completed_with_error(
|
1143
1154
|
std::move(cb), transaction_operation_failed(FAIL_DOC_NOT_FOUND, "document not found"));
|
1144
1155
|
}
|
@@ -1905,10 +1916,10 @@ attempt_context_impl::set_atr_pending_locked(const core::document_id& id, std::u
|
|
1905
1916
|
return fn(std::nullopt);
|
1906
1917
|
case FAIL_AMBIGUOUS:
|
1907
1918
|
// Retry just this
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1919
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "got FAIL_AMBIGUOUS, retrying set atr pending", ec);
|
1920
|
+
return overall_.after_delay(std::chrono::milliseconds(1), [this, doc_id, &lock, fn = std::move(fn)]() {
|
1921
|
+
set_atr_pending_locked(doc_id, std::move(lock), std::move(fn));
|
1922
|
+
});
|
1912
1923
|
case FAIL_TRANSIENT:
|
1913
1924
|
// Retry txn
|
1914
1925
|
return fn(err.retry());
|
@@ -1923,8 +1934,6 @@ attempt_context_impl::set_atr_pending_locked(const core::document_id& id, std::u
|
|
1923
1934
|
}
|
1924
1935
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "updating atr {}", atr_id_.value());
|
1925
1936
|
|
1926
|
-
// FIXME: do we need to capture "now" here?
|
1927
|
-
// std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
|
1928
1937
|
std::chrono::nanoseconds remaining = overall_.remaining();
|
1929
1938
|
// This bounds the value to [0-expirationTime]. It should always be in this range, this is just to protect
|
1930
1939
|
// against the application clock changing.
|
@@ -2153,6 +2162,7 @@ attempt_context_impl::get_doc(
|
|
2153
2162
|
lookup_in_specs::get(ATR_ID).xattr(),
|
2154
2163
|
lookup_in_specs::get(TRANSACTION_ID).xattr(),
|
2155
2164
|
lookup_in_specs::get(ATTEMPT_ID).xattr(),
|
2165
|
+
lookup_in_specs::get(OPERATION_ID).xattr(),
|
2156
2166
|
lookup_in_specs::get(STAGED_DATA).xattr(),
|
2157
2167
|
lookup_in_specs::get(ATR_BUCKET_NAME).xattr(),
|
2158
2168
|
lookup_in_specs::get(ATR_SCOPE_NAME).xattr(),
|
@@ -2193,6 +2203,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2193
2203
|
const std::vector<std::byte>& content,
|
2194
2204
|
uint64_t cas,
|
2195
2205
|
Delay&& delay,
|
2206
|
+
const std::string& op_id,
|
2196
2207
|
Handler&& cb,
|
2197
2208
|
error_class ec,
|
2198
2209
|
const std::string& message)
|
@@ -2211,7 +2222,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2211
2222
|
case FAIL_AMBIGUOUS:
|
2212
2223
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "FAIL_AMBIGUOUS in create_staged_insert, retrying");
|
2213
2224
|
delay();
|
2214
|
-
return create_staged_insert(id, content, cas, delay, std::forward<Handler>(cb));
|
2225
|
+
return create_staged_insert(id, content, cas, delay, op_id, std::forward<Handler>(cb));
|
2215
2226
|
case FAIL_OTHER:
|
2216
2227
|
return op_completed_with_error(std::forward<Handler>(cb), transaction_operation_failed(ec, "error in create_staged_insert"));
|
2217
2228
|
case FAIL_HARD:
|
@@ -2221,7 +2232,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2221
2232
|
case FAIL_CAS_MISMATCH: {
|
2222
2233
|
// special handling for doc already existing
|
2223
2234
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "found existing doc {}, may still be able to insert", id);
|
2224
|
-
auto error_handler = [this, id, content](error_class ec2, const std::string& err_message, Handler&& cb) mutable {
|
2235
|
+
auto error_handler = [this, id, op_id, content](error_class ec2, const std::string& err_message, Handler&& cb) mutable {
|
2225
2236
|
CB_ATTEMPT_CTX_LOG_TRACE(
|
2226
2237
|
this, "after a CAS_MISMATCH or DOC_ALREADY_EXISTS, then got error {} in create_staged_insert", ec2);
|
2227
2238
|
if (expiry_overtime_mode_.load()) {
|
@@ -2247,7 +2258,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2247
2258
|
}
|
2248
2259
|
return get_doc(
|
2249
2260
|
id,
|
2250
|
-
[this, id, content, cb = std::forward<Handler>(cb), error_handler, delay](
|
2261
|
+
[this, id, content, op_id, cb = std::forward<Handler>(cb), error_handler, delay](
|
2251
2262
|
std::optional<error_class> ec3, std::optional<std::string> err_message, std::optional<transaction_get_result> doc) mutable {
|
2252
2263
|
if (!ec3) {
|
2253
2264
|
if (doc) {
|
@@ -2266,7 +2277,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2266
2277
|
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2267
2278
|
this, "create staged insert found existing deleted doc, retrying with cas {}", doc->cas().value());
|
2268
2279
|
delay();
|
2269
|
-
return create_staged_insert(id, content, doc->cas().value(), delay, std::forward<Handler>(cb));
|
2280
|
+
return create_staged_insert(id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
|
2270
2281
|
}
|
2271
2282
|
if (!doc->links().is_document_in_transaction()) {
|
2272
2283
|
// doc was inserted outside txn elsewhere
|
@@ -2275,6 +2286,18 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2275
2286
|
std::forward<Handler>(cb),
|
2276
2287
|
document_exists({ couchbase::errc::transaction_op::document_exists_exception, key_value_error_context() }));
|
2277
2288
|
}
|
2289
|
+
if (doc->links().staged_attempt_id() == this->id()) {
|
2290
|
+
if (doc->links().staged_operation_id() == op_id) {
|
2291
|
+
// this is us dealing with resolving an ambiguity. So, lets just update the staged_mutation with the
|
2292
|
+
// correct cas and continue...
|
2293
|
+
staged_mutations_->add(staged_mutation(*doc, content, staged_mutation_type::INSERT));
|
2294
|
+
return op_completed_with_callback(std::forward<Handler>(cb), doc);
|
2295
|
+
}
|
2296
|
+
return op_completed_with_error(
|
2297
|
+
std::forward<Handler>(cb),
|
2298
|
+
transaction_operation_failed(FAIL_OTHER, "concurrent operations on a document are not allowed")
|
2299
|
+
.cause(CONCURRENT_OPERATIONS_DETECTED_ON_SAME_DOCUMENT));
|
2300
|
+
}
|
2278
2301
|
// CBD-3787 - Only a staged insert is ok to overwrite
|
2279
2302
|
if (doc->links().op() && *doc->links().op() != "insert") {
|
2280
2303
|
return op_completed_with_error(
|
@@ -2285,7 +2308,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2285
2308
|
check_and_handle_blocking_transactions(
|
2286
2309
|
*doc,
|
2287
2310
|
forward_compat_stage::WWC_INSERTING,
|
2288
|
-
[this, id, content, doc, cb = std::forward<Handler>(cb), delay](
|
2311
|
+
[this, id, op_id, content, doc, cb = std::forward<Handler>(cb), delay](
|
2289
2312
|
std::optional<transaction_operation_failed> err) mutable {
|
2290
2313
|
if (err) {
|
2291
2314
|
return op_completed_with_error(std::move(cb), *err);
|
@@ -2293,7 +2316,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2293
2316
|
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2294
2317
|
this, "doc ok to overwrite, retrying create_staged_insert with cas {}", doc->cas().value());
|
2295
2318
|
delay();
|
2296
|
-
return create_staged_insert(id, content, doc->cas().value(), delay, std::forward<Handler>(cb));
|
2319
|
+
return create_staged_insert(id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
|
2297
2320
|
});
|
2298
2321
|
} else {
|
2299
2322
|
// no doc now, just retry entire txn
|
@@ -2320,20 +2343,27 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
2320
2343
|
const std::vector<std::byte>& content,
|
2321
2344
|
uint64_t cas,
|
2322
2345
|
Delay&& delay,
|
2346
|
+
const std::string& op_id,
|
2323
2347
|
Handler&& cb)
|
2324
2348
|
{
|
2325
2349
|
|
2326
2350
|
if (auto ec = error_if_expired_and_not_in_overtime(STAGE_CREATE_STAGED_INSERT, id.key()); ec) {
|
2327
|
-
return create_staged_insert_error_handler(
|
2328
|
-
|
2351
|
+
return create_staged_insert_error_handler(id,
|
2352
|
+
content,
|
2353
|
+
cas,
|
2354
|
+
std::forward<Delay>(delay),
|
2355
|
+
op_id,
|
2356
|
+
std::forward<Handler>(cb),
|
2357
|
+
*ec,
|
2358
|
+
"create_staged_insert expired and not in overtime");
|
2329
2359
|
}
|
2330
2360
|
|
2331
2361
|
if (auto ec = hooks_.before_staged_insert(this, id.key()); ec) {
|
2332
2362
|
return create_staged_insert_error_handler(
|
2333
|
-
id, content, cas, std::forward<Delay>(delay), std::forward<Handler>(cb), *ec, "before_staged_insert hook threw error");
|
2363
|
+
id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb), *ec, "before_staged_insert hook threw error");
|
2334
2364
|
}
|
2335
2365
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "about to insert staged doc {} with cas {}", id, cas);
|
2336
|
-
auto req = create_staging_request(id, nullptr, "insert", content);
|
2366
|
+
auto req = create_staging_request(id, nullptr, "insert", op_id, content);
|
2337
2367
|
req.access_deleted = true;
|
2338
2368
|
req.create_as_deleted = true;
|
2339
2369
|
req.cas = couchbase::cas(cas);
|
@@ -2341,11 +2371,13 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
2341
2371
|
wrap_durable_request(req, overall_.config());
|
2342
2372
|
overall_.cluster_ref()->execute(
|
2343
2373
|
req,
|
2344
|
-
[this, id, content, cas, cb = std::forward<Handler>(cb), delay = std::forward<Delay>(delay)](
|
2374
|
+
[this, id, content, cas, op_id, cb = std::forward<Handler>(cb), delay = std::forward<Delay>(delay)](
|
2345
2375
|
core::operations::mutate_in_response resp) mutable {
|
2346
|
-
|
2376
|
+
auto ec = resp.ctx.ec() ? error_class_from_response(resp) : hooks_.after_staged_insert_complete(this, id.key());
|
2377
|
+
if (ec) {
|
2378
|
+
auto msg = (resp.ctx.ec() ? resp.ctx.ec().message() : "after_staged_insert hook threw error");
|
2347
2379
|
return create_staged_insert_error_handler(
|
2348
|
-
id, content, cas, std::forward<Delay>(delay), std::forward<Handler>(cb), *ec,
|
2380
|
+
id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb), *ec, msg);
|
2349
2381
|
}
|
2350
2382
|
if (!resp.ctx.ec()) {
|
2351
2383
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "inserted doc {} CAS={}, {}", id, resp.cas.value(), resp.ctx.ec().message());
|
@@ -2357,6 +2389,7 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
2357
2389
|
id.collection(),
|
2358
2390
|
overall_.transaction_id(),
|
2359
2391
|
this->id(),
|
2392
|
+
op_id,
|
2360
2393
|
content,
|
2361
2394
|
std::nullopt,
|
2362
2395
|
std::nullopt,
|
@@ -2373,6 +2406,7 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
2373
2406
|
content,
|
2374
2407
|
cas,
|
2375
2408
|
std::forward<Delay>(delay),
|
2409
|
+
op_id,
|
2376
2410
|
std::forward<Handler>(cb),
|
2377
2411
|
error_class_from_response(resp).value(),
|
2378
2412
|
resp.ctx.ec().message());
|
@@ -301,11 +301,23 @@ class attempt_context_impl
|
|
301
301
|
existing_error();
|
302
302
|
return func();
|
303
303
|
} catch (const async_operation_conflict& e) {
|
304
|
-
// can't do anything here but log and eat it.
|
305
304
|
CB_ATTEMPT_CTX_LOG_ERROR(this, "Attempted to perform txn operation after commit/rollback started: {}", e.what());
|
306
305
|
// you cannot call op_completed_with_error, as it tries to decrement
|
307
306
|
// the op count, however it didn't successfully increment it, so...
|
308
|
-
|
307
|
+
auto err = transaction_operation_failed(FAIL_OTHER, "async operation conflict");
|
308
|
+
switch (state()) {
|
309
|
+
case attempt_state::ABORTED:
|
310
|
+
case attempt_state::ROLLED_BACK:
|
311
|
+
err.cause(TRANSACTION_ALREADY_ABORTED);
|
312
|
+
break;
|
313
|
+
case attempt_state::COMMITTED:
|
314
|
+
case attempt_state::COMPLETED:
|
315
|
+
err.cause(TRANSACTION_ALREADY_COMMITTED);
|
316
|
+
break;
|
317
|
+
default:
|
318
|
+
err.cause(UNKNOWN);
|
319
|
+
}
|
320
|
+
op_completed_with_error_no_cache(std::move(cb), std::make_exception_ptr(err));
|
309
321
|
} catch (const transaction_operation_failed& e) {
|
310
322
|
// thrown only from call_func when previous error exists, so eat it, unless
|
311
323
|
// it has PREVIOUS_OP_FAILED cause
|
@@ -389,26 +401,25 @@ class attempt_context_impl
|
|
389
401
|
std::optional<std::string> query_context,
|
390
402
|
couchbase::transactions::async_query_handler&& handler) override
|
391
403
|
{
|
392
|
-
query(
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
404
|
+
query(statement,
|
405
|
+
opts,
|
406
|
+
query_context,
|
407
|
+
[handler = std::move(handler)](std::exception_ptr err, std::optional<core::operations::query_response> resp) {
|
408
|
+
if (err) {
|
409
|
+
try {
|
410
|
+
std::rethrow_exception(err);
|
411
|
+
} catch (const transaction_operation_failed& e) {
|
412
|
+
return handler(e.get_error_ctx(), {});
|
413
|
+
} catch (const op_exception& ex) {
|
414
|
+
return handler(ex.ctx(), {});
|
415
|
+
} catch (...) {
|
416
|
+
// just in case...
|
417
|
+
return handler(transaction_op_error_context(couchbase::errc::transaction_op::unknown), {});
|
418
|
+
}
|
407
419
|
}
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
});
|
420
|
+
auto [ctx, res] = core::impl::build_transaction_query_result(*resp);
|
421
|
+
handler(ctx, res);
|
422
|
+
});
|
412
423
|
}
|
413
424
|
|
414
425
|
void commit() override;
|
@@ -445,7 +456,7 @@ class attempt_context_impl
|
|
445
456
|
|
446
457
|
void state(attempt_state s)
|
447
458
|
{
|
448
|
-
overall_.
|
459
|
+
overall_.current_attempt_state(s);
|
449
460
|
}
|
450
461
|
|
451
462
|
[[nodiscard]] const std::string atr_id()
|
@@ -514,6 +525,7 @@ class attempt_context_impl
|
|
514
525
|
core::operations::mutate_in_request create_staging_request(const core::document_id& in,
|
515
526
|
const transaction_get_result* document,
|
516
527
|
const std::string type,
|
528
|
+
const std::string op_id,
|
517
529
|
std::optional<std::vector<std::byte>> content = std::nullopt);
|
518
530
|
|
519
531
|
template<typename Handler, typename Delay>
|
@@ -521,16 +533,21 @@ class attempt_context_impl
|
|
521
533
|
const std::vector<std::byte>& content,
|
522
534
|
uint64_t cas,
|
523
535
|
Delay&& delay,
|
536
|
+
const std::string& op_id,
|
524
537
|
Handler&& cb);
|
525
538
|
|
526
539
|
template<typename Handler>
|
527
|
-
void create_staged_replace(const transaction_get_result& document,
|
540
|
+
void create_staged_replace(const transaction_get_result& document,
|
541
|
+
const std::vector<std::byte>& content,
|
542
|
+
const std::string& op_id,
|
543
|
+
Handler&& cb);
|
528
544
|
|
529
545
|
template<typename Handler, typename Delay>
|
530
546
|
void create_staged_insert_error_handler(const core::document_id& id,
|
531
547
|
const std::vector<std::byte>& content,
|
532
548
|
uint64_t cas,
|
533
549
|
Delay&& delay,
|
550
|
+
const std::string& op_id,
|
534
551
|
Handler&& cb,
|
535
552
|
error_class ec,
|
536
553
|
const std::string& message);
|
@@ -601,6 +618,10 @@ class attempt_context_impl
|
|
601
618
|
|
602
619
|
void ensure_open_bucket(std::string bucket_name, std::function<void(std::error_code)>&& handler)
|
603
620
|
{
|
621
|
+
if (bucket_name.empty()) {
|
622
|
+
CB_LOG_DEBUG("ensure_open_bucket called with empty bucket_name");
|
623
|
+
return handler(couchbase::errc::common::bucket_not_found);
|
624
|
+
}
|
604
625
|
cluster_ref()->open_bucket(bucket_name, [handler = std::move(handler)](std::error_code ec) { handler(ec); });
|
605
626
|
}
|
606
627
|
};
|
@@ -122,8 +122,8 @@ struct forward_compat_behavior_full {
|
|
122
122
|
struct forward_compat_supported {
|
123
123
|
uint32_t protocol_major = 2;
|
124
124
|
uint32_t protocol_minor = 0;
|
125
|
-
std::list<std::string> extensions{ "TI", "MO", "BM",
|
126
|
-
"
|
125
|
+
std::list<std::string> extensions{ "TI", "MO", "BM", "QU", "SD", "BF3787", "BF3705", "BF3838", "RC",
|
126
|
+
"UA", "CO", "BF3791", "CM", "SI", "QC", "IX", "TS" };
|
127
127
|
};
|
128
128
|
|
129
129
|
struct forward_compat_requirement {
|
@@ -49,30 +49,29 @@ class transaction_context
|
|
49
49
|
|
50
50
|
[[nodiscard]] std::size_t num_attempts() const
|
51
51
|
{
|
52
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
52
53
|
return attempts_.size();
|
53
54
|
}
|
54
|
-
[[nodiscard]] const std::vector<transaction_attempt>& attempts() const
|
55
|
-
{
|
56
|
-
return attempts_;
|
57
|
-
}
|
58
|
-
[[nodiscard]] std::vector<transaction_attempt>& attempts()
|
59
|
-
{
|
60
|
-
return const_cast<std::vector<transaction_attempt>&>(const_cast<const transaction_context*>(this)->attempts());
|
61
|
-
}
|
62
55
|
[[nodiscard]] const transaction_attempt& current_attempt() const
|
63
56
|
{
|
57
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
64
58
|
if (attempts_.empty()) {
|
65
59
|
throw std::runtime_error("transaction context has no attempts yet");
|
66
60
|
}
|
67
61
|
return attempts_.back();
|
68
62
|
}
|
69
|
-
[[nodiscard]] transaction_attempt& current_attempt()
|
70
|
-
{
|
71
|
-
return const_cast<transaction_attempt&>(const_cast<const transaction_context*>(this)->current_attempt());
|
72
|
-
}
|
73
63
|
|
74
64
|
void add_attempt();
|
75
65
|
|
66
|
+
void current_attempt_state(attempt_state s)
|
67
|
+
{
|
68
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
69
|
+
if (attempts_.empty()) {
|
70
|
+
throw std::runtime_error("transaction_context has no attempts yet");
|
71
|
+
}
|
72
|
+
attempts_.back().state = s;
|
73
|
+
}
|
74
|
+
|
76
75
|
[[nodiscard]] std::shared_ptr<core::cluster> cluster_ref()
|
77
76
|
{
|
78
77
|
return transactions_.cluster_ref();
|
@@ -90,7 +89,7 @@ class transaction_context
|
|
90
89
|
|
91
90
|
[[nodiscard]] bool has_expired_client_side();
|
92
91
|
|
93
|
-
void
|
92
|
+
void after_delay(std::chrono::milliseconds delay, std::function<void()> fn);
|
94
93
|
|
95
94
|
[[nodiscard]] std::chrono::time_point<std::chrono::steady_clock> start_time_client() const
|
96
95
|
{
|
@@ -191,6 +190,7 @@ class transaction_context
|
|
191
190
|
std::string atr_collection_;
|
192
191
|
transactions_cleanup& cleanup_;
|
193
192
|
std::shared_ptr<attempt_context_impl> current_attempt_context_;
|
193
|
+
mutable std::mutex mutex_;
|
194
194
|
|
195
195
|
std::unique_ptr<exp_delay> delay_;
|
196
196
|
};
|
@@ -50,6 +50,7 @@ static const std::string TRANSACTION_RESTORE_PREFIX_ONLY = TRANSACTION_INTERFACE
|
|
50
50
|
static const std::string TRANSACTION_RESTORE_PREFIX = TRANSACTION_RESTORE_PREFIX_ONLY + ".";
|
51
51
|
static const std::string TRANSACTION_ID = TRANSACTION_INTERFACE_PREFIX + "id.txn";
|
52
52
|
static const std::string ATTEMPT_ID = TRANSACTION_INTERFACE_PREFIX + "id.atmpt";
|
53
|
+
static const std::string OPERATION_ID = TRANSACTION_INTERFACE_PREFIX + "id.op";
|
53
54
|
static const std::string ATR_ID = TRANSACTION_INTERFACE_PREFIX + "atr.id";
|
54
55
|
static const std::string ATR_BUCKET_NAME = TRANSACTION_INTERFACE_PREFIX + "atr.bkt";
|
55
56
|
|
@@ -145,6 +145,12 @@ class transactions_cleanup
|
|
145
145
|
|
146
146
|
void attempts_loop();
|
147
147
|
|
148
|
+
bool is_running()
|
149
|
+
{
|
150
|
+
std::unique_lock<std::mutex> lock(mutex_);
|
151
|
+
return running_;
|
152
|
+
}
|
153
|
+
|
148
154
|
template<class R, class P>
|
149
155
|
bool interruptable_wait(std::chrono::duration<R, P> time);
|
150
156
|
|
@@ -153,7 +159,7 @@ class transactions_cleanup
|
|
153
159
|
void create_client_record(const couchbase::transactions::transaction_keyspace& keyspace);
|
154
160
|
const atr_cleanup_stats handle_atr_cleanup(const core::document_id& atr_id,
|
155
161
|
std::vector<transactions_cleanup_attempt>* result = nullptr);
|
156
|
-
|
162
|
+
bool running_{ false };
|
157
163
|
};
|
158
164
|
} // namespace transactions
|
159
165
|
} // namespace couchbase::core
|
@@ -315,7 +315,7 @@ staged_mutation_queue::commit_doc(attempt_context_impl* ctx, staged_mutation& it
|
|
315
315
|
|
316
316
|
result res;
|
317
317
|
if (item.type() == staged_mutation_type::INSERT && !cas_zero_mode) {
|
318
|
-
core::operations::insert_request req{ item.doc().id(), item.
|
318
|
+
core::operations::insert_request req{ item.doc().id(), item.content() };
|
319
319
|
req.flags = couchbase::codec::codec_flags::json_common_flags;
|
320
320
|
wrap_durable_request(req, ctx->overall_.config());
|
321
321
|
auto barrier = std::make_shared<std::promise<result>>();
|
@@ -17,9 +17,9 @@
|
|
17
17
|
#pragma once
|
18
18
|
|
19
19
|
#include "attempt_context_impl.hxx"
|
20
|
-
|
21
20
|
#include "internal/utils.hxx"
|
22
21
|
#include "transaction_get_result.hxx"
|
22
|
+
#include "uid_generator.hxx"
|
23
23
|
|
24
24
|
#include <mutex>
|
25
25
|
#include <string>
|
@@ -35,13 +35,18 @@ class staged_mutation
|
|
35
35
|
transaction_get_result doc_;
|
36
36
|
staged_mutation_type type_;
|
37
37
|
std::vector<std::byte> content_;
|
38
|
+
std::string operation_id_;
|
38
39
|
|
39
40
|
public:
|
40
41
|
template<typename Content>
|
41
|
-
staged_mutation(transaction_get_result& doc,
|
42
|
+
staged_mutation(transaction_get_result& doc,
|
43
|
+
Content content,
|
44
|
+
staged_mutation_type type,
|
45
|
+
std::string operation_id = uid_generator::next())
|
42
46
|
: doc_(doc)
|
43
47
|
, type_(type)
|
44
48
|
, content_(std::move(content))
|
49
|
+
, operation_id_(std::move(operation_id))
|
45
50
|
{
|
46
51
|
}
|
47
52
|
|
@@ -97,6 +102,11 @@ class staged_mutation
|
|
97
102
|
}
|
98
103
|
throw std::runtime_error("unknown type of staged mutation");
|
99
104
|
}
|
105
|
+
|
106
|
+
[[nodiscard]] const std::string& operation_id() const
|
107
|
+
{
|
108
|
+
return operation_id_;
|
109
|
+
}
|
100
110
|
};
|
101
111
|
|
102
112
|
class staged_mutation_queue
|
@@ -17,7 +17,7 @@
|
|
17
17
|
#include "attempt_context_impl.hxx"
|
18
18
|
#include "uid_generator.hxx"
|
19
19
|
#include <asio/post.hpp>
|
20
|
-
#include <
|
20
|
+
#include <asio/steady_timer.hpp>
|
21
21
|
|
22
22
|
#include "internal/logging.hxx"
|
23
23
|
#include "internal/transaction_context.hxx"
|
@@ -45,6 +45,7 @@ void
|
|
45
45
|
transaction_context::add_attempt()
|
46
46
|
{
|
47
47
|
transaction_attempt attempt{};
|
48
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
48
49
|
attempts_.push_back(attempt);
|
49
50
|
}
|
50
51
|
|
@@ -78,17 +79,14 @@ transaction_context::has_expired_client_side()
|
|
78
79
|
}
|
79
80
|
|
80
81
|
void
|
81
|
-
transaction_context::
|
82
|
+
transaction_context::after_delay(std::chrono::milliseconds delay, std::function<void()> fn)
|
82
83
|
{
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
CB_ATTEMPT_CTX_LOG_TRACE(
|
90
|
-
current_attempt_context_, "about to sleep for {} ms", std::chrono::duration_cast<std::chrono::milliseconds>(delay).count());
|
91
|
-
std::this_thread::sleep_for(delay);
|
84
|
+
auto timer = std::make_shared<asio::steady_timer>(this->transactions_.cluster_ref()->io_context());
|
85
|
+
timer->expires_after(delay);
|
86
|
+
timer->async_wait([timer, fn](std::error_code) {
|
87
|
+
// have to always call the function, even if timer was canceled.
|
88
|
+
fn();
|
89
|
+
});
|
92
90
|
}
|
93
91
|
|
94
92
|
void
|