couchbase 4.2.11-rc.1 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/deps/couchbase-cxx-cache/mozilla-ca-bundle.crt +49 -2
- package/deps/couchbase-cxx-cache/mozilla-ca-bundle.sha256 +1 -1
- package/deps/couchbase-cxx-client/CMakeLists.txt +1 -0
- package/deps/couchbase-cxx-client/cmake/ThirdPartyDependencies.cmake +2 -0
- package/deps/couchbase-cxx-client/core/bucket.cxx +2 -2
- package/deps/couchbase-cxx-client/core/impl/cluster.cxx +51 -5
- package/deps/couchbase-cxx-client/core/impl/collection.cxx +224 -209
- package/deps/couchbase-cxx-client/core/impl/query_error_context.cxx +2 -2
- package/deps/couchbase-cxx-client/core/impl/query_index_manager.cxx +1 -0
- package/deps/couchbase-cxx-client/core/io/dns_client.cxx +4 -0
- package/deps/couchbase-cxx-client/core/io/dns_config.cxx +15 -4
- package/deps/couchbase-cxx-client/core/io/dns_config.hxx +1 -1
- package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +95 -53
- package/deps/couchbase-cxx-client/core/io/mcbp_session.hxx +1 -0
- package/deps/couchbase-cxx-client/core/mcbp/operation_queue.cxx +1 -0
- package/deps/couchbase-cxx-client/core/meta/features.hxx +5 -0
- package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +116 -105
- package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +116 -108
- package/deps/couchbase-cxx-client/core/operations/document_search.cxx +97 -81
- package/deps/couchbase-cxx-client/core/operations/document_search.hxx +5 -0
- package/deps/couchbase-cxx-client/core/range_scan_load_balancer.cxx +141 -0
- package/deps/couchbase-cxx-client/core/range_scan_load_balancer.hxx +64 -0
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +224 -336
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator.hxx +5 -6
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator_options.hxx +8 -5
- package/deps/couchbase-cxx-client/core/scan_result.hxx +1 -11
- package/deps/couchbase-cxx-client/core/transactions/atr_cleanup_entry.cxx +16 -7
- package/deps/couchbase-cxx-client/core/transactions/attempt_context_impl.cxx +578 -483
- package/deps/couchbase-cxx-client/core/transactions/attempt_context_testing_hooks.cxx +51 -50
- package/deps/couchbase-cxx-client/core/transactions/attempt_context_testing_hooks.hxx +4 -2
- package/deps/couchbase-cxx-client/core/transactions/cleanup_testing_hooks.cxx +6 -6
- package/deps/couchbase-cxx-client/core/transactions/cleanup_testing_hooks.hxx +3 -2
- package/deps/couchbase-cxx-client/core/transactions/internal/transactions_cleanup.hxx +2 -0
- package/deps/couchbase-cxx-client/core/transactions/internal/utils.hxx +5 -1
- package/deps/couchbase-cxx-client/core/transactions/staged_mutation.cxx +222 -179
- package/deps/couchbase-cxx-client/core/transactions/staged_mutation.hxx +23 -12
- package/deps/couchbase-cxx-client/core/transactions/transactions.cxx +61 -24
- package/deps/couchbase-cxx-client/core/transactions/transactions_cleanup.cxx +36 -16
- package/deps/couchbase-cxx-client/core/transactions/utils.cxx +9 -0
- package/deps/couchbase-cxx-client/core/transactions.hxx +40 -7
- package/deps/couchbase-cxx-client/couchbase/bucket.hxx +2 -2
- package/deps/couchbase-cxx-client/couchbase/cluster.hxx +20 -1
- package/deps/couchbase-cxx-client/couchbase/collection.hxx +1 -0
- package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +1 -1
- package/deps/couchbase-cxx-client/couchbase/error_context.hxx +1 -0
- package/deps/couchbase-cxx-client/couchbase/fork_event.hxx +39 -0
- package/deps/couchbase-cxx-client/couchbase/get_links_analytics_options.hxx +2 -2
- package/deps/couchbase-cxx-client/couchbase/scope.hxx +1 -1
- package/deps/couchbase-cxx-client/couchbase/search_options.hxx +2 -2
- package/deps/couchbase-cxx-client/couchbase/search_result.hxx +1 -1
- package/deps/couchbase-cxx-client/couchbase/subdocument_error_context.hxx +1 -0
- package/deps/couchbase-cxx-client/couchbase/transactions/transaction_options.hxx +1 -1
- package/deps/couchbase-cxx-client/couchbase-sdk-cxx-black-duck-manifest.yaml +1 -0
- package/dist/binding.d.ts +8 -0
- package/dist/bindingutilities.d.ts +6 -1
- package/dist/bindingutilities.js +15 -1
- package/dist/bucketmanager.d.ts +0 -12
- package/dist/cluster.d.ts +0 -2
- package/dist/cluster.js +0 -2
- package/dist/collection.d.ts +3 -3
- package/dist/collection.js +3 -1
- package/dist/querytypes.d.ts +0 -2
- package/dist/rangeScan.d.ts +0 -8
- package/dist/rangeScan.js +0 -8
- package/dist/scope.d.ts +0 -5
- package/dist/scope.js +0 -5
- package/dist/scopesearchindexmanager.d.ts +0 -2
- package/dist/scopesearchindexmanager.js +0 -2
- package/dist/searchexecutor.js +3 -1
- package/dist/searchtypes.d.ts +16 -6
- package/dist/searchtypes.js +2 -6
- package/dist/transactions.d.ts +23 -0
- package/dist/transactions.js +16 -10
- package/dist/vectorsearch.d.ts +8 -8
- package/dist/vectorsearch.js +7 -7
- package/package.json +7 -7
- package/src/instance.cpp +11 -1
- package/src/instance.hpp +1 -0
- package/src/jstocbpp_autogen.hpp +8 -1
- package/src/jstocbpp_transactions.hpp +40 -3
- package/src/transactions.cpp +12 -1
- package/tools/gen-bindings-json.py +0 -1
- package/deps/couchbase-cxx-client/core/scan_options.hxx +0 -44
@@ -117,49 +117,53 @@ attempt_context_impl::get(const core::document_id& id, Callback&& cb)
|
|
117
117
|
}
|
118
118
|
cache_error_async(cb, [&]() mutable {
|
119
119
|
check_if_done(cb);
|
120
|
-
do_get(
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
120
|
+
do_get(id,
|
121
|
+
std::nullopt,
|
122
|
+
[this, id, cb = std::move(cb)](
|
123
|
+
std::optional<error_class> ec, std::optional<std::string> err_message, std::optional<transaction_get_result> res) mutable {
|
124
|
+
auto handler = [this, id, err_message, res, cb = std::move(cb)](std::optional<error_class> ec) mutable {
|
125
|
+
if (ec) {
|
126
|
+
switch (*ec) {
|
127
|
+
case FAIL_EXPIRY:
|
128
|
+
return op_completed_with_error(
|
129
|
+
std::move(cb), transaction_operation_failed(*ec, "transaction expired during get").expired());
|
130
|
+
case FAIL_DOC_NOT_FOUND:
|
131
|
+
return op_completed_with_error(
|
132
|
+
std::move(cb),
|
133
|
+
transaction_operation_failed(*ec, fmt::format("document not found {}", err_message.value_or("")))
|
134
|
+
.cause(external_exception::DOCUMENT_NOT_FOUND_EXCEPTION));
|
135
|
+
case FAIL_TRANSIENT:
|
136
|
+
return op_completed_with_error(
|
137
|
+
std::move(cb),
|
138
|
+
transaction_operation_failed(*ec, fmt::format("transient failure in get {}", err_message.value_or("")))
|
139
|
+
.retry());
|
140
|
+
case FAIL_HARD:
|
141
|
+
return op_completed_with_error(
|
142
|
+
std::move(cb),
|
143
|
+
transaction_operation_failed(*ec, fmt::format("fail hard in get {}", err_message.value_or("")))
|
144
|
+
.no_rollback());
|
145
|
+
default: {
|
146
|
+
auto msg = fmt::format("got error \"{}\" while getting doc {}", err_message.value_or(""), id.key());
|
147
|
+
return op_completed_with_error(std::move(cb), transaction_operation_failed(FAIL_OTHER, msg));
|
148
|
+
}
|
149
|
+
}
|
150
|
+
} else {
|
151
|
+
if (!res) {
|
152
|
+
return op_completed_with_error(std::move(cb), transaction_operation_failed(*ec, "document not found"));
|
153
|
+
}
|
154
|
+
auto err = forward_compat::check(forward_compat_stage::GETS, res->links().forward_compat());
|
155
|
+
if (err) {
|
156
|
+
return op_completed_with_error(std::move(cb), *err);
|
157
|
+
}
|
158
|
+
return op_completed_with_callback(std::move(cb), res);
|
159
|
+
}
|
160
|
+
};
|
161
|
+
|
162
|
+
if (!ec) {
|
163
|
+
return hooks_.after_get_complete(this, id.key(), std::move(handler));
|
164
|
+
}
|
165
|
+
return handler(ec);
|
166
|
+
});
|
163
167
|
});
|
164
168
|
}
|
165
169
|
|
@@ -190,51 +194,55 @@ attempt_context_impl::get_optional(const core::document_id& id, Callback&& cb)
|
|
190
194
|
return op_completed_with_error(std::move(cb), transaction_operation_failed(FAIL_OTHER, ec.message()));
|
191
195
|
}
|
192
196
|
check_if_done(cb);
|
193
|
-
do_get(
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
197
|
+
do_get(
|
198
|
+
id,
|
199
|
+
std::nullopt,
|
200
|
+
[this, id, cb = std::move(cb)](
|
201
|
+
std::optional<error_class> ec, std::optional<std::string> err_message, std::optional<transaction_get_result> res) mutable {
|
202
|
+
auto handler = [this, id, err_message, res, cb = std::move(cb)](std::optional<error_class> ec) mutable {
|
203
|
+
if (ec) {
|
204
|
+
switch (*ec) {
|
205
|
+
case FAIL_EXPIRY:
|
206
|
+
return op_completed_with_error(
|
207
|
+
std::move(cb),
|
208
|
+
transaction_operation_failed(*ec,
|
209
|
+
fmt::format("transaction expired during get {}", err_message.value_or("")))
|
210
|
+
.expired());
|
211
|
+
case FAIL_DOC_NOT_FOUND:
|
212
|
+
return op_completed_with_callback(std::move(cb), std::optional<transaction_get_result>());
|
213
|
+
case FAIL_TRANSIENT:
|
214
|
+
return op_completed_with_error(
|
215
|
+
std::move(cb),
|
216
|
+
transaction_operation_failed(*ec, fmt::format("transient failure in get {}", err_message.value_or("")))
|
217
|
+
.retry());
|
218
|
+
case FAIL_HARD:
|
219
|
+
return op_completed_with_error(
|
220
|
+
std::move(cb),
|
221
|
+
transaction_operation_failed(*ec, fmt::format("fail hard in get {}", err_message.value_or("")))
|
222
|
+
.no_rollback());
|
223
|
+
default: {
|
224
|
+
return op_completed_with_error(
|
225
|
+
std::move(cb),
|
226
|
+
transaction_operation_failed(FAIL_OTHER,
|
227
|
+
fmt::format("error getting {} {}", id.key(), err_message.value_or(""))));
|
228
|
+
}
|
229
|
+
}
|
230
|
+
} else {
|
231
|
+
if (res) {
|
232
|
+
auto err = forward_compat::check(forward_compat_stage::GETS, res->links().forward_compat());
|
233
|
+
if (err) {
|
234
|
+
return op_completed_with_error(std::move(cb), *err);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
return op_completed_with_callback(std::move(cb), res);
|
238
|
+
}
|
239
|
+
};
|
240
|
+
|
241
|
+
if (!ec) {
|
242
|
+
return hooks_.after_get_complete(this, id.key(), std::move(handler));
|
243
|
+
}
|
244
|
+
return handler(ec);
|
245
|
+
});
|
238
246
|
});
|
239
247
|
});
|
240
248
|
}
|
@@ -386,7 +394,8 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
386
394
|
return op_completed_with_error(std::move(cb), err);
|
387
395
|
}
|
388
396
|
};
|
389
|
-
auto ec =
|
397
|
+
auto ec = wait_for_hook(
|
398
|
+
[this, key = document.id().key()](auto handler) mutable { return hooks_.before_staged_replace(this, key, std::move(handler)); });
|
390
399
|
if (ec) {
|
391
400
|
return error_handler(*ec, "before_staged_replace hook raised error", std::move(cb));
|
392
401
|
}
|
@@ -394,21 +403,27 @@ attempt_context_impl::create_staged_replace(const transaction_get_result& docume
|
|
394
403
|
this, "about to replace doc {} with cas {} in txn {}", document.id(), document.cas().value(), overall_.transaction_id());
|
395
404
|
overall_.cluster_ref().execute(
|
396
405
|
req,
|
397
|
-
[this, document
|
406
|
+
[this, document, content, cb = std::move(cb), error_handler = std::move(error_handler)](
|
398
407
|
core::operations::mutate_in_response resp) mutable {
|
399
408
|
if (auto ec2 = error_class_from_response(resp); ec2) {
|
400
409
|
return error_handler(*ec2, resp.ctx.ec().message(), std::move(cb));
|
401
410
|
}
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
411
|
+
return hooks_.after_staged_replace_complete(
|
412
|
+
this,
|
413
|
+
document.id().key(),
|
414
|
+
[this, document, content, error_handler = std::move(error_handler), cb = std::move(cb), resp = std::move(resp)](
|
415
|
+
auto ec) mutable {
|
416
|
+
if (ec) {
|
417
|
+
return error_handler(*ec, "after_staged_replace_commit hook returned error", std::move(cb));
|
418
|
+
}
|
419
|
+
|
420
|
+
transaction_get_result out = document;
|
421
|
+
out.cas(resp.cas.value());
|
422
|
+
out.content(content);
|
423
|
+
CB_ATTEMPT_CTX_LOG_TRACE(this, "replace staged content, result {}", out);
|
424
|
+
staged_mutations_->add(staged_mutation(out, content, staged_mutation_type::REPLACE));
|
425
|
+
return op_completed_with_callback(std::move(cb), std::optional<transaction_get_result>(out));
|
426
|
+
});
|
412
427
|
});
|
413
428
|
}
|
414
429
|
|
@@ -523,47 +538,51 @@ attempt_context_impl::check_atr_entry_for_blocking_document(const transaction_ge
|
|
523
538
|
{
|
524
539
|
try {
|
525
540
|
delay();
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
doc.links().atr_scope_name().value(),
|
531
|
-
doc.links().atr_collection_name().value(),
|
532
|
-
doc.links().atr_id().value());
|
533
|
-
active_transaction_record::get_atr(
|
534
|
-
cluster_ref(),
|
535
|
-
atr_id,
|
536
|
-
[this, delay = std::move(delay), cb = std::move(cb), doc = std::move(doc)](std::error_code err,
|
537
|
-
std::optional<active_transaction_record> atr) mutable {
|
538
|
-
if (!err) {
|
539
|
-
if (atr) {
|
540
|
-
auto entries = atr->entries();
|
541
|
-
auto it = std::find_if(entries.begin(), entries.end(), [&doc](const atr_entry& e) {
|
542
|
-
return e.attempt_id() == doc.links().staged_attempt_id();
|
543
|
-
});
|
544
|
-
if (it != entries.end()) {
|
545
|
-
auto fwd_err = forward_compat::check(forward_compat_stage::WWC_READING_ATR, it->forward_compat());
|
546
|
-
if (fwd_err) {
|
547
|
-
return cb(fwd_err);
|
548
|
-
}
|
549
|
-
switch (it->state()) {
|
550
|
-
case attempt_state::COMPLETED:
|
551
|
-
case attempt_state::ROLLED_BACK:
|
552
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(
|
553
|
-
this, "existing atr entry can be ignored due to state {}", attempt_state_name(it->state()));
|
554
|
-
return cb(std::nullopt);
|
555
|
-
default:
|
556
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(
|
557
|
-
this, "existing atr entry found in state {}, retrying", attempt_state_name(it->state()));
|
558
|
-
}
|
559
|
-
return check_atr_entry_for_blocking_document(doc, delay, std::move(cb));
|
560
|
-
}
|
561
|
-
}
|
562
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this, "no blocking atr entry");
|
563
|
-
return cb(std::nullopt);
|
541
|
+
return hooks_.before_check_atr_entry_for_blocking_doc(
|
542
|
+
this, doc.id().key(), [this, delay = std::move(delay), cb = std::move(cb), doc = std::move(doc)](auto ec) mutable {
|
543
|
+
if (ec) {
|
544
|
+
return cb(transaction_operation_failed(FAIL_WRITE_WRITE_CONFLICT, "document is in another transaction").retry());
|
564
545
|
}
|
565
|
-
|
566
|
-
|
546
|
+
|
547
|
+
core::document_id atr_id(doc.links().atr_bucket_name().value(),
|
548
|
+
doc.links().atr_scope_name().value(),
|
549
|
+
doc.links().atr_collection_name().value(),
|
550
|
+
doc.links().atr_id().value());
|
551
|
+
return active_transaction_record::get_atr(
|
552
|
+
cluster_ref(),
|
553
|
+
atr_id,
|
554
|
+
[this, delay = std::move(delay), cb = std::move(cb), doc = std::move(doc)](
|
555
|
+
std::error_code err, std::optional<active_transaction_record> atr) mutable {
|
556
|
+
if (!err) {
|
557
|
+
if (atr) {
|
558
|
+
auto entries = atr->entries();
|
559
|
+
auto it = std::find_if(entries.begin(), entries.end(), [&doc](const atr_entry& e) {
|
560
|
+
return e.attempt_id() == doc.links().staged_attempt_id();
|
561
|
+
});
|
562
|
+
if (it != entries.end()) {
|
563
|
+
auto fwd_err = forward_compat::check(forward_compat_stage::WWC_READING_ATR, it->forward_compat());
|
564
|
+
if (fwd_err) {
|
565
|
+
return cb(fwd_err);
|
566
|
+
}
|
567
|
+
switch (it->state()) {
|
568
|
+
case attempt_state::COMPLETED:
|
569
|
+
case attempt_state::ROLLED_BACK:
|
570
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(
|
571
|
+
this, "existing atr entry can be ignored due to state {}", attempt_state_name(it->state()));
|
572
|
+
return cb(std::nullopt);
|
573
|
+
default:
|
574
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(
|
575
|
+
this, "existing atr entry found in state {}, retrying", attempt_state_name(it->state()));
|
576
|
+
}
|
577
|
+
return check_atr_entry_for_blocking_document(doc, delay, std::move(cb));
|
578
|
+
}
|
579
|
+
}
|
580
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "no blocking atr entry");
|
581
|
+
return cb(std::nullopt);
|
582
|
+
}
|
583
|
+
// if we are here, there is still a write-write conflict
|
584
|
+
return cb(transaction_operation_failed(FAIL_WRITE_WRITE_CONFLICT, "document is in another transaction").retry());
|
585
|
+
});
|
567
586
|
});
|
568
587
|
} catch (const retry_operation_timeout&) {
|
569
588
|
return cb(transaction_operation_failed(FAIL_WRITE_WRITE_CONFLICT, "document is in another transaction").retry());
|
@@ -620,7 +639,7 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
620
639
|
return;
|
621
640
|
}
|
622
641
|
}
|
623
|
-
check_and_handle_blocking_transactions(
|
642
|
+
return check_and_handle_blocking_transactions(
|
624
643
|
document,
|
625
644
|
forward_compat_stage::WWC_REMOVING,
|
626
645
|
[this, document = std::move(document), cb = std::move(cb), op_id, error_handler = std::move(error_handler)](
|
@@ -630,38 +649,57 @@ attempt_context_impl::remove(const transaction_get_result& document, VoidCallbac
|
|
630
649
|
}
|
631
650
|
auto tmp_doc =
|
632
651
|
document_id{ document.id().bucket(), document.id().scope(), document.id().collection(), document.id().key() };
|
633
|
-
select_atr_if_needed_unlocked(
|
652
|
+
return select_atr_if_needed_unlocked(
|
634
653
|
tmp_doc,
|
635
654
|
[document = std::move(document), cb = std::move(cb), this, op_id, error_handler = std::move(error_handler)](
|
636
655
|
std::optional<transaction_operation_failed> err2) mutable {
|
637
656
|
if (err2) {
|
638
657
|
return op_completed_with_error(std::move(cb), *err2);
|
639
658
|
}
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
req,
|
649
|
-
[this, document = std::move(document), cb = std::move(cb), error_handler = std::move(error_handler)](
|
650
|
-
core::operations::mutate_in_response resp) mutable {
|
651
|
-
auto ec = error_class_from_response(resp);
|
652
|
-
if (!ec) {
|
653
|
-
ec = hooks_.after_staged_remove_complete(this, document.id().key());
|
654
|
-
}
|
655
|
-
if (!ec) {
|
656
|
-
CB_ATTEMPT_CTX_LOG_TRACE(
|
657
|
-
this, "removed doc {} CAS={}, rc={}", document.id(), resp.cas.value(), resp.ctx.ec().message());
|
658
|
-
// TODO: this copy... can we do better?
|
659
|
-
transaction_get_result new_res = document;
|
660
|
-
new_res.cas(resp.cas.value());
|
661
|
-
staged_mutations_->add(staged_mutation(new_res, std::vector<std::byte>{}, staged_mutation_type::REMOVE));
|
662
|
-
return op_completed_with_callback(cb);
|
659
|
+
auto key = document.id().key();
|
660
|
+
return hooks_.before_staged_remove(
|
661
|
+
this,
|
662
|
+
key,
|
663
|
+
[document = std::move(document), cb = std::move(cb), this, op_id, error_handler = std::move(error_handler)](
|
664
|
+
auto ec) mutable {
|
665
|
+
if (ec) {
|
666
|
+
return error_handler(*ec, "before_staged_remove hook raised error", std::move(cb));
|
663
667
|
}
|
664
|
-
|
668
|
+
CB_ATTEMPT_CTX_LOG_TRACE(this, "about to remove doc {} with cas {}", document.id(), document.cas().value());
|
669
|
+
auto req = create_staging_request(document.id(), &document, "remove", op_id);
|
670
|
+
req.cas = document.cas();
|
671
|
+
req.access_deleted = document.links().is_deleted();
|
672
|
+
return overall_.cluster_ref().execute(
|
673
|
+
req,
|
674
|
+
[this, document = std::move(document), cb = std::move(cb), error_handler = std::move(error_handler)](
|
675
|
+
core::operations::mutate_in_response resp) mutable {
|
676
|
+
auto ec = error_class_from_response(resp);
|
677
|
+
if (ec) {
|
678
|
+
return error_handler(*ec, resp.ctx.ec().message(), std::move(cb));
|
679
|
+
}
|
680
|
+
auto key = document.id().key();
|
681
|
+
return hooks_.after_staged_remove_complete(
|
682
|
+
this,
|
683
|
+
key,
|
684
|
+
[this,
|
685
|
+
document = std::move(document),
|
686
|
+
cb = std::move(cb),
|
687
|
+
error_handler = std::move(error_handler),
|
688
|
+
resp = std::move(resp)](auto ec) mutable {
|
689
|
+
if (ec) {
|
690
|
+
return error_handler(*ec, resp.ctx.ec().message(), std::move(cb));
|
691
|
+
}
|
692
|
+
|
693
|
+
CB_ATTEMPT_CTX_LOG_TRACE(
|
694
|
+
this, "removed doc {} CAS={}, rc={}", document.id(), resp.cas.value(), resp.ctx.ec().message());
|
695
|
+
// TODO: this copy... can we do better?
|
696
|
+
transaction_get_result new_res = document;
|
697
|
+
new_res.cas(resp.cas.value());
|
698
|
+
staged_mutations_->add(
|
699
|
+
staged_mutation(new_res, std::vector<std::byte>{}, staged_mutation_type::REMOVE));
|
700
|
+
return op_completed_with_callback(cb);
|
701
|
+
});
|
702
|
+
});
|
665
703
|
});
|
666
704
|
});
|
667
705
|
});
|
@@ -688,36 +726,41 @@ attempt_context_impl::remove_staged_insert(const core::document_id& id, VoidCall
|
|
688
726
|
};
|
689
727
|
CB_ATTEMPT_CTX_LOG_DEBUG(this, "removing staged insert {}", id);
|
690
728
|
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
729
|
+
return hooks_.before_remove_staged_insert(
|
730
|
+
this, id.key(), [this, id = std::move(id), cb = std::move(cb), error_handler = std::move(error_handler)](auto ec) mutable {
|
731
|
+
if (ec) {
|
732
|
+
return error_handler(*ec, "before_remove_staged_insert hook returned error", std::move(cb));
|
733
|
+
}
|
734
|
+
core::operations::mutate_in_request req{ id };
|
735
|
+
req.specs =
|
736
|
+
couchbase::mutate_in_specs{
|
737
|
+
couchbase::mutate_in_specs::remove("txn").xattr(),
|
738
|
+
}
|
739
|
+
.specs();
|
740
|
+
wrap_durable_request(req, overall_.config());
|
741
|
+
req.access_deleted = true;
|
742
|
+
|
743
|
+
return overall_.cluster_ref().execute(
|
744
|
+
req,
|
745
|
+
[this, id = std::move(id), cb = std::move(cb), error_handler = std::move(error_handler)](
|
746
|
+
core::operations::mutate_in_response resp) mutable {
|
747
|
+
auto ec = error_class_from_response(resp);
|
748
|
+
if (ec) {
|
749
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "remove_staged_insert got error {}", *ec);
|
750
|
+
return error_handler(*ec, resp.ctx.ec().message(), std::move(cb));
|
751
|
+
}
|
752
|
+
return hooks_.after_remove_staged_insert(
|
753
|
+
this,
|
754
|
+
id.key(),
|
755
|
+
[this, id = std::move(id), cb = std::move(cb), error_handler = std::move(error_handler)](auto ec) mutable {
|
756
|
+
if (ec) {
|
757
|
+
return error_handler(*ec, "after_remove_staged_insert hook returned error", std::move(cb));
|
758
|
+
}
|
759
|
+
staged_mutations_->remove_any(id);
|
760
|
+
return op_completed_with_callback(std::move(cb));
|
761
|
+
});
|
762
|
+
});
|
763
|
+
});
|
721
764
|
}
|
722
765
|
|
723
766
|
void
|
@@ -1018,21 +1061,26 @@ attempt_context_impl::wrap_query(const std::string& statement,
|
|
1018
1061
|
req.raw["txdata"] = core::utils::json::generate(txdata);
|
1019
1062
|
}
|
1020
1063
|
req.statement = statement;
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
CB_ATTEMPT_CTX_LOG_TRACE(this, "http request: {}", dump_request(req));
|
1029
|
-
overall_.cluster_ref().execute(req, [this, cb = std::move(cb)](core::operations::query_response resp) mutable {
|
1030
|
-
CB_ATTEMPT_CTX_LOG_TRACE(this, "response: {} status: {}", resp.ctx.http_body, resp.meta.status);
|
1031
|
-
if (auto ec = hooks_.after_query(this, resp.ctx.statement)) {
|
1032
|
-
auto err = std::make_exception_ptr(transaction_operation_failed(*ec, "after_query hook raised error"));
|
1033
|
-
return cb(err, {});
|
1064
|
+
return hooks_.before_query(this, statement, [this, statement, req, cb = std::move(cb)](auto ec) mutable {
|
1065
|
+
if (ec) {
|
1066
|
+
auto err = std::make_exception_ptr(transaction_operation_failed(*ec, "before_query hook raised error"));
|
1067
|
+
if (statement == BEGIN_WORK) {
|
1068
|
+
return cb(std::make_exception_ptr(transaction_operation_failed(*ec, "before_query hook raised error").no_rollback()), {});
|
1069
|
+
}
|
1070
|
+
return cb(std::make_exception_ptr(transaction_operation_failed(*ec, "before_query hook raised error")), {});
|
1034
1071
|
}
|
1035
|
-
|
1072
|
+
|
1073
|
+
CB_ATTEMPT_CTX_LOG_TRACE(this, "http request: {}", dump_request(req));
|
1074
|
+
return overall_.cluster_ref().execute(req, [this, cb = std::move(cb)](core::operations::query_response resp) mutable {
|
1075
|
+
CB_ATTEMPT_CTX_LOG_TRACE(this, "response: {} status: {}", resp.ctx.http_body, resp.meta.status);
|
1076
|
+
return hooks_.after_query(this, resp.ctx.statement, [this, resp = std::move(resp), cb = std::move(cb)](auto ec) mutable {
|
1077
|
+
if (ec) {
|
1078
|
+
auto err = std::make_exception_ptr(transaction_operation_failed(*ec, "after_query hook raised error"));
|
1079
|
+
return cb(err, {});
|
1080
|
+
}
|
1081
|
+
return cb(handle_query_error(resp), resp);
|
1082
|
+
});
|
1083
|
+
});
|
1036
1084
|
});
|
1037
1085
|
}
|
1038
1086
|
|
@@ -1389,7 +1437,8 @@ attempt_context_impl::atr_commit(bool ambiguity_resolution_mode)
|
|
1389
1437
|
if (ec) {
|
1390
1438
|
throw client_error(*ec, "atr_commit check for expiry threw error");
|
1391
1439
|
}
|
1392
|
-
|
1440
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.before_atr_commit(this, std::move(handler)); });
|
1441
|
+
if (ec) {
|
1393
1442
|
// for now, throw. Later, if this is async, we will use error handler no doubt.
|
1394
1443
|
throw client_error(*ec, "before_atr_commit hook raised error");
|
1395
1444
|
}
|
@@ -1400,7 +1449,7 @@ attempt_context_impl::atr_commit(bool ambiguity_resolution_mode)
|
|
1400
1449
|
overall_.cluster_ref().execute(
|
1401
1450
|
req, [barrier](core::operations::mutate_in_response resp) { barrier->set_value(result::create_from_subdoc_response(resp)); });
|
1402
1451
|
auto res = wrap_operation_future(f, false);
|
1403
|
-
ec = hooks_.after_atr_commit(this);
|
1452
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.after_atr_commit(this, std::move(handler)); });
|
1404
1453
|
if (ec) {
|
1405
1454
|
throw client_error(*ec, "after_atr_commit hook raised error");
|
1406
1455
|
}
|
@@ -1490,7 +1539,9 @@ attempt_context_impl::atr_commit_ambiguity_resolution()
|
|
1490
1539
|
if (ec) {
|
1491
1540
|
throw client_error(*ec, "atr_commit_ambiguity_resolution raised error");
|
1492
1541
|
}
|
1493
|
-
|
1542
|
+
ec =
|
1543
|
+
wait_for_hook([this](auto handler) mutable { return hooks_.before_atr_commit_ambiguity_resolution(this, std::move(handler)); });
|
1544
|
+
if (ec) {
|
1494
1545
|
throw client_error(*ec, "before_atr_commit_ambiguity_resolution hook threw error");
|
1495
1546
|
}
|
1496
1547
|
std::string prefix(ATR_FIELD_ATTEMPTS + "." + id() + ".");
|
@@ -1540,7 +1591,7 @@ attempt_context_impl::atr_complete()
|
|
1540
1591
|
{
|
1541
1592
|
try {
|
1542
1593
|
result atr_res;
|
1543
|
-
auto ec = hooks_.before_atr_complete(this);
|
1594
|
+
auto ec = wait_for_hook([this](auto handler) mutable { return hooks_.before_atr_complete(this, std::move(handler)); });
|
1544
1595
|
if (ec) {
|
1545
1596
|
throw client_error(*ec, "before_atr_complete hook threw error");
|
1546
1597
|
}
|
@@ -1562,7 +1613,7 @@ attempt_context_impl::atr_complete()
|
|
1562
1613
|
overall_.cluster_ref().execute(
|
1563
1614
|
req, [barrier](core::operations::mutate_in_response resp) { barrier->set_value(result::create_from_subdoc_response(resp)); });
|
1564
1615
|
wrap_operation_future(f);
|
1565
|
-
ec = hooks_.after_atr_complete(this);
|
1616
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.after_atr_complete(this, std::move(handler)); });
|
1566
1617
|
if (ec) {
|
1567
1618
|
throw client_error(*ec, "after_atr_complete hook threw error");
|
1568
1619
|
}
|
@@ -1640,7 +1691,8 @@ attempt_context_impl::atr_abort()
|
|
1640
1691
|
if (ec) {
|
1641
1692
|
throw client_error(*ec, "atr_abort check for expiry threw error");
|
1642
1693
|
}
|
1643
|
-
|
1694
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.before_atr_aborted(this, std::move(handler)); });
|
1695
|
+
if (ec) {
|
1644
1696
|
throw client_error(*ec, "before_atr_aborted hook threw error");
|
1645
1697
|
}
|
1646
1698
|
std::string prefix(ATR_FIELD_ATTEMPTS + "." + id() + ".");
|
@@ -1663,7 +1715,8 @@ attempt_context_impl::atr_abort()
|
|
1663
1715
|
req, [barrier](core::operations::mutate_in_response resp) { barrier->set_value(result::create_from_subdoc_response(resp)); });
|
1664
1716
|
wrap_operation_future(f);
|
1665
1717
|
state(attempt_state::ABORTED);
|
1666
|
-
|
1718
|
+
|
1719
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.after_atr_aborted(this, std::move(handler)); });
|
1667
1720
|
if (ec) {
|
1668
1721
|
throw client_error(*ec, "after_atr_aborted hook threw error");
|
1669
1722
|
}
|
@@ -1704,7 +1757,9 @@ attempt_context_impl::atr_rollback_complete()
|
|
1704
1757
|
if (ec) {
|
1705
1758
|
throw client_error(*ec, "atr_rollback_complete raised error");
|
1706
1759
|
}
|
1707
|
-
|
1760
|
+
|
1761
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.before_atr_rolled_back(this, std::move(handler)); });
|
1762
|
+
if (ec) {
|
1708
1763
|
throw client_error(*ec, "before_atr_rolled_back hook threw error");
|
1709
1764
|
}
|
1710
1765
|
std::string prefix(ATR_FIELD_ATTEMPTS + "." + id());
|
@@ -1721,7 +1776,7 @@ attempt_context_impl::atr_rollback_complete()
|
|
1721
1776
|
req, [barrier](core::operations::mutate_in_response resp) { barrier->set_value(result::create_from_subdoc_response(resp)); });
|
1722
1777
|
wrap_operation_future(f);
|
1723
1778
|
state(attempt_state::ROLLED_BACK);
|
1724
|
-
ec = hooks_.after_atr_rolled_back(this);
|
1779
|
+
ec = wait_for_hook([this](auto handler) mutable { return hooks_.after_atr_rolled_back(this, std::move(handler)); });
|
1725
1780
|
if (ec) {
|
1726
1781
|
throw client_error(*ec, "after_atr_rolled_back hook threw error");
|
1727
1782
|
}
|
@@ -1928,58 +1983,78 @@ attempt_context_impl::set_atr_pending_locked(const core::document_id& id, std::u
|
|
1928
1983
|
return fn(err);
|
1929
1984
|
}
|
1930
1985
|
};
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1935
|
-
|
1936
|
-
std::chrono::nanoseconds remaining = overall_.remaining();
|
1937
|
-
// This bounds the value to [0-timeout]. It should always be in this range, this is just to protect
|
1938
|
-
// against the application clock changing.
|
1939
|
-
long remaining_bounded_nanos =
|
1940
|
-
std::max(std::min(remaining.count(), overall_.config().timeout.count()), static_cast<std::chrono::nanoseconds::rep>(0));
|
1941
|
-
long remaining_bounded_msecs = remaining_bounded_nanos / 1'000'000;
|
1942
|
-
|
1943
|
-
core::operations::mutate_in_request req{ atr_id_.value() };
|
1986
|
+
return hooks_.before_atr_pending(
|
1987
|
+
this, [this, id, prefix, fn = std::forward<Handler>(fn), error_handler = std::move(error_handler)](auto ec) mutable {
|
1988
|
+
if (ec) {
|
1989
|
+
return error_handler(*ec, "before_atr_pending hook raised error", id, std::forward<Handler>(fn));
|
1990
|
+
}
|
1944
1991
|
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
|
1992
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "updating atr {}", atr_id_.value());
|
1993
|
+
|
1994
|
+
std::chrono::nanoseconds remaining = overall_.remaining();
|
1995
|
+
// This bounds the value to [0-timeout]. It should always be in this range, this is just to protect
|
1996
|
+
// against the application clock changing.
|
1997
|
+
long remaining_bounded_nanos =
|
1998
|
+
std::max(std::min(remaining.count(), overall_.config().timeout.count()), static_cast<std::chrono::nanoseconds::rep>(0));
|
1999
|
+
long remaining_bounded_msecs = remaining_bounded_nanos / 1'000'000;
|
2000
|
+
|
2001
|
+
core::operations::mutate_in_request req{ atr_id_.value() };
|
2002
|
+
|
2003
|
+
req.specs =
|
2004
|
+
couchbase::mutate_in_specs{
|
2005
|
+
couchbase::mutate_in_specs::insert(prefix + ATR_FIELD_TRANSACTION_ID, overall_.transaction_id())
|
2006
|
+
.xattr()
|
2007
|
+
.create_path(),
|
2008
|
+
couchbase::mutate_in_specs::insert(prefix + ATR_FIELD_STATUS, attempt_state_name(attempt_state::PENDING))
|
2009
|
+
.xattr()
|
2010
|
+
.create_path(),
|
2011
|
+
couchbase::mutate_in_specs::insert(prefix + ATR_FIELD_START_TIMESTAMP, subdoc::mutate_in_macro::cas)
|
2012
|
+
.xattr()
|
2013
|
+
.create_path(),
|
2014
|
+
couchbase::mutate_in_specs::insert(prefix + ATR_FIELD_EXPIRES_AFTER_MSECS, remaining_bounded_msecs)
|
2015
|
+
.xattr()
|
2016
|
+
.create_path(),
|
2017
|
+
// ExtStoreDurability
|
2018
|
+
couchbase::mutate_in_specs::insert(prefix + ATR_FIELD_DURABILITY_LEVEL,
|
2019
|
+
store_durability_level_to_string(overall_.config().level))
|
2020
|
+
.xattr()
|
2021
|
+
.create_path(),
|
2022
|
+
// subdoc::opcode::set_doc used in replace w/ empty path
|
2023
|
+
// ExtBinaryMetadata
|
2024
|
+
couchbase::mutate_in_specs::replace_raw({}, std::vector<std::byte>{ std::byte{ 0x00 } }),
|
2025
|
+
}
|
2026
|
+
.specs();
|
2027
|
+
req.store_semantics = couchbase::store_semantics::upsert;
|
2028
|
+
|
2029
|
+
wrap_durable_request(req, overall_.config());
|
2030
|
+
return overall_.cluster_ref().execute(
|
2031
|
+
req,
|
2032
|
+
[this, fn = std::forward<Handler>(fn), error_handler = std::move(error_handler)](
|
2033
|
+
core::operations::mutate_in_response resp) mutable {
|
2034
|
+
auto ec = error_class_from_response(resp);
|
2035
|
+
if (ec) {
|
2036
|
+
return error_handler(*ec,
|
2037
|
+
resp.ctx.ec().message(),
|
2038
|
+
{ resp.ctx.bucket(), resp.ctx.scope(), resp.ctx.collection(), resp.ctx.id() },
|
2039
|
+
std::forward<Handler>(fn));
|
2040
|
+
}
|
2041
|
+
return hooks_.after_atr_pending(
|
2042
|
+
this,
|
2043
|
+
[this, fn = std::forward<Handler>(fn), error_handler = std::move(error_handler), resp = std::move(resp)](
|
2044
|
+
auto ec) mutable {
|
2045
|
+
if (ec) {
|
2046
|
+
return error_handler(*ec,
|
2047
|
+
fmt::format("after_atr_pending returned hook raised {}", *ec),
|
2048
|
+
{ resp.ctx.bucket(), resp.ctx.scope(), resp.ctx.collection(), resp.ctx.id() },
|
2049
|
+
std::forward<Handler>(fn));
|
2050
|
+
}
|
1966
2051
|
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
}
|
1974
|
-
if (!ec) {
|
1975
|
-
state(attempt_state::PENDING);
|
1976
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this, "set ATR {} to Pending, got CAS (start time) {}", atr_id_.value(), resp.cas.value());
|
1977
|
-
return fn(std::nullopt);
|
1978
|
-
}
|
1979
|
-
return error_handler(*ec,
|
1980
|
-
resp.ctx.ec().message(),
|
1981
|
-
{ resp.ctx.bucket(), resp.ctx.scope(), resp.ctx.collection(), resp.ctx.id() },
|
1982
|
-
std::forward<Handler>(fn));
|
2052
|
+
state(attempt_state::PENDING);
|
2053
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2054
|
+
this, "set ATR {} to Pending, got CAS (start time) {}", atr_id_.value(), resp.cas.value());
|
2055
|
+
return fn(std::nullopt);
|
2056
|
+
});
|
2057
|
+
});
|
1983
2058
|
});
|
1984
2059
|
}
|
1985
2060
|
} catch (const std::exception& e) {
|
@@ -2034,113 +2109,123 @@ attempt_context_impl::do_get(const core::document_id& id, const std::optional<st
|
|
2034
2109
|
return cb(FAIL_DOC_NOT_FOUND, msg, std::nullopt);
|
2035
2110
|
}
|
2036
2111
|
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2043
|
-
[this, id, resolving_missing_atr_entry = std::move(resolving_missing_atr_entry), cb = std::move(cb)](
|
2044
|
-
std::optional<error_class> ec, std::optional<std::string> err_message, std::optional<transaction_get_result> doc) mutable {
|
2045
|
-
if (!ec && !doc) {
|
2046
|
-
// it just isn't there.
|
2047
|
-
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2112
|
+
return hooks_.before_doc_get(
|
2113
|
+
this,
|
2114
|
+
id.key(),
|
2115
|
+
[this, id, resolving_missing_atr_entry = std::move(resolving_missing_atr_entry), cb = std::move(cb)](auto ec) mutable {
|
2116
|
+
if (ec) {
|
2117
|
+
return cb(ec, "before_doc_get hook raised error", std::nullopt);
|
2048
2118
|
}
|
2049
|
-
if (!ec) {
|
2050
|
-
if (doc->links().is_document_in_transaction()) {
|
2051
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2052
|
-
this, "doc {} in transaction, resolving_missing_atr_entry={}", *doc, resolving_missing_atr_entry.value_or("-"));
|
2053
|
-
|
2054
|
-
if (resolving_missing_atr_entry.has_value() &&
|
2055
|
-
resolving_missing_atr_entry.value() == doc->links().staged_attempt_id()) {
|
2056
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this, "doc is in lost pending transaction");
|
2057
|
-
|
2058
|
-
if (doc->links().is_document_being_inserted()) {
|
2059
|
-
// this document is being inserted, so should not be visible yet
|
2060
|
-
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2061
|
-
}
|
2062
2119
|
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
if (doc->links().staged_attempt_id() && entry->attempt_id() == this->id()) {
|
2087
|
-
// Attempt is reading its own writes
|
2088
|
-
// This is here as backup, it should be returned from the in-memory cache instead
|
2089
|
-
content = doc->links().staged_content();
|
2090
|
-
} else {
|
2091
|
-
auto err = forward_compat::check(forward_compat_stage::GETS_READING_ATR, entry->forward_compat());
|
2092
|
-
if (err) {
|
2093
|
-
return cb(FAIL_OTHER, err->what(), std::nullopt);
|
2094
|
-
}
|
2095
|
-
switch (entry->state()) {
|
2096
|
-
case attempt_state::COMPLETED:
|
2097
|
-
case attempt_state::COMMITTED:
|
2098
|
-
if (doc->links().is_document_being_removed()) {
|
2099
|
-
ignore_doc = true;
|
2100
|
-
} else {
|
2101
|
-
content = doc->links().staged_content();
|
2102
|
-
}
|
2103
|
-
break;
|
2104
|
-
default:
|
2105
|
-
if (doc->links().is_document_being_inserted()) {
|
2106
|
-
// This document is being inserted, so should not be visible yet
|
2107
|
-
ignore_doc = true;
|
2108
|
-
}
|
2109
|
-
break;
|
2110
|
-
}
|
2111
|
-
}
|
2112
|
-
} else {
|
2113
|
-
// failed to get the ATR entry
|
2114
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this,
|
2115
|
-
"could not get ATR entry, checking again with {}",
|
2116
|
-
doc->links().staged_attempt_id().value_or("-"));
|
2117
|
-
return do_get(id, doc->links().staged_attempt_id(), cb);
|
2118
|
-
}
|
2119
|
-
if (ignore_doc) {
|
2120
|
+
return get_doc(
|
2121
|
+
id,
|
2122
|
+
[this, id, resolving_missing_atr_entry = std::move(resolving_missing_atr_entry), cb = std::move(cb)](
|
2123
|
+
std::optional<error_class> ec,
|
2124
|
+
std::optional<std::string> err_message,
|
2125
|
+
std::optional<transaction_get_result> doc) mutable {
|
2126
|
+
if (!ec && !doc) {
|
2127
|
+
// it just isn't there.
|
2128
|
+
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2129
|
+
}
|
2130
|
+
if (!ec) {
|
2131
|
+
if (doc->links().is_document_in_transaction()) {
|
2132
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this,
|
2133
|
+
"doc {} in transaction, resolving_missing_atr_entry={}",
|
2134
|
+
*doc,
|
2135
|
+
resolving_missing_atr_entry.value_or("-"));
|
2136
|
+
|
2137
|
+
if (resolving_missing_atr_entry.has_value() &&
|
2138
|
+
resolving_missing_atr_entry.value() == doc->links().staged_attempt_id()) {
|
2139
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "doc is in lost pending transaction");
|
2140
|
+
|
2141
|
+
if (doc->links().is_document_being_inserted()) {
|
2142
|
+
// this document is being inserted, so should not be visible yet
|
2120
2143
|
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2121
2144
|
}
|
2122
|
-
return cb(std::nullopt, std::nullopt, transaction_get_result::create_from(*doc, content));
|
2123
2145
|
|
2124
|
-
|
2125
|
-
// failed to get the ATR
|
2126
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2127
|
-
this, "could not get ATR, checking again with {}", doc->links().staged_attempt_id().value_or("-"));
|
2128
|
-
return do_get(id, doc->links().staged_attempt_id(), cb);
|
2146
|
+
return cb(std::nullopt, std::nullopt, doc);
|
2129
2147
|
}
|
2130
|
-
});
|
2131
|
-
} else {
|
2132
|
-
if (doc->links().is_deleted()) {
|
2133
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this, "doc not in txn, and is_deleted, so not returning it.");
|
2134
|
-
// doc has been deleted, not in txn, so don't return it
|
2135
|
-
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2136
|
-
}
|
2137
|
-
return cb(std::nullopt, std::nullopt, doc);
|
2138
|
-
}
|
2139
|
-
} else {
|
2140
|
-
return cb(ec, err_message, std::nullopt);
|
2141
|
-
}
|
2142
|
-
});
|
2143
2148
|
|
2149
|
+
core::document_id doc_atr_id{ doc->links().atr_bucket_name().value(),
|
2150
|
+
doc->links().atr_scope_name().value(),
|
2151
|
+
doc->links().atr_collection_name().value(),
|
2152
|
+
doc->links().atr_id().value() };
|
2153
|
+
active_transaction_record::get_atr(
|
2154
|
+
cluster_ref(),
|
2155
|
+
doc_atr_id,
|
2156
|
+
[this, id, doc, cb = std::move(cb)](std::error_code ec2,
|
2157
|
+
std::optional<active_transaction_record> atr) mutable {
|
2158
|
+
if (!ec2 && atr) {
|
2159
|
+
active_transaction_record& atr_doc = atr.value();
|
2160
|
+
std::optional<atr_entry> entry;
|
2161
|
+
for (const auto& e : atr_doc.entries()) {
|
2162
|
+
if (doc->links().staged_attempt_id().value() == e.attempt_id()) {
|
2163
|
+
entry.emplace(e);
|
2164
|
+
break;
|
2165
|
+
}
|
2166
|
+
}
|
2167
|
+
bool ignore_doc = false;
|
2168
|
+
auto content = doc->content();
|
2169
|
+
if (entry) {
|
2170
|
+
if (doc->links().staged_attempt_id() && entry->attempt_id() == this->id()) {
|
2171
|
+
// Attempt is reading its own writes
|
2172
|
+
// This is here as backup, it should be returned from the in-memory cache instead
|
2173
|
+
content = doc->links().staged_content();
|
2174
|
+
} else {
|
2175
|
+
auto err =
|
2176
|
+
forward_compat::check(forward_compat_stage::GETS_READING_ATR, entry->forward_compat());
|
2177
|
+
if (err) {
|
2178
|
+
return cb(FAIL_OTHER, err->what(), std::nullopt);
|
2179
|
+
}
|
2180
|
+
switch (entry->state()) {
|
2181
|
+
case attempt_state::COMPLETED:
|
2182
|
+
case attempt_state::COMMITTED:
|
2183
|
+
if (doc->links().is_document_being_removed()) {
|
2184
|
+
ignore_doc = true;
|
2185
|
+
} else {
|
2186
|
+
content = doc->links().staged_content();
|
2187
|
+
}
|
2188
|
+
break;
|
2189
|
+
default:
|
2190
|
+
if (doc->links().is_document_being_inserted()) {
|
2191
|
+
// This document is being inserted, so should not be visible yet
|
2192
|
+
ignore_doc = true;
|
2193
|
+
}
|
2194
|
+
break;
|
2195
|
+
}
|
2196
|
+
}
|
2197
|
+
} else {
|
2198
|
+
// failed to get the ATR entry
|
2199
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this,
|
2200
|
+
"could not get ATR entry, checking again with {}",
|
2201
|
+
doc->links().staged_attempt_id().value_or("-"));
|
2202
|
+
return do_get(id, doc->links().staged_attempt_id(), cb);
|
2203
|
+
}
|
2204
|
+
if (ignore_doc) {
|
2205
|
+
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2206
|
+
}
|
2207
|
+
return cb(std::nullopt, std::nullopt, transaction_get_result::create_from(*doc, content));
|
2208
|
+
|
2209
|
+
} else {
|
2210
|
+
// failed to get the ATR
|
2211
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2212
|
+
this, "could not get ATR, checking again with {}", doc->links().staged_attempt_id().value_or("-"));
|
2213
|
+
return do_get(id, doc->links().staged_attempt_id(), cb);
|
2214
|
+
}
|
2215
|
+
});
|
2216
|
+
} else {
|
2217
|
+
if (doc->links().is_deleted()) {
|
2218
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "doc not in txn, and is_deleted, so not returning it.");
|
2219
|
+
// doc has been deleted, not in txn, so don't return it
|
2220
|
+
return cb(std::nullopt, std::nullopt, std::nullopt);
|
2221
|
+
}
|
2222
|
+
return cb(std::nullopt, std::nullopt, doc);
|
2223
|
+
}
|
2224
|
+
} else {
|
2225
|
+
return cb(ec, err_message, std::nullopt);
|
2226
|
+
}
|
2227
|
+
});
|
2228
|
+
});
|
2144
2229
|
} catch (const transaction_operation_failed&) {
|
2145
2230
|
throw;
|
2146
2231
|
} catch (const std::exception& ex) {
|
@@ -2250,88 +2335,96 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
|
|
2250
2335
|
fmt::format("failed getting doc in create_staged_insert with {}", err_message)));
|
2251
2336
|
}
|
2252
2337
|
};
|
2253
|
-
|
2254
|
-
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2258
|
-
id,
|
2259
|
-
[this, id, content, op_id, cb = std::forward<Handler>(cb), error_handler, delay](
|
2260
|
-
std::optional<error_class> ec3, std::optional<std::string> err_message, std::optional<transaction_get_result> doc) mutable {
|
2261
|
-
if (!ec3) {
|
2262
|
-
if (doc) {
|
2263
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this,
|
2264
|
-
"document {} exists, is_in_transaction {}, is_deleted {} ",
|
2265
|
-
doc->id(),
|
2266
|
-
doc->links().is_document_in_transaction(),
|
2267
|
-
doc->links().is_deleted());
|
2268
|
-
|
2269
|
-
if (auto err = forward_compat::check(forward_compat_stage::WWC_INSERTING_GET, doc->links().forward_compat());
|
2270
|
-
err) {
|
2271
|
-
return op_completed_with_error(std::forward<Handler>(cb), *err);
|
2272
|
-
}
|
2273
|
-
if (!doc->links().is_document_in_transaction() && doc->links().is_deleted()) {
|
2274
|
-
// it is just a deleted doc, so we are ok. Let's try again, but with the cas
|
2275
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2276
|
-
this, "create staged insert found existing deleted doc, retrying with cas {}", doc->cas().value());
|
2277
|
-
delay();
|
2278
|
-
return create_staged_insert(id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
|
2279
|
-
}
|
2280
|
-
if (!doc->links().is_document_in_transaction()) {
|
2281
|
-
// doc was inserted outside txn elsewhere
|
2282
|
-
CB_ATTEMPT_CTX_LOG_TRACE(this, "doc {} not in txn - was inserted outside txn", id);
|
2283
|
-
return op_completed_with_error(
|
2284
|
-
std::forward<Handler>(cb),
|
2285
|
-
document_exists({ couchbase::errc::transaction_op::document_exists_exception, key_value_error_context() }));
|
2286
|
-
}
|
2287
|
-
if (doc->links().staged_attempt_id() == this->id()) {
|
2288
|
-
if (doc->links().staged_operation_id() == op_id) {
|
2289
|
-
// this is us dealing with resolving an ambiguity. So, lets just update the staged_mutation with the
|
2290
|
-
// correct cas and continue...
|
2291
|
-
staged_mutations_->add(staged_mutation(*doc, content, staged_mutation_type::INSERT));
|
2292
|
-
return op_completed_with_callback(std::forward<Handler>(cb), doc);
|
2293
|
-
}
|
2294
|
-
return op_completed_with_error(
|
2295
|
-
std::forward<Handler>(cb),
|
2296
|
-
transaction_operation_failed(FAIL_OTHER, "concurrent operations on a document are not allowed")
|
2297
|
-
.cause(CONCURRENT_OPERATIONS_DETECTED_ON_SAME_DOCUMENT));
|
2298
|
-
}
|
2299
|
-
// CBD-3787 - Only a staged insert is ok to overwrite
|
2300
|
-
if (doc->links().op() && *doc->links().op() != "insert") {
|
2301
|
-
return op_completed_with_error(
|
2302
|
-
std::forward<Handler>(cb),
|
2303
|
-
transaction_operation_failed(FAIL_DOC_ALREADY_EXISTS, "doc exists, not a staged insert")
|
2304
|
-
.cause(DOCUMENT_EXISTS_EXCEPTION));
|
2305
|
-
}
|
2306
|
-
check_and_handle_blocking_transactions(
|
2307
|
-
*doc,
|
2308
|
-
forward_compat_stage::WWC_INSERTING,
|
2309
|
-
[this, id, op_id, content, doc, cb = std::forward<Handler>(cb), delay](
|
2310
|
-
std::optional<transaction_operation_failed> err) mutable {
|
2311
|
-
if (err) {
|
2312
|
-
return op_completed_with_error(std::move(cb), *err);
|
2313
|
-
}
|
2314
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2315
|
-
this, "doc ok to overwrite, retrying create_staged_insert with cas {}", doc->cas().value());
|
2316
|
-
delay();
|
2317
|
-
return create_staged_insert(id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
|
2318
|
-
});
|
2319
|
-
} else {
|
2320
|
-
// no doc now, just retry entire txn
|
2321
|
-
CB_ATTEMPT_CTX_LOG_TRACE(this, "got {} from get_doc in exists during staged insert", *ec3);
|
2322
|
-
return op_completed_with_error(
|
2323
|
-
std::move(cb),
|
2324
|
-
transaction_operation_failed(FAIL_DOC_NOT_FOUND, "insert failed as the doc existed, but now seems to not exist")
|
2325
|
-
.retry());
|
2326
|
-
}
|
2327
|
-
} else {
|
2328
|
-
return error_handler(*ec3, *err_message, std::forward<Handler>(cb));
|
2338
|
+
return hooks_.before_get_doc_in_exists_during_staged_insert(
|
2339
|
+
this, id.key(), [this, id, content, op_id, cb = std::forward<Handler>(cb), error_handler, delay](auto ec) mutable {
|
2340
|
+
if (ec) {
|
2341
|
+
return error_handler(
|
2342
|
+
*ec, fmt::format("before_get_doc_in_exists_during_staged_insert hook raised {}", *ec), std::forward<Handler>(cb));
|
2329
2343
|
}
|
2344
|
+
return get_doc(
|
2345
|
+
id,
|
2346
|
+
[this, id, content, op_id, cb = std::forward<Handler>(cb), error_handler, delay](
|
2347
|
+
std::optional<error_class> ec3,
|
2348
|
+
std::optional<std::string> err_message,
|
2349
|
+
std::optional<transaction_get_result> doc) mutable {
|
2350
|
+
if (!ec3) {
|
2351
|
+
if (doc) {
|
2352
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this,
|
2353
|
+
"document {} exists, is_in_transaction {}, is_deleted {} ",
|
2354
|
+
doc->id(),
|
2355
|
+
doc->links().is_document_in_transaction(),
|
2356
|
+
doc->links().is_deleted());
|
2357
|
+
|
2358
|
+
if (auto err =
|
2359
|
+
forward_compat::check(forward_compat_stage::WWC_INSERTING_GET, doc->links().forward_compat());
|
2360
|
+
err) {
|
2361
|
+
return op_completed_with_error(std::forward<Handler>(cb), *err);
|
2362
|
+
}
|
2363
|
+
if (!doc->links().is_document_in_transaction() && doc->links().is_deleted()) {
|
2364
|
+
// it is just a deleted doc, so we are ok. Let's try again, but with the cas
|
2365
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2366
|
+
this, "create staged insert found existing deleted doc, retrying with cas {}", doc->cas().value());
|
2367
|
+
delay();
|
2368
|
+
return create_staged_insert(id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
|
2369
|
+
}
|
2370
|
+
if (!doc->links().is_document_in_transaction()) {
|
2371
|
+
// doc was inserted outside txn elsewhere
|
2372
|
+
CB_ATTEMPT_CTX_LOG_TRACE(this, "doc {} not in txn - was inserted outside txn", id);
|
2373
|
+
return op_completed_with_error(
|
2374
|
+
std::forward<Handler>(cb),
|
2375
|
+
document_exists(
|
2376
|
+
{ couchbase::errc::transaction_op::document_exists_exception, key_value_error_context() }));
|
2377
|
+
}
|
2378
|
+
if (doc->links().staged_attempt_id() == this->id()) {
|
2379
|
+
if (doc->links().staged_operation_id() == op_id) {
|
2380
|
+
// this is us dealing with resolving an ambiguity. So, lets just update the staged_mutation with
|
2381
|
+
// the correct cas and continue...
|
2382
|
+
staged_mutations_->add(staged_mutation(*doc, content, staged_mutation_type::INSERT));
|
2383
|
+
return op_completed_with_callback(std::forward<Handler>(cb), doc);
|
2384
|
+
}
|
2385
|
+
return op_completed_with_error(
|
2386
|
+
std::forward<Handler>(cb),
|
2387
|
+
transaction_operation_failed(FAIL_OTHER, "concurrent operations on a document are not allowed")
|
2388
|
+
.cause(CONCURRENT_OPERATIONS_DETECTED_ON_SAME_DOCUMENT));
|
2389
|
+
}
|
2390
|
+
// CBD-3787 - Only a staged insert is ok to overwrite
|
2391
|
+
if (doc->links().op() && *doc->links().op() != "insert") {
|
2392
|
+
return op_completed_with_error(
|
2393
|
+
std::forward<Handler>(cb),
|
2394
|
+
transaction_operation_failed(FAIL_DOC_ALREADY_EXISTS, "doc exists, not a staged insert")
|
2395
|
+
.cause(DOCUMENT_EXISTS_EXCEPTION));
|
2396
|
+
}
|
2397
|
+
check_and_handle_blocking_transactions(
|
2398
|
+
*doc,
|
2399
|
+
forward_compat_stage::WWC_INSERTING,
|
2400
|
+
[this, id, op_id, content, doc, cb = std::forward<Handler>(cb), delay](
|
2401
|
+
std::optional<transaction_operation_failed> err) mutable {
|
2402
|
+
if (err) {
|
2403
|
+
return op_completed_with_error(std::move(cb), *err);
|
2404
|
+
}
|
2405
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(
|
2406
|
+
this, "doc ok to overwrite, retrying create_staged_insert with cas {}", doc->cas().value());
|
2407
|
+
delay();
|
2408
|
+
return create_staged_insert(id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
|
2409
|
+
});
|
2410
|
+
} else {
|
2411
|
+
// no doc now, just retry entire txn
|
2412
|
+
CB_ATTEMPT_CTX_LOG_TRACE(this, "got {} from get_doc in exists during staged insert", *ec3);
|
2413
|
+
return op_completed_with_error(
|
2414
|
+
std::move(cb),
|
2415
|
+
transaction_operation_failed(FAIL_DOC_NOT_FOUND,
|
2416
|
+
"insert failed as the doc existed, but now seems to not exist")
|
2417
|
+
.retry());
|
2418
|
+
}
|
2419
|
+
} else {
|
2420
|
+
return error_handler(*ec3, *err_message, std::forward<Handler>(cb));
|
2421
|
+
}
|
2422
|
+
});
|
2330
2423
|
});
|
2331
|
-
break;
|
2332
2424
|
}
|
2333
2425
|
default:
|
2334
|
-
return op_completed_with_error(std::
|
2426
|
+
return op_completed_with_error(std::forward<Handler>(cb),
|
2427
|
+
transaction_operation_failed(ec, "failed in create_staged_insert").retry());
|
2335
2428
|
}
|
2336
2429
|
}
|
2337
2430
|
|
@@ -2356,7 +2449,9 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
2356
2449
|
"create_staged_insert expired and not in overtime");
|
2357
2450
|
}
|
2358
2451
|
|
2359
|
-
|
2452
|
+
auto ec =
|
2453
|
+
wait_for_hook([this, key = id.key()](auto handler) mutable { return hooks_.before_staged_insert(this, key, std::move(handler)); });
|
2454
|
+
if (ec) {
|
2360
2455
|
return create_staged_insert_error_handler(
|
2361
2456
|
id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb), *ec, "before_staged_insert hook threw error");
|
2362
2457
|
}
|
@@ -2371,43 +2466,43 @@ attempt_context_impl::create_staged_insert(const core::document_id& id,
|
|
2371
2466
|
req,
|
2372
2467
|
[this, id, content, cas, op_id, cb = std::forward<Handler>(cb), delay = std::forward<Delay>(delay)](
|
2373
2468
|
core::operations::mutate_in_response resp) mutable {
|
2374
|
-
auto ec =
|
2375
|
-
if (ec) {
|
2376
|
-
auto msg = (resp.ctx.ec() ? resp.ctx.ec().message() : "after_staged_insert hook threw error");
|
2469
|
+
if (auto ec = error_class_from_response(resp); ec) {
|
2377
2470
|
return create_staged_insert_error_handler(
|
2378
|
-
id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb), *ec,
|
2379
|
-
}
|
2380
|
-
if (!resp.ctx.ec()) {
|
2381
|
-
CB_ATTEMPT_CTX_LOG_DEBUG(this, "inserted doc {} CAS={}, {}", id, resp.cas.value(), resp.ctx.ec().message());
|
2382
|
-
|
2383
|
-
// TODO: clean this up (do most of this in transactions_document(...))
|
2384
|
-
transaction_links links(atr_id_->key(),
|
2385
|
-
id.bucket(),
|
2386
|
-
id.scope(),
|
2387
|
-
id.collection(),
|
2388
|
-
overall_.transaction_id(),
|
2389
|
-
this->id(),
|
2390
|
-
op_id,
|
2391
|
-
content,
|
2392
|
-
std::nullopt,
|
2393
|
-
std::nullopt,
|
2394
|
-
std::nullopt,
|
2395
|
-
std::nullopt,
|
2396
|
-
std::string("insert"),
|
2397
|
-
std::nullopt,
|
2398
|
-
true);
|
2399
|
-
transaction_get_result out(id, content, resp.cas.value(), links, std::nullopt);
|
2400
|
-
staged_mutations_->add(staged_mutation(out, content, staged_mutation_type::INSERT));
|
2401
|
-
return op_completed_with_callback(cb, std::optional<transaction_get_result>(out));
|
2471
|
+
id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb), *ec, resp.ctx.ec().message());
|
2402
2472
|
}
|
2403
|
-
return
|
2404
|
-
|
2405
|
-
|
2406
|
-
|
2407
|
-
|
2408
|
-
|
2409
|
-
|
2410
|
-
|
2473
|
+
return hooks_.after_staged_insert_complete(
|
2474
|
+
this,
|
2475
|
+
id.key(),
|
2476
|
+
[this, id, content, cas, op_id, cb = std::forward<Handler>(cb), delay = std::forward<Delay>(delay), resp = std::move(resp)](
|
2477
|
+
auto ec) mutable {
|
2478
|
+
if (ec) {
|
2479
|
+
auto msg = (resp.ctx.ec() ? resp.ctx.ec().message() : "after_staged_insert hook threw error");
|
2480
|
+
return create_staged_insert_error_handler(
|
2481
|
+
id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb), *ec, msg);
|
2482
|
+
}
|
2483
|
+
|
2484
|
+
CB_ATTEMPT_CTX_LOG_DEBUG(this, "inserted doc {} CAS={}, {}", id, resp.cas.value(), resp.ctx.ec().message());
|
2485
|
+
|
2486
|
+
// TODO: clean this up (do most of this in transactions_document(...))
|
2487
|
+
transaction_links links(atr_id_->key(),
|
2488
|
+
id.bucket(),
|
2489
|
+
id.scope(),
|
2490
|
+
id.collection(),
|
2491
|
+
overall_.transaction_id(),
|
2492
|
+
this->id(),
|
2493
|
+
op_id,
|
2494
|
+
content,
|
2495
|
+
std::nullopt,
|
2496
|
+
std::nullopt,
|
2497
|
+
std::nullopt,
|
2498
|
+
std::nullopt,
|
2499
|
+
std::string("insert"),
|
2500
|
+
std::nullopt,
|
2501
|
+
true);
|
2502
|
+
transaction_get_result out(id, content, resp.cas.value(), links, std::nullopt);
|
2503
|
+
staged_mutations_->add(staged_mutation(out, content, staged_mutation_type::INSERT));
|
2504
|
+
return op_completed_with_callback(cb, std::optional<transaction_get_result>(out));
|
2505
|
+
});
|
2411
2506
|
});
|
2412
2507
|
}
|
2413
2508
|
|