couchbase 3.5.6 → 3.6.0
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/cache/extconf_include.rb +3 -3
- data/ext/cache/mozilla-ca-bundle.crt +3 -165
- data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
- data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/CMakeLists.txt +14 -10
- data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.cc +7 -4
- data/ext/couchbase/CMakeLists.txt +12 -1
- data/ext/couchbase/cmake/Profiler.cmake +15 -0
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +2 -2
- data/ext/couchbase/cmake/couchbase_cxx_client.pc.in +1 -1
- data/ext/couchbase/core/app_telemetry_address.cxx +55 -0
- data/ext/couchbase/core/app_telemetry_address.hxx +39 -0
- data/ext/couchbase/core/app_telemetry_meter.cxx +753 -0
- data/ext/couchbase/core/app_telemetry_meter.hxx +198 -0
- data/ext/couchbase/core/app_telemetry_reporter.cxx +895 -0
- data/ext/couchbase/core/app_telemetry_reporter.hxx +59 -0
- data/ext/couchbase/core/bucket.cxx +77 -35
- data/ext/couchbase/core/bucket.hxx +17 -10
- data/ext/couchbase/core/cluster.cxx +54 -16
- data/ext/couchbase/core/cluster_credentials.cxx +27 -0
- data/ext/couchbase/core/cluster_credentials.hxx +36 -0
- data/ext/couchbase/core/cluster_options.hxx +12 -0
- data/ext/couchbase/core/collections_component.cxx +7 -5
- data/ext/couchbase/core/http_component.cxx +6 -0
- data/ext/couchbase/core/impl/binary_collection.cxx +4 -0
- data/ext/couchbase/core/impl/bucket_manager.cxx +2 -0
- data/ext/couchbase/core/impl/cluster.cxx +9 -0
- data/ext/couchbase/core/impl/collection.cxx +2 -0
- data/ext/couchbase/core/impl/error.cxx +1 -0
- data/ext/couchbase/core/impl/logger.cxx +51 -0
- data/ext/couchbase/core/impl/replica_utils.cxx +1 -1
- data/ext/couchbase/core/impl/transaction_get_multi_replicas_from_preferred_server_group_spec.cxx +32 -0
- data/ext/couchbase/core/impl/transaction_get_multi_spec.cxx +30 -0
- data/ext/couchbase/core/impl/transaction_op_error_category.cxx +2 -0
- data/ext/couchbase/core/io/config_tracker.cxx +6 -6
- data/ext/couchbase/core/io/http_command.hxx +35 -11
- data/ext/couchbase/core/io/http_session.cxx +10 -0
- data/ext/couchbase/core/io/http_session.hxx +4 -0
- data/ext/couchbase/core/io/http_session_manager.hxx +83 -34
- data/ext/couchbase/core/io/mcbp_command.hxx +41 -2
- data/ext/couchbase/core/io/mcbp_session.cxx +52 -19
- data/ext/couchbase/core/io/mcbp_session.hxx +3 -0
- data/ext/couchbase/core/logger/logger.cxx +46 -0
- data/ext/couchbase/core/logger/logger.hxx +41 -1
- data/ext/couchbase/core/management/bucket_settings.hxx +1 -0
- data/ext/couchbase/core/management/bucket_settings_json.hxx +4 -0
- data/ext/couchbase/core/meta/features.hxx +32 -0
- data/ext/couchbase/core/operations/document_analytics.cxx +9 -9
- data/ext/couchbase/core/operations/document_append.cxx +1 -0
- data/ext/couchbase/core/operations/document_append.hxx +1 -0
- data/ext/couchbase/core/operations/document_get_all_replicas.hxx +10 -2
- data/ext/couchbase/core/operations/document_lookup_in.cxx +4 -0
- data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +14 -2
- data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +4 -0
- data/ext/couchbase/core/operations/document_mutate_in.cxx +4 -0
- data/ext/couchbase/core/operations/document_mutate_in.hxx +1 -0
- data/ext/couchbase/core/operations/document_prepend.cxx +1 -0
- data/ext/couchbase/core/operations/document_prepend.hxx +1 -0
- data/ext/couchbase/core/operations/document_query.cxx +12 -10
- data/ext/couchbase/core/operations/http_noop.cxx +1 -0
- data/ext/couchbase/core/operations/management/bucket_create.cxx +3 -0
- data/ext/couchbase/core/operations/management/bucket_update.cxx +3 -0
- data/ext/couchbase/core/origin.cxx +0 -5
- data/ext/couchbase/core/origin.hxx +2 -11
- data/ext/couchbase/core/platform/random.cc +6 -3
- data/ext/couchbase/core/platform/random.h +2 -2
- data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +9 -0
- data/ext/couchbase/core/timeout_defaults.hxx +4 -0
- data/ext/couchbase/core/topology/configuration.cxx +10 -13
- data/ext/couchbase/core/topology/configuration.hxx +14 -15
- data/ext/couchbase/core/topology/configuration_json.hxx +6 -0
- data/ext/couchbase/core/transactions/async_attempt_context.hxx +22 -2
- data/ext/couchbase/core/transactions/attempt_context.hxx +25 -7
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +688 -238
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +91 -12
- data/ext/couchbase/core/transactions/exceptions.cxx +5 -0
- data/ext/couchbase/core/transactions/exceptions.hxx +20 -0
- data/ext/couchbase/core/transactions/exceptions_fmt.hxx +3 -0
- data/ext/couchbase/core/transactions/forward_compat.cxx +71 -6
- data/ext/couchbase/core/transactions/forward_compat.hxx +45 -59
- data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +616 -0
- data/ext/couchbase/core/transactions/get_multi_orchestrator.hxx +61 -0
- data/ext/couchbase/core/transactions/internal/doc_record.cxx +8 -0
- data/ext/couchbase/core/transactions/internal/doc_record.hxx +16 -5
- data/ext/couchbase/core/transactions/internal/exceptions_internal.hxx +12 -0
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +13 -0
- data/ext/couchbase/core/transactions/internal/transaction_fields.hxx +1 -0
- data/ext/couchbase/core/transactions/staged_mutation.cxx +277 -96
- data/ext/couchbase/core/transactions/staged_mutation.hxx +28 -76
- data/ext/couchbase/core/transactions/transaction_context.cxx +33 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_mode.hxx +28 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +27 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +71 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_result.hxx +66 -0
- data/ext/couchbase/core/transactions/transaction_links.hxx +10 -0
- data/ext/couchbase/core/transactions/transactions.cxx +8 -3
- data/ext/couchbase/core/utils/connection_string.cxx +4 -0
- data/ext/couchbase/core/utils/url_codec.cxx +26 -0
- data/ext/couchbase/core/utils/url_codec.hxx +11 -0
- data/ext/couchbase/core/websocket_codec.cxx +647 -0
- data/ext/couchbase/core/websocket_codec.hxx +77 -0
- data/ext/couchbase/couchbase/analytics_options.hxx +70 -6
- data/ext/couchbase/couchbase/application_telemetry_options.hxx +124 -0
- data/ext/couchbase/couchbase/cluster_options.hxx +17 -0
- data/ext/couchbase/couchbase/error_codes.hxx +1 -0
- data/ext/couchbase/couchbase/logger.hxx +16 -0
- data/ext/couchbase/couchbase/management/bucket_settings.hxx +1 -0
- data/ext/couchbase/couchbase/query_options.hxx +70 -6
- data/ext/couchbase/couchbase/transactions/async_attempt_context.hxx +29 -5
- data/ext/couchbase/couchbase/transactions/attempt_context.hxx +24 -7
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_mode.hxx +47 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_options.hxx +44 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +46 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_options.hxx +48 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +109 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_spec.hxx +47 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +102 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_spec.hxx +45 -0
- data/ext/extconf.rb +6 -0
- data/ext/rcb_buckets.cxx +26 -0
- data/lib/active_support/cache/couchbase_store.rb +1 -1
- data/lib/couchbase/cluster.rb +1 -1
- data/lib/couchbase/collection.rb +1 -1
- data/lib/couchbase/collection_options.rb +2 -2
- data/lib/couchbase/management/analytics_index_manager.rb +4 -4
- data/lib/couchbase/management/bucket_manager.rb +8 -2
- data/lib/couchbase/protostellar/cluster.rb +2 -2
- data/lib/couchbase/protostellar/collection.rb +1 -1
- data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +1 -1
- data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +4 -4
- data/lib/couchbase/protostellar/request_generator/admin/collection.rb +6 -6
- data/lib/couchbase/protostellar/request_generator/admin/query.rb +13 -13
- data/lib/couchbase/protostellar/request_generator/kv.rb +25 -25
- data/lib/couchbase/protostellar/request_generator/query.rb +4 -4
- data/lib/couchbase/protostellar/request_generator/search.rb +25 -25
- data/lib/couchbase/protostellar/response_converter/search.rb +1 -1
- data/lib/couchbase/protostellar/retry/reason.rb +1 -1
- data/lib/couchbase/protostellar/timeouts.rb +1 -1
- data/lib/couchbase/scope.rb +1 -1
- data/lib/couchbase/transcoder_flags.rb +1 -1
- data/lib/couchbase/utils/stdlib_logger_adapter.rb +1 -1
- data/lib/couchbase/version.rb +1 -1
- metadata +47 -19
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/COPYING +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/SnappyConfig.cmake.in +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/config.h.in +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.cc +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-internal.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.cc +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.cc +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-public.h.in +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.h +0 -0
@@ -21,12 +21,18 @@
|
|
21
21
|
#include "attempt_context_testing_hooks.hxx"
|
22
22
|
#include "attempt_state.hxx"
|
23
23
|
#include "core/cluster.hxx"
|
24
|
+
#include "core/error_context/transaction_error_context.hxx"
|
25
|
+
#include "core/error_context/transaction_op_error_context.hxx"
|
24
26
|
#include "core/impl/error.hxx"
|
25
27
|
#include "core/operations.hxx"
|
26
28
|
#include "core/transactions/error_class.hxx"
|
29
|
+
#include "core/transactions/transaction_get_multi_mode.hxx"
|
30
|
+
#include "couchbase/error_codes.hxx"
|
27
31
|
#include "durability_level.hxx"
|
28
32
|
#include "exceptions.hxx"
|
33
|
+
#include "exceptions_fmt.hxx"
|
29
34
|
#include "forward_compat.hxx"
|
35
|
+
#include "get_multi_orchestrator.hxx"
|
30
36
|
#include "internal/exceptions_internal.hxx"
|
31
37
|
#include "internal/exceptions_internal_fmt.hxx"
|
32
38
|
#include "internal/logging.hxx"
|
@@ -36,6 +42,8 @@
|
|
36
42
|
#include "internal/utils.hxx"
|
37
43
|
#include "staged_mutation.hxx"
|
38
44
|
|
45
|
+
#include <optional>
|
46
|
+
|
39
47
|
namespace couchbase::core::transactions
|
40
48
|
{
|
41
49
|
|
@@ -235,9 +243,10 @@ attempt_context_impl::get(const core::document_id& id, Callback&& cb)
|
|
235
243
|
false,
|
236
244
|
std::nullopt,
|
237
245
|
[self, id, cb = std::move(cb)](std::optional<error_class> ec,
|
246
|
+
std::optional<external_exception> cause,
|
238
247
|
const std::optional<std::string>& err_message,
|
239
248
|
std::optional<transaction_get_result> res) mutable {
|
240
|
-
auto handler = [self, id, err_message, res = std::move(res), cb = std::move(cb)](
|
249
|
+
auto handler = [self, id, cause, err_message, res = std::move(res), cb = std::move(cb)](
|
241
250
|
std::optional<error_class> ec) mutable {
|
242
251
|
if (ec) {
|
243
252
|
switch (*ec) {
|
@@ -268,8 +277,8 @@ attempt_context_impl::get(const core::document_id& id, Callback&& cb)
|
|
268
277
|
err_message.value_or(""),
|
269
278
|
*ec,
|
270
279
|
id.key());
|
271
|
-
return self->op_completed_with_error(
|
272
|
-
|
280
|
+
return self->op_completed_with_error(
|
281
|
+
std::move(cb), transaction_operation_failed(FAIL_OTHER, cause, msg));
|
273
282
|
}
|
274
283
|
}
|
275
284
|
} else {
|
@@ -330,9 +339,10 @@ attempt_context_impl::get_optional(const core::document_id& id, Callback&& cb)
|
|
330
339
|
false,
|
331
340
|
std::nullopt,
|
332
341
|
[self, id, cb = std::move(cb)](std::optional<error_class> ec,
|
342
|
+
std::optional<external_exception> cause,
|
333
343
|
const std::optional<std::string>& err_message,
|
334
344
|
std::optional<transaction_get_result> res) mutable {
|
335
|
-
auto handler = [self, id, err_message, res, cb = std::move(cb)](
|
345
|
+
auto handler = [self, id, cause, err_message, res, cb = std::move(cb)](
|
336
346
|
std::optional<error_class> ec) mutable {
|
337
347
|
if (ec) {
|
338
348
|
switch (*ec) {
|
@@ -341,6 +351,7 @@ attempt_context_impl::get_optional(const core::document_id& id, Callback&& cb)
|
|
341
351
|
std::move(cb),
|
342
352
|
transaction_operation_failed(
|
343
353
|
*ec,
|
354
|
+
cause,
|
344
355
|
fmt::format("transaction expired during get {}", err_message.value_or("")))
|
345
356
|
.expired());
|
346
357
|
case FAIL_DOC_NOT_FOUND:
|
@@ -350,19 +361,22 @@ attempt_context_impl::get_optional(const core::document_id& id, Callback&& cb)
|
|
350
361
|
return self->op_completed_with_error(
|
351
362
|
std::move(cb),
|
352
363
|
transaction_operation_failed(
|
353
|
-
*ec,
|
364
|
+
*ec,
|
365
|
+
cause,
|
366
|
+
fmt::format("transient failure in get {}", err_message.value_or("")))
|
354
367
|
.retry());
|
355
368
|
case FAIL_HARD:
|
356
369
|
return self->op_completed_with_error(
|
357
370
|
std::move(cb),
|
358
371
|
transaction_operation_failed(
|
359
|
-
*ec, fmt::format("fail hard in get {}", err_message.value_or("")))
|
372
|
+
*ec, cause, fmt::format("fail hard in get {}", err_message.value_or("")))
|
360
373
|
.no_rollback());
|
361
374
|
default: {
|
362
375
|
return self->op_completed_with_error(
|
363
376
|
std::move(cb),
|
364
377
|
transaction_operation_failed(
|
365
378
|
FAIL_OTHER,
|
379
|
+
cause,
|
366
380
|
fmt::format("error getting {} {}", id.key(), err_message.value_or(""))));
|
367
381
|
}
|
368
382
|
}
|
@@ -393,10 +407,10 @@ attempt_context_impl::get_replica_from_preferred_server_group(
|
|
393
407
|
std::function<void(std::exception_ptr, std::optional<transaction_get_result>)>&& cb)
|
394
408
|
{
|
395
409
|
if (op_list_.get_mode().is_query()) {
|
396
|
-
return cb(
|
397
|
-
|
398
|
-
|
399
|
-
|
410
|
+
return cb(
|
411
|
+
std::make_exception_ptr(transaction_operation_failed(
|
412
|
+
FAIL_OTHER, FEATURE_NOT_AVAILABLE_EXCEPTION, "Get Replica is not supported in Query Mode")),
|
413
|
+
{});
|
400
414
|
}
|
401
415
|
cache_error_async(cb, [self = shared_from_this(), id, cb]() mutable {
|
402
416
|
self->check_if_done(cb);
|
@@ -405,16 +419,18 @@ attempt_context_impl::get_replica_from_preferred_server_group(
|
|
405
419
|
true,
|
406
420
|
std::nullopt,
|
407
421
|
[self, id, cb = std::move(cb)](std::optional<error_class> ec,
|
422
|
+
std::optional<external_exception> cause,
|
408
423
|
const std::optional<std::string>& err_message,
|
409
424
|
std::optional<transaction_get_result> res) mutable {
|
410
|
-
auto handler = [self, id, err_message, res = std::move(res), cb = std::move(cb)](
|
425
|
+
auto handler = [self, id, cause, err_message, res = std::move(res), cb = std::move(cb)](
|
411
426
|
std::optional<error_class> ec) mutable {
|
412
427
|
if (ec) {
|
413
428
|
switch (*ec) {
|
414
429
|
case FAIL_EXPIRY:
|
415
430
|
return self->op_completed_with_error(
|
416
431
|
std::move(cb),
|
417
|
-
transaction_operation_failed(*ec, "transaction expired during get")
|
432
|
+
transaction_operation_failed(*ec, cause, "transaction expired during get")
|
433
|
+
.expired());
|
418
434
|
case FAIL_DOC_NOT_FOUND:
|
419
435
|
return self->op_completed_with_callback(std::move(cb),
|
420
436
|
std::optional<transaction_get_result>());
|
@@ -422,18 +438,22 @@ attempt_context_impl::get_replica_from_preferred_server_group(
|
|
422
438
|
return self->op_completed_with_error(
|
423
439
|
std::move(cb),
|
424
440
|
transaction_operation_failed(
|
425
|
-
*ec,
|
441
|
+
*ec,
|
442
|
+
cause,
|
443
|
+
fmt::format("transient failure in get {}", err_message.value_or("")))
|
426
444
|
.retry());
|
427
445
|
case FAIL_HARD:
|
428
446
|
return self->op_completed_with_error(
|
429
447
|
std::move(cb),
|
430
448
|
transaction_operation_failed(
|
431
|
-
*ec, fmt::format("fail hard in get {}", err_message.value_or("")))
|
449
|
+
*ec, cause, fmt::format("fail hard in get {}", err_message.value_or("")))
|
432
450
|
.no_rollback());
|
433
451
|
case FAIL_OTHER:
|
434
|
-
if (
|
435
|
-
return self->op_completed_with_callback(
|
436
|
-
|
452
|
+
if (cause == DOCUMENT_UNRETRIEVABLE_EXCEPTION) {
|
453
|
+
return self->op_completed_with_callback(
|
454
|
+
std::move(cb),
|
455
|
+
transaction_operation_failed(FAIL_OTHER, cause, "failed to retrieve document"),
|
456
|
+
std::move(res));
|
437
457
|
}
|
438
458
|
[[fallthrough]];
|
439
459
|
default: {
|
@@ -441,14 +461,16 @@ attempt_context_impl::get_replica_from_preferred_server_group(
|
|
441
461
|
err_message.value_or(""),
|
442
462
|
*ec,
|
443
463
|
id.key());
|
444
|
-
return self->op_completed_with_error(
|
445
|
-
|
464
|
+
return self->op_completed_with_error(
|
465
|
+
std::move(cb), transaction_operation_failed(FAIL_OTHER, cause, msg));
|
446
466
|
}
|
447
467
|
}
|
448
468
|
} else {
|
449
469
|
if (!res) {
|
450
470
|
return self->op_completed_with_error(
|
451
|
-
std::move(cb),
|
471
|
+
std::move(cb),
|
472
|
+
transaction_operation_failed(
|
473
|
+
*ec, external_exception::DOCUMENT_NOT_FOUND_EXCEPTION, "document not found"));
|
452
474
|
}
|
453
475
|
auto err =
|
454
476
|
check_forward_compat(forward_compat_stage::GETS, res->links().forward_compat());
|
@@ -476,7 +498,7 @@ attempt_context_impl::get_replica_from_preferred_server_group(const core::docume
|
|
476
498
|
get_replica_from_preferred_server_group(
|
477
499
|
id, [barrier](const std::exception_ptr& err, std::optional<transaction_get_result> res) {
|
478
500
|
if (err) {
|
479
|
-
barrier->set_exception(err);
|
501
|
+
return barrier->set_exception(err);
|
480
502
|
}
|
481
503
|
return barrier->set_value(std::move(res));
|
482
504
|
});
|
@@ -520,6 +542,277 @@ attempt_context_impl::get_replica_from_preferred_server_group(
|
|
520
542
|
});
|
521
543
|
}
|
522
544
|
|
545
|
+
namespace
|
546
|
+
{
|
547
|
+
// FIXME(SA): do not use Public API in the core
|
548
|
+
auto
|
549
|
+
from_public_api(couchbase::transactions::transaction_get_multi_mode mode)
|
550
|
+
-> transaction_get_multi_mode
|
551
|
+
{
|
552
|
+
switch (mode) {
|
553
|
+
case couchbase::transactions::transaction_get_multi_mode::prioritise_latency:
|
554
|
+
return transaction_get_multi_mode::prioritise_latency;
|
555
|
+
case couchbase::transactions::transaction_get_multi_mode::disable_read_skew_detection:
|
556
|
+
return transaction_get_multi_mode::disable_read_skew_detection;
|
557
|
+
case couchbase::transactions::transaction_get_multi_mode::prioritise_read_skew_detection:
|
558
|
+
return transaction_get_multi_mode::prioritise_read_skew_detection;
|
559
|
+
}
|
560
|
+
return transaction_get_multi_mode::prioritise_latency;
|
561
|
+
}
|
562
|
+
|
563
|
+
auto
|
564
|
+
from_public_api(
|
565
|
+
couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_mode mode)
|
566
|
+
-> transaction_get_multi_replicas_from_preferred_server_group_mode
|
567
|
+
{
|
568
|
+
switch (mode) {
|
569
|
+
case couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_mode::
|
570
|
+
prioritise_latency:
|
571
|
+
return transaction_get_multi_replicas_from_preferred_server_group_mode::prioritise_latency;
|
572
|
+
case couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_mode::
|
573
|
+
disable_read_skew_detection:
|
574
|
+
return transaction_get_multi_replicas_from_preferred_server_group_mode::
|
575
|
+
disable_read_skew_detection;
|
576
|
+
case couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_mode::
|
577
|
+
prioritise_read_skew_detection:
|
578
|
+
return transaction_get_multi_replicas_from_preferred_server_group_mode::
|
579
|
+
prioritise_read_skew_detection;
|
580
|
+
break;
|
581
|
+
}
|
582
|
+
return transaction_get_multi_replicas_from_preferred_server_group_mode::prioritise_latency;
|
583
|
+
}
|
584
|
+
} // namespace
|
585
|
+
|
586
|
+
void
|
587
|
+
attempt_context_impl::get_multi(
|
588
|
+
const std::vector<core::document_id>& ids,
|
589
|
+
transaction_get_multi_mode mode,
|
590
|
+
std::function<void(std::exception_ptr, std::optional<transaction_get_multi_result>)>&& cb)
|
591
|
+
{
|
592
|
+
if (op_list_.get_mode().is_query()) {
|
593
|
+
return cb(std::make_exception_ptr(op_exception({ errc::transaction_op::feature_not_available },
|
594
|
+
"Get Multi is not supported in Query Mode",
|
595
|
+
FEATURE_NOT_AVAILABLE_EXCEPTION)),
|
596
|
+
{});
|
597
|
+
}
|
598
|
+
cache_error_async(cb, [self = shared_from_this(), ids, mode, cb]() mutable {
|
599
|
+
self->check_if_done(cb);
|
600
|
+
const auto manager = std::make_shared<get_multi_orchestrator>(self, ids);
|
601
|
+
manager->get_multi(
|
602
|
+
mode,
|
603
|
+
[self, cb = std::move(cb)](std::exception_ptr err,
|
604
|
+
std::optional<transaction_get_multi_result> res) mutable {
|
605
|
+
if (err) {
|
606
|
+
self->op_completed_with_error(std::move(cb), std::move(err));
|
607
|
+
return;
|
608
|
+
}
|
609
|
+
self->op_completed_with_callback(std::move(cb), std::move(res));
|
610
|
+
});
|
611
|
+
});
|
612
|
+
}
|
613
|
+
|
614
|
+
auto
|
615
|
+
attempt_context_impl::get_multi(const std::vector<core::document_id>& ids,
|
616
|
+
transaction_get_multi_mode mode) -> transaction_get_multi_result
|
617
|
+
{
|
618
|
+
auto barrier = std::make_shared<std::promise<transaction_get_multi_result>>();
|
619
|
+
auto f = barrier->get_future();
|
620
|
+
get_multi(
|
621
|
+
ids, mode, [barrier](std::exception_ptr err, std::optional<transaction_get_multi_result> res) {
|
622
|
+
if (err) {
|
623
|
+
return barrier->set_exception(std::move(err));
|
624
|
+
}
|
625
|
+
if (res) {
|
626
|
+
return barrier->set_value(std::move(*res));
|
627
|
+
}
|
628
|
+
return barrier->set_exception(std::make_exception_ptr(
|
629
|
+
transaction_operation_failed(FAIL_OTHER, "get_multi: either error or result must be set")));
|
630
|
+
});
|
631
|
+
return f.get();
|
632
|
+
}
|
633
|
+
|
634
|
+
void
|
635
|
+
attempt_context_impl::get_multi(
|
636
|
+
const std::vector<couchbase::transactions::transaction_get_multi_spec>& specs,
|
637
|
+
const couchbase::transactions::transaction_get_multi_options& options,
|
638
|
+
std::function<void(error, std::optional<couchbase::transactions::transaction_get_multi_result>)>&&
|
639
|
+
cb)
|
640
|
+
{
|
641
|
+
std::vector<core::document_id> ids;
|
642
|
+
ids.reserve(specs.size());
|
643
|
+
for (const auto& spec : specs) {
|
644
|
+
ids.emplace_back(spec.bucket_, spec.scope_, spec.collection_, spec.id_);
|
645
|
+
}
|
646
|
+
|
647
|
+
get_multi(ids, from_public_api(options.mode_), [cb = std::move(cb)](const auto& err, auto res) {
|
648
|
+
if (err) {
|
649
|
+
try {
|
650
|
+
std::rethrow_exception(err);
|
651
|
+
} catch (const op_exception& e) {
|
652
|
+
return cb(core::impl::make_error(e.ctx()), {});
|
653
|
+
} catch (const transaction_operation_failed& e) {
|
654
|
+
return cb(core::impl::make_error(e), {});
|
655
|
+
} catch (...) {
|
656
|
+
return cb({ errc::transaction_op::generic }, {});
|
657
|
+
}
|
658
|
+
}
|
659
|
+
if (res) {
|
660
|
+
return cb({}, couchbase::transactions::transaction_get_multi_result{ res->content() });
|
661
|
+
}
|
662
|
+
return cb({ errc::transaction_op::generic }, {});
|
663
|
+
});
|
664
|
+
}
|
665
|
+
|
666
|
+
auto
|
667
|
+
attempt_context_impl::get_multi(
|
668
|
+
const std::vector<couchbase::transactions::transaction_get_multi_spec>& specs,
|
669
|
+
const couchbase::transactions::transaction_get_multi_options& options)
|
670
|
+
-> std::pair<error, std::optional<couchbase::transactions::transaction_get_multi_result>>
|
671
|
+
{
|
672
|
+
auto barrier = std::make_shared<std::promise<
|
673
|
+
std::pair<error, std::optional<couchbase::transactions::transaction_get_multi_result>>>>();
|
674
|
+
auto f = barrier->get_future();
|
675
|
+
get_multi(
|
676
|
+
specs,
|
677
|
+
options,
|
678
|
+
[barrier](error err, std::optional<couchbase::transactions::transaction_get_multi_result> res) {
|
679
|
+
return barrier->set_value(std::make_pair(std::move(err), std::move(res)));
|
680
|
+
});
|
681
|
+
return f.get();
|
682
|
+
}
|
683
|
+
|
684
|
+
void
|
685
|
+
attempt_context_impl::get_multi_replicas_from_preferred_server_group(
|
686
|
+
const std::vector<core::document_id>& ids,
|
687
|
+
transaction_get_multi_replicas_from_preferred_server_group_mode mode,
|
688
|
+
std::function<
|
689
|
+
void(std::exception_ptr,
|
690
|
+
std::optional<transaction_get_multi_replicas_from_preferred_server_group_result>)>&& cb)
|
691
|
+
{
|
692
|
+
if (op_list_.get_mode().is_query()) {
|
693
|
+
return cb(
|
694
|
+
std::make_exception_ptr(op_exception({ errc::transaction_op::feature_not_available },
|
695
|
+
"Get Multi Replica is not supported in Query Mode",
|
696
|
+
FEATURE_NOT_AVAILABLE_EXCEPTION)),
|
697
|
+
{});
|
698
|
+
}
|
699
|
+
|
700
|
+
cache_error_async(cb, [self = shared_from_this(), ids, mode, cb]() mutable {
|
701
|
+
self->check_if_done(cb);
|
702
|
+
const auto manager = std::make_shared<get_multi_orchestrator>(self, ids);
|
703
|
+
manager->get_multi_replicas_from_preferred_server_group(
|
704
|
+
mode,
|
705
|
+
[self, cb = std::move(cb)](
|
706
|
+
std::exception_ptr err,
|
707
|
+
std::optional<transaction_get_multi_replicas_from_preferred_server_group_result>
|
708
|
+
res) mutable {
|
709
|
+
if (err) {
|
710
|
+
self->op_completed_with_error(std::move(cb), std::move(err));
|
711
|
+
return;
|
712
|
+
}
|
713
|
+
self->op_completed_with_callback(std::move(cb), std::move(res));
|
714
|
+
});
|
715
|
+
});
|
716
|
+
}
|
717
|
+
|
718
|
+
auto
|
719
|
+
attempt_context_impl::get_multi_replicas_from_preferred_server_group(
|
720
|
+
const std::vector<core::document_id>& ids,
|
721
|
+
transaction_get_multi_replicas_from_preferred_server_group_mode mode)
|
722
|
+
-> transaction_get_multi_replicas_from_preferred_server_group_result
|
723
|
+
{
|
724
|
+
auto barrier = std::make_shared<
|
725
|
+
std::promise<transaction_get_multi_replicas_from_preferred_server_group_result>>();
|
726
|
+
auto f = barrier->get_future();
|
727
|
+
get_multi_replicas_from_preferred_server_group(
|
728
|
+
ids,
|
729
|
+
mode,
|
730
|
+
[barrier](
|
731
|
+
std::exception_ptr err,
|
732
|
+
std::optional<transaction_get_multi_replicas_from_preferred_server_group_result> res) {
|
733
|
+
if (err) {
|
734
|
+
return barrier->set_exception(std::move(err));
|
735
|
+
}
|
736
|
+
if (res) {
|
737
|
+
return barrier->set_value(std::move(*res));
|
738
|
+
}
|
739
|
+
return barrier->set_exception(std::make_exception_ptr(
|
740
|
+
transaction_operation_failed(FAIL_OTHER, "get_multi: either error or result must be set")));
|
741
|
+
});
|
742
|
+
return f.get();
|
743
|
+
}
|
744
|
+
|
745
|
+
void
|
746
|
+
attempt_context_impl::get_multi_replicas_from_preferred_server_group(
|
747
|
+
const std::vector<
|
748
|
+
couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_spec>&
|
749
|
+
specs,
|
750
|
+
const couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_options&
|
751
|
+
options,
|
752
|
+
std::function<
|
753
|
+
void(error,
|
754
|
+
std::optional<couchbase::transactions::
|
755
|
+
transaction_get_multi_replicas_from_preferred_server_group_result>)>&& cb)
|
756
|
+
{
|
757
|
+
std::vector<core::document_id> ids;
|
758
|
+
ids.reserve(specs.size());
|
759
|
+
for (const auto& spec : specs) {
|
760
|
+
ids.emplace_back(spec.bucket_, spec.scope_, spec.collection_, spec.id_);
|
761
|
+
}
|
762
|
+
|
763
|
+
get_multi_replicas_from_preferred_server_group(
|
764
|
+
ids, from_public_api(options.mode_), [cb = std::move(cb)](const auto& err, auto res) {
|
765
|
+
if (err) {
|
766
|
+
try {
|
767
|
+
std::rethrow_exception(err);
|
768
|
+
} catch (const op_exception& e) {
|
769
|
+
return cb(core::impl::make_error(e.ctx()), {});
|
770
|
+
} catch (const transaction_operation_failed& e) {
|
771
|
+
return cb(core::impl::make_error(e), {});
|
772
|
+
} catch (...) {
|
773
|
+
return cb({ errc::transaction_op::generic }, {});
|
774
|
+
}
|
775
|
+
}
|
776
|
+
if (res) {
|
777
|
+
return cb(
|
778
|
+
{},
|
779
|
+
couchbase::transactions::
|
780
|
+
transaction_get_multi_replicas_from_preferred_server_group_result{ res->content() });
|
781
|
+
}
|
782
|
+
return cb({ errc::transaction_op::generic }, {});
|
783
|
+
});
|
784
|
+
}
|
785
|
+
|
786
|
+
auto
|
787
|
+
attempt_context_impl::get_multi_replicas_from_preferred_server_group(
|
788
|
+
const std::vector<
|
789
|
+
couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_spec>&
|
790
|
+
specs,
|
791
|
+
const couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_options&
|
792
|
+
options)
|
793
|
+
-> std::pair<
|
794
|
+
error,
|
795
|
+
std::optional<
|
796
|
+
couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_result>>
|
797
|
+
{
|
798
|
+
auto barrier = std::make_shared<std::promise<std::pair<
|
799
|
+
error,
|
800
|
+
std::optional<couchbase::transactions::
|
801
|
+
transaction_get_multi_replicas_from_preferred_server_group_result>>>>();
|
802
|
+
auto f = barrier->get_future();
|
803
|
+
get_multi_replicas_from_preferred_server_group(
|
804
|
+
specs,
|
805
|
+
options,
|
806
|
+
[barrier](
|
807
|
+
error err,
|
808
|
+
std::optional<
|
809
|
+
couchbase::transactions::transaction_get_multi_replicas_from_preferred_server_group_result>
|
810
|
+
res) {
|
811
|
+
return barrier->set_value(std::make_pair(std::move(err), std::move(res)));
|
812
|
+
});
|
813
|
+
return f.get();
|
814
|
+
}
|
815
|
+
|
523
816
|
auto
|
524
817
|
attempt_context_impl::create_document_metadata(
|
525
818
|
const std::string& operation_type,
|
@@ -576,8 +869,8 @@ attempt_context_impl::create_document_metadata(
|
|
576
869
|
}),
|
577
870
|
});
|
578
871
|
txn["fc"] = {
|
579
|
-
{ to_string(forward_compat_stage::
|
580
|
-
{ to_string(forward_compat_stage::
|
872
|
+
{ to_string(forward_compat_stage::WRITE_WRITE_CONFLICT_INSERTING), fc_check },
|
873
|
+
{ to_string(forward_compat_stage::WRITE_WRITE_CONFLICT_INSERTING_GET), fc_check },
|
581
874
|
{ to_string(forward_compat_stage::GETS), fc_check },
|
582
875
|
{ to_string(forward_compat_stage::CLEANUP_ENTRY), fc_check },
|
583
876
|
};
|
@@ -637,7 +930,7 @@ attempt_context_impl::replace(const transaction_get_result& document,
|
|
637
930
|
|
638
931
|
self->check_and_handle_blocking_transactions(
|
639
932
|
document,
|
640
|
-
forward_compat_stage::
|
933
|
+
forward_compat_stage::WRITE_WRITE_CONFLICT_REPLACING,
|
641
934
|
[self,
|
642
935
|
existing_sm,
|
643
936
|
document,
|
@@ -670,7 +963,7 @@ attempt_context_impl::replace(const transaction_get_result& document,
|
|
670
963
|
self, "found existing INSERT of {} while replacing", document);
|
671
964
|
self->create_staged_insert(document.id(),
|
672
965
|
std::move(content),
|
673
|
-
|
966
|
+
document.cas().value(),
|
674
967
|
exp_delay(std::chrono::milliseconds(5),
|
675
968
|
std::chrono::milliseconds(300),
|
676
969
|
self->overall()->config().timeout),
|
@@ -678,7 +971,13 @@ attempt_context_impl::replace(const transaction_get_result& document,
|
|
678
971
|
std::move(cb));
|
679
972
|
return;
|
680
973
|
}
|
681
|
-
self->create_staged_replace(document
|
974
|
+
self->create_staged_replace(document.id(),
|
975
|
+
std::move(content),
|
976
|
+
document.content().flags,
|
977
|
+
document.cas(),
|
978
|
+
op_id,
|
979
|
+
document.metadata(),
|
980
|
+
std::move(cb));
|
682
981
|
});
|
683
982
|
});
|
684
983
|
} catch (const client_error& e) {
|
@@ -701,6 +1000,9 @@ template<typename Response>
|
|
701
1000
|
auto
|
702
1001
|
external_exception_from_response(const Response& resp) -> external_exception
|
703
1002
|
{
|
1003
|
+
if (resp.ctx.ec() == errc::key_value::document_irretrievable) {
|
1004
|
+
return DOCUMENT_UNRETRIEVABLE_EXCEPTION;
|
1005
|
+
}
|
704
1006
|
if (const auto error_index = resp.ctx.first_error_index(); error_index) {
|
705
1007
|
const auto status = resp.fields.at(error_index.value()).status;
|
706
1008
|
const auto path = resp.fields.at(error_index.value()).path;
|
@@ -714,20 +1016,22 @@ external_exception_from_response(const Response& resp) -> external_exception
|
|
714
1016
|
|
715
1017
|
template<typename Handler>
|
716
1018
|
void
|
717
|
-
attempt_context_impl::create_staged_replace(
|
718
|
-
|
719
|
-
|
720
|
-
|
1019
|
+
attempt_context_impl::create_staged_replace(
|
1020
|
+
const document_id& id,
|
1021
|
+
codec::encoded_value content,
|
1022
|
+
std::uint32_t original_flags,
|
1023
|
+
const couchbase::cas& cas,
|
1024
|
+
const std::string& op_id,
|
1025
|
+
const std::optional<document_metadata>& document_metadata,
|
1026
|
+
Handler&& cb)
|
721
1027
|
{
|
722
|
-
|
1028
|
+
operations::mutate_in_request req{ id };
|
723
1029
|
const bool binary =
|
724
1030
|
codec::codec_flags::has_common_flags(content.flags, codec::codec_flags::binary_common_flags);
|
725
|
-
auto txn = create_document_metadata("replace", op_id,
|
1031
|
+
auto txn = create_document_metadata("replace", op_id, document_metadata, content.flags);
|
726
1032
|
req.specs =
|
727
1033
|
mutate_in_specs{
|
728
|
-
mutate_in_specs::upsert_raw("txn",
|
729
|
-
.xattr()
|
730
|
-
.create_path(),
|
1034
|
+
mutate_in_specs::upsert_raw("txn", utils::to_binary(jsonify(txn))).xattr().create_path(),
|
731
1035
|
mutate_in_specs::upsert_raw(binary ? "txn.op.bin" : "txn.op.stgd", content.data)
|
732
1036
|
.xattr()
|
733
1037
|
.binary(binary),
|
@@ -737,8 +1041,8 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
737
1041
|
}
|
738
1042
|
.specs();
|
739
1043
|
req.durability_level = overall()->config().level;
|
740
|
-
req.cas =
|
741
|
-
req.flags =
|
1044
|
+
req.cas = cas;
|
1045
|
+
req.flags = original_flags;
|
742
1046
|
req.access_deleted = true;
|
743
1047
|
auto error_handler = [self = shared_from_this()](error_class ec,
|
744
1048
|
external_exception cause,
|
@@ -759,25 +1063,26 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
759
1063
|
return self->op_completed_with_error(std::forward<Handler>(cb), err);
|
760
1064
|
}
|
761
1065
|
};
|
762
|
-
auto ec =
|
763
|
-
|
764
|
-
|
765
|
-
});
|
1066
|
+
auto ec = wait_for_hook([self = shared_from_this(), key = id.key()](auto handler) mutable {
|
1067
|
+
return self->hooks_.before_staged_replace(self, key, std::move(handler));
|
1068
|
+
});
|
766
1069
|
if (ec) {
|
767
1070
|
return error_handler(
|
768
1071
|
*ec, UNKNOWN, "before_staged_replace hook raised error", std::forward<Handler>(cb));
|
769
1072
|
}
|
770
1073
|
CB_ATTEMPT_CTX_LOG_TRACE(this,
|
771
1074
|
"about to replace doc {} with cas {} in txn {}",
|
772
|
-
|
773
|
-
|
1075
|
+
id,
|
1076
|
+
cas.value(),
|
774
1077
|
overall()->transaction_id());
|
775
1078
|
overall()->cluster_ref().execute(
|
776
1079
|
req,
|
777
1080
|
[self = shared_from_this(),
|
778
|
-
|
779
|
-
|
1081
|
+
op_id,
|
1082
|
+
id,
|
1083
|
+
document_metadata,
|
780
1084
|
content = std::move(content),
|
1085
|
+
original_flags,
|
781
1086
|
cb = std::forward<Handler>(cb),
|
782
1087
|
error_handler = std::move(error_handler)](core::operations::mutate_in_response resp) mutable {
|
783
1088
|
if (auto ec2 = error_class_from_response(resp); ec2) {
|
@@ -789,11 +1094,13 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
789
1094
|
}
|
790
1095
|
return self->hooks_.after_staged_replace_complete(
|
791
1096
|
self,
|
792
|
-
|
1097
|
+
id.key(),
|
793
1098
|
[self,
|
794
|
-
|
795
|
-
|
1099
|
+
op_id,
|
1100
|
+
id,
|
1101
|
+
document_metadata,
|
796
1102
|
content = std::move(content),
|
1103
|
+
original_flags,
|
797
1104
|
error_handler = std::move(error_handler),
|
798
1105
|
cb = std::forward<Handler>(cb),
|
799
1106
|
resp = std::move(resp)](auto ec) mutable {
|
@@ -808,23 +1115,23 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
808
1115
|
std::optional<codec::encoded_value> staged_content_binary{};
|
809
1116
|
if (codec::codec_flags::has_common_flags(content.flags,
|
810
1117
|
codec::codec_flags::json_common_flags)) {
|
811
|
-
staged_content_json =
|
1118
|
+
staged_content_json = content;
|
812
1119
|
} else if (codec::codec_flags::has_common_flags(
|
813
1120
|
content.flags, codec::codec_flags::binary_common_flags)) {
|
814
|
-
staged_content_binary =
|
1121
|
+
staged_content_binary = content;
|
815
1122
|
}
|
816
1123
|
transaction_get_result out{
|
817
|
-
|
818
|
-
|
1124
|
+
id,
|
1125
|
+
content,
|
819
1126
|
resp.cas.value(),
|
820
1127
|
transaction_links{
|
821
1128
|
self->atr_id_->key(),
|
822
|
-
|
823
|
-
|
824
|
-
|
1129
|
+
id.bucket(),
|
1130
|
+
id.scope(),
|
1131
|
+
id.collection(),
|
825
1132
|
self->overall()->transaction_id(),
|
826
1133
|
self->id(),
|
827
|
-
|
1134
|
+
op_id,
|
828
1135
|
std::move(staged_content_json),
|
829
1136
|
std::move(staged_content_binary),
|
830
1137
|
std::nullopt,
|
@@ -835,25 +1142,50 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
835
1142
|
std::nullopt,
|
836
1143
|
false,
|
837
1144
|
},
|
838
|
-
|
1145
|
+
document_metadata,
|
839
1146
|
};
|
840
1147
|
|
841
1148
|
CB_ATTEMPT_CTX_LOG_TRACE(self, "replace staged content, result {}", out);
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
1149
|
+
|
1150
|
+
self->supports_replace_body_with_xattr(
|
1151
|
+
id.bucket(),
|
1152
|
+
[self,
|
1153
|
+
out = std::move(out),
|
1154
|
+
error_handler = std::move(error_handler),
|
1155
|
+
original_flags,
|
1156
|
+
cb = std::forward<Handler>(cb)](auto ec, bool supports) mutable {
|
1157
|
+
if (ec) {
|
1158
|
+
return error_handler(
|
1159
|
+
FAIL_OTHER,
|
1160
|
+
UNKNOWN,
|
1161
|
+
"failed to check whether replace_body_with_xattr is supported: " + ec.message(),
|
1162
|
+
std::forward<Handler>(cb));
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
auto [staged_content, staged_flags] = out.links().staged_content_json_or_binary();
|
1166
|
+
|
1167
|
+
self->staged_mutations_->add(staged_mutation{
|
1168
|
+
staged_mutation_type::REPLACE,
|
1169
|
+
out.id(),
|
1170
|
+
out.cas(),
|
1171
|
+
supports ? std::nullopt
|
1172
|
+
: std::make_optional(staged_content), // We don't store the staged contents
|
1173
|
+
// if the cluster supports
|
1174
|
+
// replace_body_with_xattr
|
1175
|
+
staged_flags,
|
1176
|
+
original_flags,
|
1177
|
+
out.metadata(),
|
1178
|
+
});
|
1179
|
+
return self->op_completed_with_callback(std::forward<Handler>(cb),
|
1180
|
+
std::optional(out));
|
1181
|
+
});
|
850
1182
|
});
|
851
1183
|
});
|
852
1184
|
}
|
853
1185
|
|
854
1186
|
auto
|
855
|
-
attempt_context_impl::replace(const transaction_get_result& document,
|
856
|
-
|
1187
|
+
attempt_context_impl::replace(const transaction_get_result& document, codec::encoded_value content)
|
1188
|
+
-> transaction_get_result
|
857
1189
|
{
|
858
1190
|
auto barrier = std::make_shared<std::promise<transaction_get_result>>();
|
859
1191
|
auto f = barrier->get_future();
|
@@ -920,8 +1252,8 @@ attempt_context_impl::insert_raw(const collection& coll,
|
|
920
1252
|
}
|
921
1253
|
|
922
1254
|
auto
|
923
|
-
attempt_context_impl::insert(const core::document_id& id,
|
924
|
-
|
1255
|
+
attempt_context_impl::insert(const core::document_id& id, codec::encoded_value content)
|
1256
|
+
-> transaction_get_result
|
925
1257
|
{
|
926
1258
|
auto barrier = std::make_shared<std::promise<transaction_get_result>>();
|
927
1259
|
auto f = barrier->get_future();
|
@@ -981,8 +1313,13 @@ attempt_context_impl::insert(const core::document_id& id,
|
|
981
1313
|
}
|
982
1314
|
if (existing_sm != nullptr && existing_sm->type() == staged_mutation_type::REMOVE) {
|
983
1315
|
CB_ATTEMPT_CTX_LOG_DEBUG(self, "found existing remove of {} while inserting", id);
|
984
|
-
return self->create_staged_replace(
|
985
|
-
|
1316
|
+
return self->create_staged_replace(existing_sm->id(),
|
1317
|
+
std::move(content),
|
1318
|
+
existing_sm->current_user_flags(),
|
1319
|
+
existing_sm->cas(),
|
1320
|
+
op_id,
|
1321
|
+
existing_sm->doc_metadata(),
|
1322
|
+
std::move(cb));
|
986
1323
|
}
|
987
1324
|
const std::uint64_t cas = 0;
|
988
1325
|
self->create_staged_insert(id,
|
@@ -1076,8 +1413,8 @@ attempt_context_impl::check_atr_entry_for_blocking_document(const transaction_ge
|
|
1076
1413
|
return e.attempt_id() == doc.links().staged_attempt_id();
|
1077
1414
|
});
|
1078
1415
|
if (it != entries.end()) {
|
1079
|
-
auto fwd_err = check_forward_compat(
|
1080
|
-
|
1416
|
+
auto fwd_err = check_forward_compat(
|
1417
|
+
forward_compat_stage::WRITE_WRITE_CONFLICT_READING_ATR, it->forward_compat());
|
1081
1418
|
if (fwd_err) {
|
1082
1419
|
return cb(fwd_err);
|
1083
1420
|
}
|
@@ -1168,7 +1505,7 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
1168
1505
|
}
|
1169
1506
|
return self->check_and_handle_blocking_transactions(
|
1170
1507
|
document,
|
1171
|
-
forward_compat_stage::
|
1508
|
+
forward_compat_stage::WRITE_WRITE_CONFLICT_REMOVING,
|
1172
1509
|
[self, document, cb = std::move(cb), op_id, error_handler = std::move(error_handler)](
|
1173
1510
|
std::optional<transaction_operation_failed> err1) mutable {
|
1174
1511
|
if (err1) {
|
@@ -1252,11 +1589,16 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
1252
1589
|
document.id(),
|
1253
1590
|
resp.cas.value(),
|
1254
1591
|
resp.ctx.ec().message());
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1592
|
+
|
1593
|
+
self->staged_mutations_->add(staged_mutation{
|
1594
|
+
staged_mutation_type::REMOVE,
|
1595
|
+
document.id(),
|
1596
|
+
resp.cas,
|
1597
|
+
{},
|
1598
|
+
document.content().flags,
|
1599
|
+
document.content().flags,
|
1600
|
+
document.metadata(),
|
1601
|
+
});
|
1260
1602
|
return self->op_completed_with_callback(cb);
|
1261
1603
|
});
|
1262
1604
|
});
|
@@ -1413,11 +1755,11 @@ attempt_context_impl::query_begin_work(const std::optional<std::string>& query_c
|
|
1413
1755
|
if (!staged_mutations_->empty()) {
|
1414
1756
|
staged_mutations_->iterate([&mutations](staged_mutation& mut) {
|
1415
1757
|
mutations.push_back(tao::json::value{
|
1416
|
-
{ "scp", mut.
|
1417
|
-
{ "coll", mut.
|
1418
|
-
{ "bkt", mut.
|
1419
|
-
{ "id", mut.
|
1420
|
-
{ "cas", std::to_string(mut.
|
1758
|
+
{ "scp", mut.id().scope() },
|
1759
|
+
{ "coll", mut.id().collection() },
|
1760
|
+
{ "bkt", mut.id().bucket() },
|
1761
|
+
{ "id", mut.id().key() },
|
1762
|
+
{ "cas", std::to_string(mut.cas().value()) },
|
1421
1763
|
{ "type", mut.type_as_string() },
|
1422
1764
|
});
|
1423
1765
|
});
|
@@ -1809,8 +2151,8 @@ attempt_context_impl::do_public_query(
|
|
1809
2151
|
}
|
1810
2152
|
|
1811
2153
|
auto
|
1812
|
-
make_params(const core::document_id& id,
|
1813
|
-
|
2154
|
+
make_params(const core::document_id& id, std::optional<codec::encoded_value> content)
|
2155
|
+
-> std::vector<core::json_string>
|
1814
2156
|
{
|
1815
2157
|
if (content && !codec::codec_flags::has_common_flags(content->flags,
|
1816
2158
|
codec::codec_flags::json_common_flags)) {
|
@@ -2939,20 +3281,32 @@ attempt_context_impl::do_get(const core::document_id& id,
|
|
2939
3281
|
{
|
2940
3282
|
try {
|
2941
3283
|
if (check_expiry_pre_commit(STAGE_GET, id.key())) {
|
2942
|
-
return cb(FAIL_EXPIRY, "expired in do_get", std::nullopt);
|
3284
|
+
return cb(FAIL_EXPIRY, std::nullopt, "expired in do_get", std::nullopt);
|
2943
3285
|
}
|
2944
3286
|
|
3287
|
+
// Check if we already have a staged insert/replace for this document AND we have the content
|
3288
|
+
// for it (i.e. the cluster does not support replace body_with_xattr)
|
2945
3289
|
if (const staged_mutation* own_write = check_for_own_write(id); own_write != nullptr) {
|
2946
|
-
|
2947
|
-
|
2948
|
-
|
2949
|
-
|
3290
|
+
const auto own_write_content = own_write->staged_content();
|
3291
|
+
if (own_write_content.has_value()) {
|
3292
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "found own-write of mutated doc {}", id);
|
3293
|
+
return cb(std::nullopt,
|
3294
|
+
std::nullopt,
|
3295
|
+
std::nullopt,
|
3296
|
+
transaction_get_result{
|
3297
|
+
own_write->id(),
|
3298
|
+
codec::encoded_value{ own_write_content.value(), own_write->staged_flags() },
|
3299
|
+
own_write->cas().value(),
|
3300
|
+
{},
|
3301
|
+
{},
|
3302
|
+
});
|
3303
|
+
}
|
2950
3304
|
}
|
2951
3305
|
if (const staged_mutation* own_remove = staged_mutations_->find_remove(id);
|
2952
3306
|
own_remove != nullptr) {
|
2953
3307
|
auto msg = fmt::format("found own-write of removed doc {}", id);
|
2954
3308
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "{}", msg);
|
2955
|
-
return cb(FAIL_DOC_NOT_FOUND, msg, std::nullopt);
|
3309
|
+
return cb(FAIL_DOC_NOT_FOUND, std::nullopt, msg, std::nullopt);
|
2956
3310
|
}
|
2957
3311
|
|
2958
3312
|
return hooks_.before_doc_get(
|
@@ -2964,7 +3318,7 @@ attempt_context_impl::do_get(const core::document_id& id,
|
|
2964
3318
|
resolving_missing_atr_entry = std::move(resolving_missing_atr_entry),
|
2965
3319
|
cb = std::forward<Handler>(cb)](auto ec) mutable {
|
2966
3320
|
if (ec) {
|
2967
|
-
return cb(ec, "before_doc_get hook raised error", std::nullopt);
|
3321
|
+
return cb(ec, std::nullopt, "before_doc_get hook raised error", std::nullopt);
|
2968
3322
|
}
|
2969
3323
|
|
2970
3324
|
return self->get_doc(
|
@@ -2975,115 +3329,133 @@ attempt_context_impl::do_get(const core::document_id& id,
|
|
2975
3329
|
allow_replica,
|
2976
3330
|
resolving_missing_atr_entry = std::move(resolving_missing_atr_entry),
|
2977
3331
|
cb = std::move(cb)](std::optional<error_class> ec,
|
3332
|
+
std::optional<external_exception> cause,
|
2978
3333
|
const std::optional<std::string>& err_message,
|
2979
3334
|
std::optional<transaction_get_result> doc) mutable {
|
2980
3335
|
if (!ec && !doc) {
|
2981
3336
|
// it just isn't there.
|
2982
|
-
return cb(std::nullopt, std::nullopt, std::nullopt);
|
3337
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, std::nullopt);
|
2983
3338
|
}
|
2984
|
-
|
2985
|
-
|
3339
|
+
|
3340
|
+
if (ec) {
|
3341
|
+
return cb(ec, cause, err_message, std::nullopt);
|
3342
|
+
}
|
3343
|
+
|
3344
|
+
if (!doc->links().is_document_in_transaction()) {
|
3345
|
+
if (doc->links().is_deleted()) {
|
2986
3346
|
CB_ATTEMPT_CTX_LOG_DEBUG(self,
|
2987
|
-
"doc
|
2988
|
-
|
2989
|
-
|
2990
|
-
|
2991
|
-
|
2992
|
-
|
2993
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(self, "doc is in lost pending transaction");
|
2994
|
-
|
2995
|
-
if (doc->links().is_document_being_inserted()) {
|
2996
|
-
// this document is being inserted, so should not be visible
|
2997
|
-
// yet
|
2998
|
-
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2999
|
-
}
|
3347
|
+
"doc not in txn, and is_deleted, so not returning it.");
|
3348
|
+
// doc has been deleted, not in txn, so don't return it
|
3349
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, std::nullopt);
|
3350
|
+
}
|
3351
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, doc);
|
3352
|
+
}
|
3000
3353
|
|
3001
|
-
|
3002
|
-
|
3354
|
+
if (doc->links().staged_attempt_id() == self->id()) {
|
3355
|
+
// This is a RYOW, and we can optimise here by not looking up the document's ATR.
|
3003
3356
|
|
3004
|
-
|
3005
|
-
|
3006
|
-
|
3007
|
-
|
3008
|
-
|
3009
|
-
|
3010
|
-
|
3011
|
-
|
3012
|
-
|
3013
|
-
|
3014
|
-
|
3015
|
-
|
3016
|
-
|
3017
|
-
|
3018
|
-
|
3019
|
-
|
3020
|
-
|
3357
|
+
if (doc->links().is_document_being_removed()) {
|
3358
|
+
// The document is being removed by this attempt, return empty.
|
3359
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, std::nullopt);
|
3360
|
+
}
|
3361
|
+
|
3362
|
+
// Return the post-transaction version.
|
3363
|
+
return cb(std::nullopt,
|
3364
|
+
std::nullopt,
|
3365
|
+
std::nullopt,
|
3366
|
+
transaction_get_result::create_from(
|
3367
|
+
*doc, doc->links().staged_content_json_or_binary()));
|
3368
|
+
}
|
3369
|
+
|
3370
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(self,
|
3371
|
+
"doc {} in transaction, resolving_missing_atr_entry={}",
|
3372
|
+
*doc,
|
3373
|
+
resolving_missing_atr_entry.value_or("-"));
|
3374
|
+
|
3375
|
+
if (resolving_missing_atr_entry.has_value() &&
|
3376
|
+
resolving_missing_atr_entry.value() == doc->links().staged_attempt_id()) {
|
3377
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(self, "doc is in lost pending transaction");
|
3378
|
+
|
3379
|
+
if (doc->links().is_document_being_inserted()) {
|
3380
|
+
// this document is being inserted, so should not be visible
|
3381
|
+
// yet
|
3382
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, std::nullopt);
|
3383
|
+
}
|
3384
|
+
|
3385
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, doc);
|
3386
|
+
}
|
3387
|
+
|
3388
|
+
const core::document_id doc_atr_id{ doc->links().atr_bucket_name().value(),
|
3389
|
+
doc->links().atr_scope_name().value(),
|
3390
|
+
doc->links().atr_collection_name().value(),
|
3391
|
+
doc->links().atr_id().value() };
|
3392
|
+
active_transaction_record::get_atr(
|
3393
|
+
self->cluster_ref(),
|
3394
|
+
doc_atr_id,
|
3395
|
+
[self, id, allow_replica, doc, cb = std::move(cb)](
|
3396
|
+
std::error_code ec2, std::optional<active_transaction_record> atr) mutable {
|
3397
|
+
if (!ec2 && atr) {
|
3398
|
+
const active_transaction_record& atr_doc = atr.value();
|
3399
|
+
std::optional<atr_entry> entry;
|
3400
|
+
for (const auto& e : atr_doc.entries()) {
|
3401
|
+
if (doc->links().staged_attempt_id().value() == e.attempt_id()) {
|
3402
|
+
entry.emplace(e);
|
3403
|
+
break;
|
3404
|
+
}
|
3405
|
+
}
|
3406
|
+
bool ignore_doc = false;
|
3407
|
+
auto content = doc->content();
|
3408
|
+
if (entry) {
|
3409
|
+
if (doc->links().staged_attempt_id() && entry->attempt_id() == self->id()) {
|
3410
|
+
// Attempt is reading its own writes
|
3411
|
+
// This is here as backup, it should be returned
|
3412
|
+
// from the in-memory cache instead
|
3413
|
+
content = doc->links().staged_content_json_or_binary();
|
3414
|
+
} else {
|
3415
|
+
auto err = check_forward_compat(forward_compat_stage::GETS_READING_ATR,
|
3416
|
+
entry->forward_compat());
|
3417
|
+
if (err) {
|
3418
|
+
return cb(FAIL_OTHER, err->cause(), err->what(), std::nullopt);
|
3021
3419
|
}
|
3022
|
-
|
3023
|
-
|
3024
|
-
|
3025
|
-
|
3026
|
-
|
3027
|
-
|
3028
|
-
|
3029
|
-
content = doc->links().staged_content_json_or_binary();
|
3030
|
-
} else {
|
3031
|
-
auto err = check_forward_compat(forward_compat_stage::GETS_READING_ATR,
|
3032
|
-
entry->forward_compat());
|
3033
|
-
if (err) {
|
3034
|
-
return cb(FAIL_OTHER, err->what(), std::nullopt);
|
3420
|
+
switch (entry->state()) {
|
3421
|
+
case attempt_state::COMPLETED:
|
3422
|
+
case attempt_state::COMMITTED:
|
3423
|
+
if (doc->links().is_document_being_removed()) {
|
3424
|
+
ignore_doc = true;
|
3425
|
+
} else {
|
3426
|
+
content = doc->links().staged_content_json_or_binary();
|
3035
3427
|
}
|
3036
|
-
|
3037
|
-
|
3038
|
-
|
3039
|
-
|
3040
|
-
|
3041
|
-
|
3042
|
-
content = doc->links().staged_content_json_or_binary();
|
3043
|
-
}
|
3044
|
-
break;
|
3045
|
-
default:
|
3046
|
-
if (doc->links().is_document_being_inserted()) {
|
3047
|
-
// This document is being inserted, so should
|
3048
|
-
// not be visible yet
|
3049
|
-
ignore_doc = true;
|
3050
|
-
}
|
3051
|
-
break;
|
3428
|
+
break;
|
3429
|
+
default:
|
3430
|
+
if (doc->links().is_document_being_inserted()) {
|
3431
|
+
// This document is being inserted, so should
|
3432
|
+
// not be visible yet
|
3433
|
+
ignore_doc = true;
|
3052
3434
|
}
|
3053
|
-
|
3054
|
-
} else {
|
3055
|
-
// failed to get the ATR entry
|
3056
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(self,
|
3057
|
-
"could not get ATR entry, checking again with {}",
|
3058
|
-
doc->links().staged_attempt_id().value_or("-"));
|
3059
|
-
return self->do_get(
|
3060
|
-
id, allow_replica, doc->links().staged_attempt_id(), cb);
|
3061
|
-
}
|
3062
|
-
if (ignore_doc) {
|
3063
|
-
return cb(std::nullopt, std::nullopt, std::nullopt);
|
3435
|
+
break;
|
3064
3436
|
}
|
3065
|
-
return cb(std::nullopt,
|
3066
|
-
std::nullopt,
|
3067
|
-
transaction_get_result::create_from(*doc, content));
|
3068
3437
|
}
|
3069
|
-
|
3438
|
+
} else {
|
3439
|
+
// failed to get the ATR entry
|
3070
3440
|
CB_ATTEMPT_CTX_LOG_DEBUG(self,
|
3071
|
-
"could not get ATR, checking again with {}",
|
3441
|
+
"could not get ATR entry, checking again with {}",
|
3072
3442
|
doc->links().staged_attempt_id().value_or("-"));
|
3073
3443
|
return self->do_get(id, allow_replica, doc->links().staged_attempt_id(), cb);
|
3074
|
-
}
|
3075
|
-
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3079
|
-
|
3080
|
-
|
3444
|
+
}
|
3445
|
+
if (ignore_doc) {
|
3446
|
+
return cb(std::nullopt, std::nullopt, std::nullopt, std::nullopt);
|
3447
|
+
}
|
3448
|
+
return cb(std::nullopt,
|
3449
|
+
std::nullopt,
|
3450
|
+
std::nullopt,
|
3451
|
+
transaction_get_result::create_from(*doc, content));
|
3081
3452
|
}
|
3082
|
-
|
3083
|
-
|
3084
|
-
|
3085
|
-
|
3086
|
-
|
3453
|
+
// failed to get the ATR
|
3454
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(self,
|
3455
|
+
"could not get ATR, checking again with {}",
|
3456
|
+
doc->links().staged_attempt_id().value_or("-"));
|
3457
|
+
return self->do_get(id, allow_replica, doc->links().staged_attempt_id(), cb);
|
3458
|
+
});
|
3087
3459
|
});
|
3088
3460
|
});
|
3089
3461
|
} catch (const transaction_operation_failed&) {
|
@@ -3108,12 +3480,17 @@ execute_lookup(attempt_context_impl* ctx, LookupInRequest& req, Callback&& cb)
|
|
3108
3480
|
CB_ATTEMPT_CTX_LOG_TRACE(ctx, "get_doc got error {} : {}", resp.ctx.ec().message(), *ec);
|
3109
3481
|
switch (*ec) {
|
3110
3482
|
case FAIL_PATH_NOT_FOUND:
|
3111
|
-
return cb(ec,
|
3483
|
+
return cb(ec,
|
3484
|
+
external_exception_from_response(resp),
|
3485
|
+
resp.ctx.ec().message(),
|
3486
|
+
transaction_get_result::create_from(resp));
|
3112
3487
|
default:
|
3113
|
-
return cb(
|
3488
|
+
return cb(
|
3489
|
+
ec, external_exception_from_response(resp), resp.ctx.ec().message(), std::nullopt);
|
3114
3490
|
}
|
3115
3491
|
} else {
|
3116
|
-
return cb(
|
3492
|
+
return cb(
|
3493
|
+
std::nullopt, std::nullopt, std::nullopt, transaction_get_result::create_from(resp));
|
3117
3494
|
}
|
3118
3495
|
});
|
3119
3496
|
}
|
@@ -3123,6 +3500,7 @@ void
|
|
3123
3500
|
attempt_context_impl::get_doc(const core::document_id& id,
|
3124
3501
|
bool allow_replica,
|
3125
3502
|
std::function<void(std::optional<error_class>,
|
3503
|
+
std::optional<external_exception>,
|
3126
3504
|
std::optional<std::string>,
|
3127
3505
|
std::optional<transaction_get_result>)>&& cb)
|
3128
3506
|
{
|
@@ -3145,7 +3523,7 @@ attempt_context_impl::get_doc(const core::document_id& id,
|
|
3145
3523
|
try {
|
3146
3524
|
if (allow_replica) {
|
3147
3525
|
core::operations::lookup_in_any_replica_request req{ id };
|
3148
|
-
req.read_preference = couchbase::read_preference::
|
3526
|
+
req.read_preference = couchbase::read_preference::selected_server_group_or_all_available;
|
3149
3527
|
req.specs = specs;
|
3150
3528
|
execute_lookup(this, req, cb);
|
3151
3529
|
} else {
|
@@ -3155,7 +3533,7 @@ attempt_context_impl::get_doc(const core::document_id& id,
|
|
3155
3533
|
execute_lookup(this, req, cb);
|
3156
3534
|
}
|
3157
3535
|
} catch (const std::exception& e) {
|
3158
|
-
return cb(FAIL_OTHER, e.what(), std::nullopt);
|
3536
|
+
return cb(FAIL_OTHER, std::nullopt, e.what(), std::nullopt);
|
3159
3537
|
}
|
3160
3538
|
}
|
3161
3539
|
|
@@ -3252,6 +3630,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
3252
3630
|
false,
|
3253
3631
|
[self, id, content, op_id, cb = std::forward<Handler>(cb), error_handler, delay](
|
3254
3632
|
std::optional<error_class> ec3,
|
3633
|
+
std::optional<external_exception> /* cause */,
|
3255
3634
|
std::optional<std::string> err_message,
|
3256
3635
|
std::optional<transaction_get_result> doc) mutable {
|
3257
3636
|
if (!ec3) {
|
@@ -3263,8 +3642,9 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
3263
3642
|
doc->links().is_document_in_transaction(),
|
3264
3643
|
doc->links().is_deleted());
|
3265
3644
|
|
3266
|
-
if (auto err = check_forward_compat(
|
3267
|
-
|
3645
|
+
if (auto err = check_forward_compat(
|
3646
|
+
forward_compat_stage::WRITE_WRITE_CONFLICT_INSERTING_GET,
|
3647
|
+
doc->links().forward_compat());
|
3268
3648
|
err) {
|
3269
3649
|
return self->op_completed_with_error(std::forward<Handler>(cb), *err);
|
3270
3650
|
}
|
@@ -3293,8 +3673,15 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
3293
3673
|
// this is us dealing with resolving an ambiguity. So, lets
|
3294
3674
|
// just update the staged_mutation with the correct cas and
|
3295
3675
|
// continue...
|
3296
|
-
self->staged_mutations_->add(
|
3297
|
-
|
3676
|
+
self->staged_mutations_->add(staged_mutation{
|
3677
|
+
staged_mutation_type::INSERT,
|
3678
|
+
doc->id(),
|
3679
|
+
doc->cas(),
|
3680
|
+
content.data,
|
3681
|
+
content.flags,
|
3682
|
+
doc->content().flags,
|
3683
|
+
doc->metadata(),
|
3684
|
+
});
|
3298
3685
|
return self->op_completed_with_callback(std::forward<Handler>(cb), doc);
|
3299
3686
|
}
|
3300
3687
|
return self->op_completed_with_error(
|
@@ -3313,7 +3700,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
3313
3700
|
}
|
3314
3701
|
self->check_and_handle_blocking_transactions(
|
3315
3702
|
*doc,
|
3316
|
-
forward_compat_stage::
|
3703
|
+
forward_compat_stage::WRITE_WRITE_CONFLICT_INSERTING,
|
3317
3704
|
[self, id, op_id, content, doc, cb = std::forward<Handler>(cb), delay](
|
3318
3705
|
std::optional<transaction_operation_failed> err) mutable {
|
3319
3706
|
if (err) {
|
@@ -3459,55 +3846,89 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
3459
3846
|
|
3460
3847
|
CB_ATTEMPT_CTX_LOG_DEBUG(
|
3461
3848
|
self, "inserted doc {} CAS={}, {}", id, resp.cas.value(), resp.ctx.ec().message());
|
3462
|
-
|
3463
|
-
|
3464
|
-
|
3465
|
-
|
3466
|
-
|
3467
|
-
|
3468
|
-
|
3469
|
-
|
3470
|
-
|
3471
|
-
|
3472
|
-
|
3473
|
-
|
3474
|
-
|
3475
|
-
|
3476
|
-
|
3477
|
-
|
3478
|
-
|
3479
|
-
|
3480
|
-
|
3481
|
-
|
3482
|
-
|
3483
|
-
|
3484
|
-
|
3485
|
-
|
3486
|
-
std::
|
3487
|
-
std::
|
3488
|
-
|
3489
|
-
|
3490
|
-
|
3491
|
-
|
3492
|
-
|
3493
|
-
|
3494
|
-
|
3495
|
-
|
3496
|
-
|
3497
|
-
|
3498
|
-
|
3499
|
-
|
3500
|
-
|
3501
|
-
|
3502
|
-
|
3503
|
-
|
3849
|
+
|
3850
|
+
self->supports_replace_body_with_xattr(
|
3851
|
+
id.bucket(),
|
3852
|
+
[self,
|
3853
|
+
id,
|
3854
|
+
content = std::move(content),
|
3855
|
+
cas,
|
3856
|
+
op_id,
|
3857
|
+
delay = std::forward<Delay>(delay),
|
3858
|
+
resp = std::move(resp),
|
3859
|
+
cb = std::forward<Handler>(cb)](auto ec, bool supports) mutable {
|
3860
|
+
if (ec) {
|
3861
|
+
return self->create_staged_insert_error_handler(
|
3862
|
+
id,
|
3863
|
+
std::move(content),
|
3864
|
+
cas,
|
3865
|
+
std::forward<Delay>(delay),
|
3866
|
+
op_id,
|
3867
|
+
std::forward<Handler>(cb),
|
3868
|
+
FAIL_OTHER,
|
3869
|
+
UNKNOWN,
|
3870
|
+
"failed to check whether replace_body_with_xattr is supported: " + ec.message());
|
3871
|
+
}
|
3872
|
+
|
3873
|
+
std::optional<codec::encoded_value> staged_content_json{};
|
3874
|
+
std::optional<codec::encoded_value> staged_content_binary{};
|
3875
|
+
if (codec::codec_flags::has_common_flags(content.flags,
|
3876
|
+
codec::codec_flags::json_common_flags)) {
|
3877
|
+
staged_content_json = content;
|
3878
|
+
} else if (codec::codec_flags::has_common_flags(
|
3879
|
+
content.flags, codec::codec_flags::binary_common_flags)) {
|
3880
|
+
staged_content_binary = content;
|
3881
|
+
}
|
3882
|
+
|
3883
|
+
transaction_get_result out{
|
3884
|
+
id,
|
3885
|
+
content,
|
3886
|
+
resp.cas.value(),
|
3887
|
+
transaction_links{
|
3888
|
+
self->atr_id_->key(),
|
3889
|
+
id.bucket(),
|
3890
|
+
id.scope(),
|
3891
|
+
id.collection(),
|
3892
|
+
self->overall()->transaction_id(),
|
3893
|
+
self->id(),
|
3894
|
+
op_id,
|
3895
|
+
std::move(staged_content_json),
|
3896
|
+
std::move(staged_content_binary),
|
3897
|
+
std::nullopt,
|
3898
|
+
std::nullopt,
|
3899
|
+
std::nullopt,
|
3900
|
+
std::nullopt,
|
3901
|
+
"insert",
|
3902
|
+
std::nullopt,
|
3903
|
+
true,
|
3904
|
+
},
|
3905
|
+
std::nullopt,
|
3906
|
+
};
|
3907
|
+
|
3908
|
+
auto [staged_content, staged_flags] = out.links().staged_content_json_or_binary();
|
3909
|
+
|
3910
|
+
self->staged_mutations_->add(staged_mutation{
|
3911
|
+
staged_mutation_type::INSERT,
|
3912
|
+
id,
|
3913
|
+
resp.cas,
|
3914
|
+
supports ? std::nullopt
|
3915
|
+
: std::make_optional(staged_content), // We don't store the staged contents
|
3916
|
+
// if the cluster supports
|
3917
|
+
// replace_body_with_xattr
|
3918
|
+
staged_flags,
|
3919
|
+
staged_flags,
|
3920
|
+
out.metadata(),
|
3921
|
+
});
|
3922
|
+
return self->op_completed_with_callback(std::forward<Handler>(cb),
|
3923
|
+
std::optional{ std::move(out) });
|
3924
|
+
});
|
3504
3925
|
});
|
3505
3926
|
});
|
3506
3927
|
}
|
3507
3928
|
|
3508
3929
|
void
|
3509
3930
|
attempt_context_impl::ensure_open_bucket(const std::string& bucket_name,
|
3510
|
-
std::function<void(std::error_code)>&& handler)
|
3931
|
+
std::function<void(std::error_code)>&& handler) const
|
3511
3932
|
{
|
3512
3933
|
if (bucket_name.empty()) {
|
3513
3934
|
CB_LOG_DEBUG("ensure_open_bucket called with empty bucket_name");
|
@@ -3518,6 +3939,29 @@ attempt_context_impl::ensure_open_bucket(const std::string& bucket_name,
|
|
3518
3939
|
});
|
3519
3940
|
}
|
3520
3941
|
|
3942
|
+
void
|
3943
|
+
attempt_context_impl::supports_replace_body_with_xattr(
|
3944
|
+
const std::string& bucket_name,
|
3945
|
+
std::function<void(std::error_code, bool)>&& handler) const
|
3946
|
+
{
|
3947
|
+
cluster_ref().with_bucket_configuration(
|
3948
|
+
bucket_name,
|
3949
|
+
[handler = std::move(handler)](std::error_code ec,
|
3950
|
+
const std::shared_ptr<topology::configuration>& config) {
|
3951
|
+
if (ec) {
|
3952
|
+
handler(ec, {});
|
3953
|
+
return;
|
3954
|
+
}
|
3955
|
+
|
3956
|
+
// We use subdoc_revive_document instead of subdoc_replace_body_with_xattr.
|
3957
|
+
// The version where subdoc_replace_body_with_xattr was released (7.0) contained a significant
|
3958
|
+
// bug.
|
3959
|
+
// (https://github.com/couchbaselabs/couchbase-transactions-specs/blob/master/transactions-stages.md#supportsreplacebodywithxattr)
|
3960
|
+
handler(
|
3961
|
+
{}, config->capabilities.has_bucket_capability(bucket_capability::subdoc_revive_document));
|
3962
|
+
});
|
3963
|
+
}
|
3964
|
+
|
3521
3965
|
void
|
3522
3966
|
attempt_context_impl::remove(couchbase::transactions::transaction_get_result doc,
|
3523
3967
|
couchbase::transactions::async_err_handler&& handler)
|
@@ -3729,4 +4173,10 @@ attempt_context_impl::atr_collection_name(const std::string& coll) const
|
|
3729
4173
|
{
|
3730
4174
|
overall()->atr_collection(coll);
|
3731
4175
|
}
|
4176
|
+
|
4177
|
+
auto
|
4178
|
+
attempt_context_impl::expiry_time() const -> std::chrono::steady_clock::time_point
|
4179
|
+
{
|
4180
|
+
return overall()->expiry_time();
|
4181
|
+
}
|
3732
4182
|
} // namespace couchbase::core::transactions
|