couchbase 4.2.6-dev.1 → 4.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. package/CONTRIBUTING.md +97 -0
  2. package/deps/couchbase-cxx-client/.github/workflows/windows.yml +0 -3
  3. package/deps/couchbase-cxx-client/CMakeLists.txt +4 -0
  4. package/deps/couchbase-cxx-client/bin/build-tests.rb +1 -1
  5. package/deps/couchbase-cxx-client/core/impl/lookup_in.cxx +1 -0
  6. package/deps/couchbase-cxx-client/core/impl/lookup_in_all_replicas.cxx +176 -0
  7. package/deps/couchbase-cxx-client/core/impl/lookup_in_all_replicas.hxx +80 -0
  8. package/deps/couchbase-cxx-client/core/impl/lookup_in_any_replica.cxx +167 -0
  9. package/deps/couchbase-cxx-client/core/impl/lookup_in_any_replica.hxx +75 -0
  10. package/deps/couchbase-cxx-client/core/impl/lookup_in_replica.cxx +97 -0
  11. package/deps/couchbase-cxx-client/core/impl/lookup_in_replica.hxx +67 -0
  12. package/deps/couchbase-cxx-client/core/io/dns_client.cxx +48 -10
  13. package/deps/couchbase-cxx-client/core/io/http_session.hxx +24 -1
  14. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +22 -1
  15. package/deps/couchbase-cxx-client/core/logger/custom_rotating_file_sink.cxx +1 -1
  16. package/deps/couchbase-cxx-client/core/logger/logger.cxx +80 -20
  17. package/deps/couchbase-cxx-client/core/logger/logger.hxx +31 -0
  18. package/deps/couchbase-cxx-client/core/meta/features.hxx +19 -0
  19. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  20. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +188 -0
  21. package/deps/couchbase-cxx-client/core/operations.hxx +2 -0
  22. package/deps/couchbase-cxx-client/core/protocol/cmd_hello.hxx +1 -0
  23. package/deps/couchbase-cxx-client/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  24. package/deps/couchbase-cxx-client/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  25. package/deps/couchbase-cxx-client/core/protocol/hello_feature.hxx +6 -0
  26. package/deps/couchbase-cxx-client/core/protocol/hello_feature_fmt.hxx +3 -0
  27. package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +22 -1
  28. package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +2 -0
  29. package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +6 -0
  30. package/deps/couchbase-cxx-client/core/topology/configuration.hxx +10 -0
  31. package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +4 -1
  32. package/deps/couchbase-cxx-client/couchbase/collection.hxx +111 -0
  33. package/deps/couchbase-cxx-client/couchbase/get_and_lock_options.hxx +2 -2
  34. package/deps/couchbase-cxx-client/couchbase/get_and_touch_options.hxx +2 -2
  35. package/deps/couchbase-cxx-client/couchbase/get_options.hxx +2 -2
  36. package/deps/couchbase-cxx-client/couchbase/insert_options.hxx +3 -3
  37. package/deps/couchbase-cxx-client/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  38. package/deps/couchbase-cxx-client/couchbase/lookup_in_any_replica_options.hxx +101 -0
  39. package/deps/couchbase-cxx-client/couchbase/lookup_in_options.hxx +2 -2
  40. package/deps/couchbase-cxx-client/couchbase/lookup_in_replica_result.hxx +74 -0
  41. package/deps/couchbase-cxx-client/couchbase/lookup_in_result.hxx +26 -0
  42. package/deps/couchbase-cxx-client/couchbase/mutate_in_options.hxx +2 -2
  43. package/deps/couchbase-cxx-client/couchbase/remove_options.hxx +2 -2
  44. package/deps/couchbase-cxx-client/couchbase/replace_options.hxx +3 -3
  45. package/deps/couchbase-cxx-client/couchbase/touch_options.hxx +2 -2
  46. package/deps/couchbase-cxx-client/couchbase/unlock_options.hxx +2 -2
  47. package/deps/couchbase-cxx-client/couchbase/upsert_options.hxx +3 -3
  48. package/deps/couchbase-cxx-client/docs/cbc-analytics.md +3 -2
  49. package/deps/couchbase-cxx-client/docs/cbc-get.md +3 -2
  50. package/deps/couchbase-cxx-client/docs/cbc-pillowfight.md +3 -2
  51. package/deps/couchbase-cxx-client/docs/cbc-query.md +3 -2
  52. package/deps/couchbase-cxx-client/test/test_integration_subdoc.cxx +655 -0
  53. package/deps/couchbase-cxx-client/test/utils/logger.cxx +7 -0
  54. package/deps/couchbase-cxx-client/tools/utils.cxx +9 -2
  55. package/dist/binding.d.ts +47 -0
  56. package/dist/collection.d.ts +53 -1
  57. package/dist/collection.js +139 -1
  58. package/dist/couchbase.d.ts +15 -0
  59. package/dist/couchbase.js +22 -1
  60. package/dist/crudoptypes.d.ts +24 -0
  61. package/dist/crudoptypes.js +14 -1
  62. package/package.json +13 -13
  63. package/src/binding.cpp +28 -0
  64. package/src/connection.cpp +4 -0
  65. package/src/connection.hpp +2 -0
  66. package/src/connection_autogen.cpp +28 -0
  67. package/src/jstocbpp_autogen.hpp +262 -0
@@ -0,0 +1,192 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "core/error_context/key_value.hxx"
21
+ #include "core/impl/lookup_in_replica.hxx"
22
+ #include "core/impl/subdoc/command.hxx"
23
+ #include "core/operations/document_lookup_in.hxx"
24
+ #include "core/operations/operation_traits.hxx"
25
+ #include "core/utils/movable_function.hxx"
26
+ #include "couchbase/codec/encoded_value.hxx"
27
+ #include "couchbase/error_codes.hxx"
28
+
29
+ #include <functional>
30
+ #include <memory>
31
+ #include <mutex>
32
+
33
+ namespace couchbase::core::operations
34
+ {
35
+ struct lookup_in_all_replicas_response {
36
+ struct entry {
37
+ struct lookup_in_entry {
38
+ std::string path;
39
+ codec::binary value;
40
+ std::size_t original_index;
41
+ bool exists;
42
+ protocol::subdoc_opcode opcode;
43
+ key_value_status_code status;
44
+ std::error_code ec{};
45
+ };
46
+ std::vector<lookup_in_entry> fields{};
47
+ couchbase::cas cas{};
48
+ bool deleted{ false };
49
+ bool is_replica{ true };
50
+ };
51
+ subdocument_error_context ctx{};
52
+ std::vector<entry> entries{};
53
+ };
54
+
55
+ struct lookup_in_all_replicas_request {
56
+ using response_type = lookup_in_all_replicas_response;
57
+ using encoded_request_type = core::protocol::client_request<core::protocol::lookup_in_replica_request_body>;
58
+ using encoded_response_type = core::protocol::client_response<core::protocol::lookup_in_replica_response_body>;
59
+
60
+ core::document_id id;
61
+ std::vector<couchbase::core::impl::subdoc::command> specs{};
62
+ std::optional<std::chrono::milliseconds> timeout{};
63
+ std::shared_ptr<couchbase::tracing::request_span> parent_span{ nullptr };
64
+
65
+ template<typename Core, typename Handler>
66
+ void execute(Core core, Handler handler)
67
+ {
68
+ core->with_bucket_configuration(
69
+ id.bucket(),
70
+ [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
71
+ std::error_code ec, const topology::configuration& config) mutable {
72
+ if (!config.supports_subdoc_read_replica()) {
73
+ ec = errc::common::feature_not_available;
74
+ }
75
+
76
+ if (ec) {
77
+ std::optional<std::string> first_error_path{};
78
+ std::optional<std::size_t> first_error_index{};
79
+ return h(response_type{
80
+ make_subdocument_error_context(make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
81
+ }
82
+ using handler_type = utils::movable_function<void(response_type)>;
83
+
84
+ struct replica_context {
85
+ replica_context(handler_type handler, std::uint32_t expected_responses)
86
+ : handler_(std::move(handler))
87
+ , expected_responses_(expected_responses)
88
+ {
89
+ }
90
+
91
+ handler_type handler_;
92
+ std::uint32_t expected_responses_;
93
+ bool done_{ false };
94
+ std::mutex mutex_{};
95
+ std::vector<lookup_in_all_replicas_response::entry> result_{};
96
+ };
97
+ auto ctx = std::make_shared<replica_context>(std::move(h), config.num_replicas.value_or(0U) + 1U);
98
+
99
+ for (std::size_t idx = 1U; idx <= config.num_replicas.value_or(0U); ++idx) {
100
+ document_id replica_id{ id };
101
+ replica_id.node_index(idx);
102
+ core->execute(impl::lookup_in_replica_request{ std::move(replica_id), specs, timeout, parent_span },
103
+ [ctx](impl::lookup_in_replica_response&& resp) {
104
+ handler_type local_handler{};
105
+ {
106
+ std::scoped_lock lock(ctx->mutex_);
107
+ if (ctx->done_) {
108
+ return;
109
+ }
110
+ --ctx->expected_responses_;
111
+ if (resp.ctx.ec()) {
112
+ if (ctx->expected_responses_ > 0) {
113
+ // just ignore the response
114
+ return;
115
+ }
116
+ } else {
117
+ lookup_in_all_replicas_response::entry top_entry{};
118
+ top_entry.cas = resp.cas;
119
+ top_entry.deleted = resp.deleted;
120
+ top_entry.is_replica = true;
121
+ for (auto& field : resp.fields) {
122
+ lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
123
+ lookup_in_entry.path = field.path;
124
+ lookup_in_entry.value = field.value;
125
+ lookup_in_entry.status = field.status;
126
+ lookup_in_entry.ec = field.ec;
127
+ lookup_in_entry.exists = field.exists;
128
+ lookup_in_entry.original_index = field.original_index;
129
+ lookup_in_entry.opcode = field.opcode;
130
+ top_entry.fields.emplace_back(lookup_in_entry);
131
+ }
132
+ ctx->result_.emplace_back(lookup_in_all_replicas_response::entry{ top_entry });
133
+ }
134
+ if (ctx->expected_responses_ == 0) {
135
+ ctx->done_ = true;
136
+ std::swap(local_handler, ctx->handler_);
137
+ }
138
+ }
139
+ if (local_handler) {
140
+ return local_handler({ std::move(resp.ctx), std::move(ctx->result_) });
141
+ }
142
+ });
143
+ }
144
+
145
+ core->execute(lookup_in_request{ document_id{ id }, {}, {}, false, specs, timeout }, [ctx](lookup_in_response&& resp) {
146
+ handler_type local_handler{};
147
+ {
148
+ std::scoped_lock lock(ctx->mutex_);
149
+ if (ctx->done_) {
150
+ return;
151
+ }
152
+ --ctx->expected_responses_;
153
+ if (resp.ctx.ec()) {
154
+ if (ctx->expected_responses_ > 0) {
155
+ // just ignore the response
156
+ return;
157
+ }
158
+ } else {
159
+ lookup_in_all_replicas_response::entry top_entry{};
160
+ top_entry.cas = resp.cas;
161
+ top_entry.deleted = resp.deleted;
162
+ top_entry.is_replica = false;
163
+ for (auto& field : resp.fields) {
164
+ lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
165
+ lookup_in_entry.path = field.path;
166
+ lookup_in_entry.value = field.value;
167
+ lookup_in_entry.status = field.status;
168
+ lookup_in_entry.ec = field.ec;
169
+ lookup_in_entry.exists = field.exists;
170
+ lookup_in_entry.original_index = field.original_index;
171
+ lookup_in_entry.opcode = field.opcode;
172
+ top_entry.fields.emplace_back(lookup_in_entry);
173
+ }
174
+ ctx->result_.emplace_back(lookup_in_all_replicas_response::entry{ top_entry });
175
+ }
176
+ if (ctx->expected_responses_ == 0) {
177
+ ctx->done_ = true;
178
+ std::swap(local_handler, ctx->handler_);
179
+ }
180
+ }
181
+ if (local_handler) {
182
+ return local_handler({ std::move(resp.ctx), std::move(ctx->result_) });
183
+ }
184
+ });
185
+ });
186
+ }
187
+ };
188
+
189
+ template<>
190
+ struct is_compound_operation<lookup_in_all_replicas_request> : public std::true_type {
191
+ };
192
+ } // namespace couchbase::core::operations
@@ -0,0 +1,188 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "core/error_context/key_value.hxx"
21
+ #include "core/impl/lookup_in_replica.hxx"
22
+ #include "core/impl/subdoc/command.hxx"
23
+ #include "core/operations/document_lookup_in.hxx"
24
+ #include "core/operations/operation_traits.hxx"
25
+ #include "core/utils/movable_function.hxx"
26
+ #include "couchbase/codec/encoded_value.hxx"
27
+ #include "couchbase/error_codes.hxx"
28
+
29
+ #include <functional>
30
+ #include <memory>
31
+ #include <mutex>
32
+
33
+ namespace couchbase::core::operations
34
+ {
35
+ struct lookup_in_any_replica_response {
36
+ struct entry {
37
+ std::string path;
38
+ codec::binary value;
39
+ std::size_t original_index;
40
+ bool exists;
41
+ protocol::subdoc_opcode opcode;
42
+ key_value_status_code status;
43
+ std::error_code ec{};
44
+ };
45
+ subdocument_error_context ctx{};
46
+ couchbase::cas cas{};
47
+ std::vector<entry> fields{};
48
+ bool deleted{ false };
49
+ bool is_replica{ true };
50
+ };
51
+
52
+ struct lookup_in_any_replica_request {
53
+ using response_type = lookup_in_any_replica_response;
54
+ using encoded_request_type = core::protocol::client_request<core::protocol::lookup_in_replica_request_body>;
55
+ using encoded_response_type = core::protocol::client_response<core::protocol::lookup_in_replica_response_body>;
56
+
57
+ core::document_id id;
58
+ std::vector<couchbase::core::impl::subdoc::command> specs{};
59
+ std::optional<std::chrono::milliseconds> timeout{};
60
+ std::shared_ptr<couchbase::tracing::request_span> parent_span{ nullptr };
61
+
62
+ template<typename Core, typename Handler>
63
+ void execute(Core core, Handler handler)
64
+ {
65
+ core->with_bucket_configuration(
66
+ id.bucket(),
67
+ [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
68
+ std::error_code ec, const topology::configuration& config) mutable {
69
+ if (!config.supports_subdoc_read_replica()) {
70
+ ec = errc::common::feature_not_available;
71
+ }
72
+ if (specs.size() > 16) {
73
+ ec = errc::common::invalid_argument;
74
+ }
75
+
76
+ if (ec) {
77
+ std::optional<std::string> first_error_path{};
78
+ std::optional<std::size_t> first_error_index{};
79
+ return h(response_type{
80
+ make_subdocument_error_context(make_key_value_error_context(ec, id), ec, first_error_path, first_error_index, false) });
81
+ }
82
+ using handler_type = utils::movable_function<void(response_type)>;
83
+
84
+ struct replica_context {
85
+ replica_context(handler_type&& handler, std::uint32_t expected_responses)
86
+ : handler_(std::move(handler))
87
+ , expected_responses_(expected_responses)
88
+ {
89
+ }
90
+
91
+ handler_type handler_;
92
+ std::uint32_t expected_responses_;
93
+ bool done_{ false };
94
+ std::mutex mutex_{};
95
+ };
96
+ auto ctx = std::make_shared<replica_context>(std::move(h), config.num_replicas.value_or(0U) + 1U);
97
+
98
+ for (std::size_t idx = 1U; idx <= config.num_replicas.value_or(0U); ++idx) {
99
+ document_id replica_id{ id };
100
+ replica_id.node_index(idx);
101
+ core->execute(impl::lookup_in_replica_request{ std::move(replica_id), specs, timeout, parent_span },
102
+ [ctx](impl::lookup_in_replica_response&& resp) {
103
+ handler_type local_handler;
104
+ {
105
+ std::scoped_lock lock(ctx->mutex_);
106
+ if (ctx->done_) {
107
+ return;
108
+ }
109
+ --ctx->expected_responses_;
110
+ if (resp.ctx.ec()) {
111
+ if (ctx->expected_responses_ > 0) {
112
+ // just ignore the response
113
+ return;
114
+ }
115
+ // consider document irretrievable and give up
116
+ resp.ctx.override_ec(errc::key_value::document_irretrievable);
117
+ }
118
+ ctx->done_ = true;
119
+ std::swap(local_handler, ctx->handler_);
120
+ }
121
+ if (local_handler) {
122
+ response_type res{};
123
+ res.ctx = resp.ctx;
124
+ res.cas = resp.cas;
125
+ res.deleted = resp.deleted;
126
+ res.is_replica = true;
127
+ for (auto& field : resp.fields) {
128
+ auto lookup_in_entry = lookup_in_any_replica_response::entry{};
129
+ lookup_in_entry.path = field.path;
130
+ lookup_in_entry.value = field.value;
131
+ lookup_in_entry.status = field.status;
132
+ lookup_in_entry.ec = field.ec;
133
+ lookup_in_entry.exists = field.exists;
134
+ lookup_in_entry.original_index = field.original_index;
135
+ lookup_in_entry.opcode = field.opcode;
136
+ res.fields.emplace_back(lookup_in_entry);
137
+ }
138
+ return local_handler(res);
139
+ }
140
+ });
141
+ }
142
+ core->execute(lookup_in_request{ id, {}, {}, false, specs, timeout }, [ctx](lookup_in_response&& resp) {
143
+ handler_type local_handler{};
144
+ {
145
+ std::scoped_lock lock(ctx->mutex_);
146
+ if (ctx->done_) {
147
+ return;
148
+ }
149
+ --ctx->expected_responses_;
150
+ if (resp.ctx.ec()) {
151
+ if (ctx->expected_responses_ > 0) {
152
+ // just ignore the response
153
+ return;
154
+ }
155
+ // consider document irretrievable and give up
156
+ resp.ctx.override_ec(errc::key_value::document_irretrievable);
157
+ }
158
+ ctx->done_ = true;
159
+ std::swap(local_handler, ctx->handler_);
160
+ }
161
+ if (local_handler) {
162
+ auto res = response_type{};
163
+ res.ctx = resp.ctx;
164
+ res.cas = resp.cas;
165
+ res.deleted = resp.deleted;
166
+ res.is_replica = false;
167
+ for (auto& field : resp.fields) {
168
+ auto lookup_in_entry = lookup_in_any_replica_response::entry{};
169
+ lookup_in_entry.path = field.path;
170
+ lookup_in_entry.value = field.value;
171
+ lookup_in_entry.status = field.status;
172
+ lookup_in_entry.ec = field.ec;
173
+ lookup_in_entry.exists = field.exists;
174
+ lookup_in_entry.original_index = field.original_index;
175
+ lookup_in_entry.opcode = field.opcode;
176
+ res.fields.emplace_back(lookup_in_entry);
177
+ }
178
+ return local_handler(res);
179
+ }
180
+ });
181
+ });
182
+ }
183
+ };
184
+
185
+ template<>
186
+ struct is_compound_operation<lookup_in_any_replica_request> : public std::true_type {
187
+ };
188
+ } // namespace couchbase::core::operations
@@ -30,6 +30,8 @@
30
30
  #include "core/operations/document_increment.hxx"
31
31
  #include "core/operations/document_insert.hxx"
32
32
  #include "core/operations/document_lookup_in.hxx"
33
+ #include "core/operations/document_lookup_in_all_replicas.hxx"
34
+ #include "core/operations/document_lookup_in_any_replica.hxx"
33
35
  #include "core/operations/document_mutate_in.hxx"
34
36
  #include "core/operations/document_prepend.hxx"
35
37
  #include "core/operations/document_query.hxx"
@@ -71,6 +71,7 @@ class hello_request_body
71
71
  hello_feature::collections,
72
72
  hello_feature::subdoc_create_as_deleted,
73
73
  hello_feature::preserve_ttl,
74
+ hello_feature::subdoc_replica_read,
74
75
  };
75
76
  std::vector<std::byte> value_;
76
77
 
@@ -0,0 +1,107 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "cmd_lookup_in_replica.hxx"
19
+
20
+ #include "core/utils/byteswap.hxx"
21
+ #include "core/utils/unsigned_leb128.hxx"
22
+
23
+ #include <cstring>
24
+ #include <gsl/assert>
25
+
26
+ namespace couchbase::core::protocol
27
+ {
28
+ bool
29
+ lookup_in_replica_response_body::parse(key_value_status_code status,
30
+ const header_buffer& header,
31
+ std::uint8_t framing_extras_size,
32
+ std::uint16_t key_size,
33
+ std::uint8_t extras_size,
34
+ const std::vector<std::byte>& body,
35
+ const cmd_info& /* info */)
36
+ {
37
+ Expects(header[1] == static_cast<std::byte>(opcode));
38
+ if (status == key_value_status_code::success || status == key_value_status_code::subdoc_multi_path_failure ||
39
+ status == key_value_status_code::subdoc_success_deleted || status == key_value_status_code::subdoc_multi_path_failure_deleted) {
40
+ using offset_type = std::vector<std::byte>::difference_type;
41
+ offset_type offset = framing_extras_size + key_size + extras_size;
42
+ fields_.reserve(16); /* we won't have more than 16 entries anyway */
43
+ while (static_cast<std::size_t>(offset) < body.size()) {
44
+ lookup_in_field field;
45
+
46
+ std::uint16_t entry_status = 0;
47
+ memcpy(&entry_status, body.data() + offset, sizeof(entry_status));
48
+ entry_status = utils::byte_swap(entry_status);
49
+ Expects(is_valid_status(entry_status));
50
+ field.status = static_cast<key_value_status_code>(entry_status);
51
+ offset += static_cast<offset_type>(sizeof(entry_status));
52
+
53
+ std::uint32_t entry_size = 0;
54
+ memcpy(&entry_size, body.data() + offset, sizeof(entry_size));
55
+ entry_size = utils::byte_swap(entry_size);
56
+ Expects(entry_size < 20 * 1024 * 1024);
57
+ offset += static_cast<offset_type>(sizeof(entry_size));
58
+
59
+ field.value.resize(entry_size);
60
+ memcpy(field.value.data(), body.data() + offset, entry_size);
61
+ offset += static_cast<offset_type>(entry_size);
62
+
63
+ fields_.emplace_back(field);
64
+ }
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+
70
+ void
71
+ lookup_in_replica_request_body::id(const document_id& id)
72
+ {
73
+ key_ = make_protocol_key(id);
74
+ }
75
+
76
+ void
77
+ lookup_in_replica_request_body::fill_extras()
78
+ {
79
+ if (flags_ != 0) {
80
+ extras_.resize(sizeof(flags_));
81
+ extras_[0] = std::byte{ flags_ };
82
+ }
83
+ }
84
+
85
+ void
86
+ lookup_in_replica_request_body::fill_value()
87
+ {
88
+ size_t value_size = 0;
89
+ for (const auto& spec : specs_) {
90
+ value_size += sizeof(spec.opcode_) + sizeof(std::uint8_t) + sizeof(std::uint16_t) + spec.path_.size();
91
+ }
92
+ Expects(value_size > 0);
93
+ value_.resize(value_size);
94
+ std::vector<std::byte>::size_type offset = 0;
95
+ for (const auto& spec : specs_) {
96
+ value_[offset] = static_cast<std::byte>(spec.opcode_);
97
+ ++offset;
98
+ value_[offset] = spec.flags_;
99
+ ++offset;
100
+ std::uint16_t path_size = utils::byte_swap(gsl::narrow_cast<std::uint16_t>(spec.path_.size()));
101
+ std::memcpy(value_.data() + offset, &path_size, sizeof(path_size));
102
+ offset += sizeof(path_size);
103
+ std::memcpy(value_.data() + offset, spec.path_.data(), spec.path_.size());
104
+ offset += spec.path_.size();
105
+ }
106
+ }
107
+ } // namespace couchbase::core::protocol
@@ -0,0 +1,137 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "client_opcode.hxx"
21
+ #include "cmd_info.hxx"
22
+ #include "core/document_id.hxx"
23
+ #include "core/impl/subdoc/command.hxx"
24
+ #include "core/io/mcbp_message.hxx"
25
+ #include "status.hxx"
26
+
27
+ #include <gsl/assert>
28
+
29
+ namespace couchbase::core::protocol
30
+ {
31
+
32
+ class lookup_in_replica_response_body
33
+ {
34
+ public:
35
+ static const inline client_opcode opcode = client_opcode::subdoc_multi_lookup;
36
+
37
+ struct lookup_in_field {
38
+ key_value_status_code status{};
39
+ std::string value;
40
+ };
41
+
42
+ private:
43
+ std::vector<lookup_in_field> fields_{};
44
+
45
+ public:
46
+ [[nodiscard]] const std::vector<lookup_in_field>& fields() const
47
+ {
48
+ return fields_;
49
+ }
50
+
51
+ [[nodiscard]] bool parse(key_value_status_code status,
52
+ const header_buffer& header,
53
+ std::uint8_t framing_extras_size,
54
+ std::uint16_t key_size,
55
+ std::uint8_t extras_size,
56
+ const std::vector<std::byte>& body,
57
+ const cmd_info& info);
58
+ };
59
+
60
+ class lookup_in_replica_request_body
61
+ {
62
+ public:
63
+ using response_body_type = lookup_in_replica_response_body;
64
+ static const inline client_opcode opcode = client_opcode::subdoc_multi_lookup;
65
+
66
+ /**
67
+ * Tells the server to operate on replica vbucket instead of active
68
+ */
69
+ static const inline std::uint8_t doc_flag_replica_read = 0b0010'0000;
70
+
71
+ private:
72
+ std::vector<std::byte> key_;
73
+ std::vector<std::byte> extras_{};
74
+ std::vector<std::byte> value_{};
75
+
76
+ std::uint8_t flags_{ 0 };
77
+ std::vector<couchbase::core::impl::subdoc::command> specs_;
78
+
79
+ public:
80
+ void id(const document_id& id);
81
+
82
+ void read_replica(bool value)
83
+ {
84
+ if (value) {
85
+ flags_ = flags_ | doc_flag_replica_read;
86
+ }
87
+ }
88
+
89
+ void specs(const std::vector<couchbase::core::impl::subdoc::command>& specs)
90
+ {
91
+ specs_ = specs;
92
+ }
93
+
94
+ [[nodiscard]] const auto& key() const
95
+ {
96
+ return key_;
97
+ }
98
+
99
+ [[nodiscard]] const auto& framing_extras() const
100
+ {
101
+ return empty_buffer;
102
+ }
103
+
104
+ [[nodiscard]] const auto& extras()
105
+ {
106
+ if (extras_.empty()) {
107
+ fill_extras();
108
+ }
109
+ return extras_;
110
+ }
111
+
112
+ [[nodiscard]] const auto& value()
113
+ {
114
+ if (value_.empty()) {
115
+ fill_value();
116
+ }
117
+ return value_;
118
+ }
119
+
120
+ [[nodiscard]] std::size_t size()
121
+ {
122
+ if (extras_.empty()) {
123
+ fill_extras();
124
+ }
125
+ if (value_.empty()) {
126
+ fill_value();
127
+ }
128
+ return key_.size() + extras_.size() + value_.size();
129
+ }
130
+
131
+ private:
132
+ void fill_extras();
133
+
134
+ void fill_value();
135
+ };
136
+
137
+ } // namespace couchbase::core::protocol
@@ -155,6 +155,11 @@ enum class hello_feature : std::uint16_t {
155
155
  replace_body_with_xattr = 0x19,
156
156
 
157
157
  resource_units = 0x1a,
158
+
159
+ /**
160
+ * Indicates support for subdoc lookup operations on replicas
161
+ */
162
+ subdoc_replica_read = 0x1c,
158
163
  };
159
164
 
160
165
  constexpr bool
@@ -185,6 +190,7 @@ is_valid_hello_feature(std::uint16_t code)
185
190
  case hello_feature::subdoc_document_macro_support:
186
191
  case hello_feature::replace_body_with_xattr:
187
192
  case hello_feature::resource_units:
193
+ case hello_feature::subdoc_replica_read:
188
194
  return true;
189
195
  }
190
196
  return false;
@@ -106,6 +106,9 @@ struct fmt::formatter<couchbase::core::protocol::hello_feature> {
106
106
  case couchbase::core::protocol::hello_feature::resource_units:
107
107
  name = "resource_units";
108
108
  break;
109
+ case couchbase::core::protocol::hello_feature::subdoc_replica_read:
110
+ name = "subdoc_replica_read";
111
+ break;
109
112
  }
110
113
  return format_to(ctx.out(), "{}", name);
111
114
  }