couchbase 4.7.0-dev.1 → 4.7.1
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.
- package/deps/couchbase-cxx-cache/mozilla-ca-bundle.crt +3 -575
- package/deps/couchbase-cxx-cache/mozilla-ca-bundle.sha256 +1 -1
- package/deps/couchbase-cxx-client/CMakeLists.txt +3 -1
- package/deps/couchbase-cxx-client/README.md +2 -2
- package/deps/couchbase-cxx-client/core/error_context/base_error_context.hxx +32 -0
- package/deps/couchbase-cxx-client/core/error_context/key_value.cxx +1 -0
- package/deps/couchbase-cxx-client/core/error_context/key_value.hxx +23 -0
- package/deps/couchbase-cxx-client/core/error_context/key_value_error_context.hxx +35 -0
- package/deps/couchbase-cxx-client/core/error_context/subdocument_error_context.hxx +41 -0
- package/deps/couchbase-cxx-client/core/impl/binary_collection.cxx +123 -88
- package/deps/couchbase-cxx-client/core/impl/collection.cxx +416 -189
- package/deps/couchbase-cxx-client/core/impl/error.cxx +29 -4
- package/deps/couchbase-cxx-client/core/impl/invoke_with_node_id.hxx +44 -0
- package/deps/couchbase-cxx-client/core/impl/node_id.cxx +110 -0
- package/deps/couchbase-cxx-client/core/impl/node_id.hxx +40 -0
- package/deps/couchbase-cxx-client/core/io/configuration_belongs_to_session.hxx +67 -0
- package/deps/couchbase-cxx-client/core/io/http_session_manager.hxx +97 -57
- package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +7 -16
- package/deps/couchbase-cxx-client/core/operations/document_get_all_replicas.hxx +14 -4
- package/deps/couchbase-cxx-client/core/operations/document_get_projected.cxx +3 -4
- package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +4 -0
- package/deps/couchbase-cxx-client/core/operations/document_query.cxx +12 -12
- package/deps/couchbase-cxx-client/core/operations/management/collection_create.cxx +11 -11
- package/deps/couchbase-cxx-client/core/operations/management/collection_drop.cxx +11 -11
- package/deps/couchbase-cxx-client/core/operations/management/collection_update.cxx +11 -11
- package/deps/couchbase-cxx-client/core/operations/management/error_utils.cxx +9 -6
- package/deps/couchbase-cxx-client/core/operations/management/query_index_create.cxx +5 -4
- package/deps/couchbase-cxx-client/core/operations/management/query_index_drop.cxx +7 -6
- package/deps/couchbase-cxx-client/core/operations/management/scope_create.cxx +12 -13
- package/deps/couchbase-cxx-client/core/operations/management/scope_drop.cxx +8 -9
- package/deps/couchbase-cxx-client/core/topology/configuration.cxx +21 -0
- package/deps/couchbase-cxx-client/core/topology/configuration.hxx +28 -0
- package/deps/couchbase-cxx-client/core/utils/contains_string.cxx +61 -0
- package/deps/couchbase-cxx-client/core/utils/contains_string.hxx +26 -0
- package/deps/couchbase-cxx-client/couchbase/collection.hxx +73 -0
- package/deps/couchbase-cxx-client/couchbase/error.hxx +16 -0
- package/deps/couchbase-cxx-client/couchbase/node_id.hxx +123 -0
- package/deps/couchbase-cxx-client/couchbase/node_id_for_options.hxx +61 -0
- package/deps/couchbase-cxx-client/couchbase/node_ids_options.hxx +62 -0
- package/deps/couchbase-cxx-client/couchbase/result.hxx +42 -0
- package/dist/binding.d.ts +1 -0
- package/dist/bucket.d.ts +9 -1
- package/dist/bucket.js +21 -0
- package/dist/cluster.d.ts +10 -1
- package/dist/cluster.js +22 -0
- package/dist/collection.d.ts +3 -3
- package/dist/collection.js +161 -123
- package/dist/diagnosticsexecutor.js +2 -2
- package/dist/diagnosticstypes.d.ts +36 -0
- package/dist/diagnosticstypes.js +22 -1
- package/dist/observability.d.ts +5 -1
- package/dist/observability.js +11 -3
- package/dist/observabilityhandler.d.ts +6 -0
- package/dist/observabilityhandler.js +51 -14
- package/dist/observabilitytypes.js +19 -42
- package/dist/observabilityutilities.js +1 -1
- package/dist/oteltracer.d.ts +5 -0
- package/dist/oteltracer.js +7 -0
- package/dist/queryindexmanager.js +15 -0
- package/dist/thresholdlogging.d.ts +5 -0
- package/dist/thresholdlogging.js +7 -0
- package/dist/tracing.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/waituntilreadyexecutor.d.ts +37 -0
- package/dist/waituntilreadyexecutor.js +156 -0
- package/package.json +8 -8
- package/scripts/prebuilds.js +13 -0
- package/src/binding.cpp +1 -1
|
@@ -75,6 +75,17 @@ error::error(std::error_code ec,
|
|
|
75
75
|
{
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
error::error(std::error_code ec,
|
|
79
|
+
std::string message,
|
|
80
|
+
couchbase::error_context ctx,
|
|
81
|
+
couchbase::node_id node_id)
|
|
82
|
+
: ec_{ ec }
|
|
83
|
+
, message_{ std::move(message) }
|
|
84
|
+
, ctx_{ std::move(ctx) }
|
|
85
|
+
, node_id_{ std::move(node_id) }
|
|
86
|
+
{
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
auto
|
|
79
90
|
error::ec() const -> std::error_code
|
|
80
91
|
{
|
|
@@ -103,6 +114,12 @@ error::cause() const -> std::optional<error>
|
|
|
103
114
|
return *cause_;
|
|
104
115
|
}
|
|
105
116
|
|
|
117
|
+
auto
|
|
118
|
+
error::node_id() const -> const couchbase::node_id&
|
|
119
|
+
{
|
|
120
|
+
return node_id_;
|
|
121
|
+
}
|
|
122
|
+
|
|
106
123
|
error::operator bool() const
|
|
107
124
|
{
|
|
108
125
|
return ec_.value() != 0;
|
|
@@ -164,19 +181,27 @@ make_error(const core::error_context::http& core_ctx) -> couchbase::error
|
|
|
164
181
|
auto
|
|
165
182
|
make_error(const couchbase::core::key_value_error_context& core_ctx) -> couchbase::error
|
|
166
183
|
{
|
|
184
|
+
// Always preserve the node_id so that callers on both success and error
|
|
185
|
+
// paths can identify which node handled the request.
|
|
167
186
|
if (!core_ctx.ec()) {
|
|
168
|
-
return {};
|
|
187
|
+
return { {}, {}, {}, core_ctx.last_dispatched_to_node_id() };
|
|
169
188
|
}
|
|
170
|
-
return { core_ctx.ec(),
|
|
189
|
+
return { core_ctx.ec(),
|
|
190
|
+
{},
|
|
191
|
+
internal_error_context::build_error_context(core_ctx),
|
|
192
|
+
core_ctx.last_dispatched_to_node_id() };
|
|
171
193
|
}
|
|
172
194
|
|
|
173
195
|
auto
|
|
174
196
|
make_error(const couchbase::core::subdocument_error_context& core_ctx) -> couchbase::error
|
|
175
197
|
{
|
|
176
198
|
if (!core_ctx.ec()) {
|
|
177
|
-
return {};
|
|
199
|
+
return { {}, {}, {}, core_ctx.last_dispatched_to_node_id() };
|
|
178
200
|
}
|
|
179
|
-
return { core_ctx.ec(),
|
|
201
|
+
return { core_ctx.ec(),
|
|
202
|
+
{},
|
|
203
|
+
internal_error_context::build_error_context(core_ctx),
|
|
204
|
+
core_ctx.last_dispatched_to_node_id() };
|
|
180
205
|
}
|
|
181
206
|
|
|
182
207
|
auto
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2026-Present Couchbase, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#pragma once
|
|
19
|
+
|
|
20
|
+
#include <couchbase/error.hxx>
|
|
21
|
+
|
|
22
|
+
#include <utility>
|
|
23
|
+
|
|
24
|
+
namespace couchbase::core::impl
|
|
25
|
+
{
|
|
26
|
+
/**
|
|
27
|
+
* Forwards (err, result) to the caller's handler, propagating the node_id
|
|
28
|
+
* carried by the error (which make_error populates for KV contexts) onto
|
|
29
|
+
* the result. Using a plain function template avoids the extra std::function
|
|
30
|
+
* allocation a type-erasing wrapper would introduce for the sole purpose of
|
|
31
|
+
* setting node_id - that extra allocation was the source of the 18
|
|
32
|
+
* "Potential memory leak" reports clang-static-analyzer flagged against the
|
|
33
|
+
* previous wrap_with_node_id helper. The handler is taken by forwarding
|
|
34
|
+
* reference and perfect-forwarded to the invocation so that callers moving
|
|
35
|
+
* an rvalue handler consume it without an intermediate copy.
|
|
36
|
+
*/
|
|
37
|
+
template<typename Handler, typename Result>
|
|
38
|
+
void
|
|
39
|
+
invoke_with_node_id(Handler&& handler, couchbase::error err, Result result)
|
|
40
|
+
{
|
|
41
|
+
result.node_id(err.node_id());
|
|
42
|
+
std::forward<Handler>(handler)(std::move(err), std::move(result));
|
|
43
|
+
}
|
|
44
|
+
} // namespace couchbase::core::impl
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2024-Present Couchbase, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "node_id.hxx"
|
|
19
|
+
|
|
20
|
+
#include "core/utils/crc32.hxx"
|
|
21
|
+
|
|
22
|
+
#include <spdlog/fmt/bundled/core.h>
|
|
23
|
+
|
|
24
|
+
#include <cstdint>
|
|
25
|
+
#include <string>
|
|
26
|
+
|
|
27
|
+
namespace couchbase
|
|
28
|
+
{
|
|
29
|
+
|
|
30
|
+
namespace
|
|
31
|
+
{
|
|
32
|
+
/**
|
|
33
|
+
* Produces a stable, opaque hex string from hostname + KV port.
|
|
34
|
+
*
|
|
35
|
+
* Uses CRC32 (already in-tree for vBucket mapping) rather than a
|
|
36
|
+
* cryptographic hash — collision resistance across a handful of cluster
|
|
37
|
+
* nodes is more than sufficient, and this avoids pulling in OpenSSL headers.
|
|
38
|
+
*/
|
|
39
|
+
auto
|
|
40
|
+
derive_fallback_id(const std::string& hostname, std::uint16_t port) -> std::string
|
|
41
|
+
{
|
|
42
|
+
auto input = fmt::format("{}:{}", hostname, port);
|
|
43
|
+
auto crc = core::utils::hash_crc32(input.data(), input.size());
|
|
44
|
+
return fmt::format("{:08x}", crc);
|
|
45
|
+
}
|
|
46
|
+
} // namespace
|
|
47
|
+
|
|
48
|
+
node_id::node_id(std::string node_uuid, std::string hostname, std::uint16_t port)
|
|
49
|
+
: node_uuid_{ std::move(node_uuid) }
|
|
50
|
+
, hostname_{ std::move(hostname) }
|
|
51
|
+
, port_{ port }
|
|
52
|
+
, id_{ node_uuid_.empty() ? derive_fallback_id(hostname_, port_) : node_uuid_ }
|
|
53
|
+
{
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
auto
|
|
57
|
+
node_id::id() const -> const std::string&
|
|
58
|
+
{
|
|
59
|
+
return id_;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
auto
|
|
63
|
+
node_id::node_uuid() const -> const std::string&
|
|
64
|
+
{
|
|
65
|
+
return node_uuid_;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
auto
|
|
69
|
+
node_id::hostname() const -> const std::string&
|
|
70
|
+
{
|
|
71
|
+
return hostname_;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
auto
|
|
75
|
+
node_id::port() const -> std::uint16_t
|
|
76
|
+
{
|
|
77
|
+
return port_;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
node_id::
|
|
81
|
+
operator bool() const
|
|
82
|
+
{
|
|
83
|
+
return !id_.empty();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
auto
|
|
87
|
+
node_id::operator==(const node_id& other) const -> bool
|
|
88
|
+
{
|
|
89
|
+
return id_ == other.id_;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
auto
|
|
93
|
+
node_id::operator!=(const node_id& other) const -> bool
|
|
94
|
+
{
|
|
95
|
+
return !(*this == other);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
auto
|
|
99
|
+
node_id::operator<(const node_id& other) const -> bool
|
|
100
|
+
{
|
|
101
|
+
return id_ < other.id_;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
auto
|
|
105
|
+
internal_node_id::build(std::string node_uuid, std::string hostname, std::uint16_t port) -> node_id
|
|
106
|
+
{
|
|
107
|
+
return { std::move(node_uuid), std::move(hostname), port };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
} // namespace couchbase
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2024-Present Couchbase, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#pragma once
|
|
19
|
+
|
|
20
|
+
#include <couchbase/node_id.hxx>
|
|
21
|
+
|
|
22
|
+
#include <cstdint>
|
|
23
|
+
#include <string>
|
|
24
|
+
|
|
25
|
+
namespace couchbase
|
|
26
|
+
{
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Internal factory for constructing node_id instances from core-level data.
|
|
30
|
+
*
|
|
31
|
+
* This class is a friend of node_id and provides the only way to construct
|
|
32
|
+
* a non-default node_id outside the couchbase namespace.
|
|
33
|
+
*/
|
|
34
|
+
class internal_node_id
|
|
35
|
+
{
|
|
36
|
+
public:
|
|
37
|
+
static auto build(std::string node_uuid, std::string hostname, std::uint16_t port) -> node_id;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace couchbase
|
|
@@ -0,0 +1,67 @@
|
|
|
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 <optional>
|
|
21
|
+
#include <string>
|
|
22
|
+
|
|
23
|
+
namespace couchbase::core::io
|
|
24
|
+
{
|
|
25
|
+
/**
|
|
26
|
+
* Decide whether a freshly received cluster map belongs to this session and must
|
|
27
|
+
* therefore be accepted.
|
|
28
|
+
*
|
|
29
|
+
* A session's bucket binding is fixed for its entire lifetime (set once in the
|
|
30
|
+
* constructor, never reassigned), so the rule reduces to a single invariant:
|
|
31
|
+
* the bucket identity carried by the configuration must match the bucket identity
|
|
32
|
+
* of the session.
|
|
33
|
+
*
|
|
34
|
+
* - a cluster-level (bucket-less) session accepts only cluster-level configs;
|
|
35
|
+
* - a bucket-level session accepts only configs for *its own* bucket.
|
|
36
|
+
*
|
|
37
|
+
* @param config_bucket bucket name embedded in the configuration payload
|
|
38
|
+
* (std::nullopt => cluster-level / GCCCP config)
|
|
39
|
+
* @param session_bucket bucket this session is bound to
|
|
40
|
+
* (std::nullopt => cluster-level / GCCCP session)
|
|
41
|
+
* @return true if the configuration must be accepted, false if it must be ignored
|
|
42
|
+
*/
|
|
43
|
+
[[nodiscard]] inline auto
|
|
44
|
+
configuration_belongs_to_session(const std::optional<std::string>& config_bucket,
|
|
45
|
+
const std::optional<std::string>& session_bucket) -> bool
|
|
46
|
+
{
|
|
47
|
+
// Stage 1 — cluster-level config for a cluster-level session.
|
|
48
|
+
// Neither side names a bucket: this is a GCCCP (cluster) map and the session is
|
|
49
|
+
// not bound to any bucket, so the config belongs here. Accept.
|
|
50
|
+
if (!config_bucket.has_value() && !session_bucket.has_value()) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Stage 2 — cardinality mismatch: exactly one side names a bucket. Reject.
|
|
55
|
+
// Either a bucket-less config arrived on a bucket-bound session, or a bucket
|
|
56
|
+
// config arrived on a cluster-level session. In both cases the config does not
|
|
57
|
+
// belong to this session.
|
|
58
|
+
if (config_bucket.has_value() != session_bucket.has_value()) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Stage 3 — both sides name a bucket: accept only if it is the *same* bucket.
|
|
63
|
+
// Reaching here guarantees both optionals are engaged, so value() is safe.
|
|
64
|
+
// Guards against applying another bucket's map to this session.
|
|
65
|
+
return config_bucket.value() == session_bucket.value();
|
|
66
|
+
}
|
|
67
|
+
} // namespace couchbase::core::io
|
|
@@ -251,45 +251,45 @@ public:
|
|
|
251
251
|
app_telemetry_meter_,
|
|
252
252
|
options_.default_timeout_for(request.type));
|
|
253
253
|
#endif
|
|
254
|
-
cmd->start(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
254
|
+
cmd->start(
|
|
255
|
+
[start = std::chrono::steady_clock::now(),
|
|
256
|
+
self = shared_from_this(),
|
|
257
|
+
type,
|
|
258
|
+
cmd,
|
|
259
|
+
handler = collector->build_reporter()](operations::http_noop_response&& resp) {
|
|
260
|
+
diag::ping_state state = diag::ping_state::ok;
|
|
261
|
+
std::optional<std::string> error{};
|
|
262
|
+
if (auto ec = resp.ctx.ec; ec) {
|
|
263
|
+
if (ec == errc::common::unambiguous_timeout ||
|
|
264
|
+
ec == errc::common::ambiguous_timeout) {
|
|
265
|
+
state = diag::ping_state::timeout;
|
|
266
|
+
} else {
|
|
267
|
+
state = diag::ping_state::error;
|
|
268
|
+
}
|
|
269
|
+
error.emplace(fmt::format("code={}, message={}, http_code={}",
|
|
270
|
+
ec.value(),
|
|
271
|
+
ec.message(),
|
|
272
|
+
resp.ctx.http_status));
|
|
268
273
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
state,
|
|
289
|
-
{},
|
|
290
|
-
error });
|
|
291
|
-
self->check_in(type, cmd->session_);
|
|
292
|
-
});
|
|
274
|
+
auto remote_address = cmd->session_->remote_address();
|
|
275
|
+
// If not connected, the remote address will be empty. Better to
|
|
276
|
+
// give the user some context on the "attempted" remote address.
|
|
277
|
+
if (remote_address.empty()) {
|
|
278
|
+
remote_address =
|
|
279
|
+
fmt::format("{}:{}", cmd->session_->hostname(), cmd->session_->port());
|
|
280
|
+
}
|
|
281
|
+
handler->report(
|
|
282
|
+
diag::endpoint_ping_info{ type,
|
|
283
|
+
cmd->session_->id(),
|
|
284
|
+
std::chrono::duration_cast<std::chrono::microseconds>(
|
|
285
|
+
std::chrono::steady_clock::now() - start),
|
|
286
|
+
remote_address,
|
|
287
|
+
cmd->session_->local_address(),
|
|
288
|
+
state,
|
|
289
|
+
{},
|
|
290
|
+
error });
|
|
291
|
+
self->check_in(type, cmd->session_);
|
|
292
|
+
});
|
|
293
293
|
|
|
294
294
|
cmd->set_command_session(session);
|
|
295
295
|
if (!session->is_connected()) {
|
|
@@ -402,9 +402,20 @@ public:
|
|
|
402
402
|
return;
|
|
403
403
|
}
|
|
404
404
|
if (!session->is_connected()) {
|
|
405
|
-
|
|
406
|
-
|
|
405
|
+
{
|
|
406
|
+
std::scoped_lock lock(sessions_mutex_);
|
|
407
|
+
if (auto pend_it = pending_sessions_.find(type); pend_it != pending_sessions_.end()) {
|
|
408
|
+
pend_it->second.remove_if([id = session->id()](const auto& s) {
|
|
409
|
+
return !s || s->id() == id;
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
CB_LOG_DEBUG("{} HTTP session never connected. Ensured session is not in pending.",
|
|
414
|
+
session->log_prefix());
|
|
415
|
+
return;
|
|
407
416
|
}
|
|
417
|
+
bool should_stop = false;
|
|
418
|
+
std::chrono::milliseconds idle_timeout{};
|
|
408
419
|
{
|
|
409
420
|
std::scoped_lock lock(config_mutex_);
|
|
410
421
|
if (!session->keep_alive() || !config_.has_node(options_.network,
|
|
@@ -412,22 +423,34 @@ public:
|
|
|
412
423
|
options_.enable_tls,
|
|
413
424
|
session->hostname(),
|
|
414
425
|
session->port())) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
426
|
+
should_stop = true;
|
|
427
|
+
} else {
|
|
428
|
+
idle_timeout = options_.idle_http_connection_timeout;
|
|
418
429
|
}
|
|
419
430
|
}
|
|
431
|
+
if (should_stop) {
|
|
432
|
+
return asio::post(session->get_executor(), [session]() {
|
|
433
|
+
session->stop();
|
|
434
|
+
});
|
|
435
|
+
}
|
|
420
436
|
if (!session->is_stopped()) {
|
|
421
|
-
|
|
437
|
+
// set_idle() arms the idle timer via async_wait — it never calls stop() inline,
|
|
438
|
+
// so on_stop cannot re-enter sessions_mutex_ here. It must precede publication to
|
|
439
|
+
// idle_sessions_ so a concurrent check_out's reset_idle() finds a pending timer.
|
|
440
|
+
session->set_idle(idle_timeout);
|
|
422
441
|
CB_LOG_DEBUG("{} put HTTP session back to idle connections", session->log_prefix());
|
|
423
442
|
std::scoped_lock lock(sessions_mutex_);
|
|
424
443
|
idle_sessions_[type].push_back(session);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
444
|
+
if (auto busy_it = busy_sessions_.find(type); busy_it != busy_sessions_.end()) {
|
|
445
|
+
busy_it->second.remove_if([id = session->id()](const auto& s) -> bool {
|
|
446
|
+
return !s || s->id() == id;
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (auto pend_it = pending_sessions_.find(type); pend_it != pending_sessions_.end()) {
|
|
450
|
+
pend_it->second.remove_if([id = session->id()](const auto& s) -> bool {
|
|
451
|
+
return !s || s->id() == id;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
431
454
|
}
|
|
432
455
|
}
|
|
433
456
|
|
|
@@ -741,13 +764,30 @@ private:
|
|
|
741
764
|
}
|
|
742
765
|
|
|
743
766
|
session->on_stop([type, id = session->id(), self = this->shared_from_this()]() {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
767
|
+
// Declared outside the lock so destructors run after sessions_mutex_ is released.
|
|
768
|
+
// cppcheck-suppress variableScope
|
|
769
|
+
std::vector<std::shared_ptr<http_session>> dropped;
|
|
770
|
+
{
|
|
771
|
+
const std::scoped_lock inner_lock(self->sessions_mutex_);
|
|
772
|
+
for (auto* map :
|
|
773
|
+
{ &self->busy_sessions_, &self->idle_sessions_, &self->pending_sessions_ }) {
|
|
774
|
+
auto map_it = map->find(type);
|
|
775
|
+
if (map_it == map->end()) {
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
auto& list = map_it->second;
|
|
779
|
+
for (auto it = list.begin(); it != list.end();) {
|
|
780
|
+
if (!*it || (*it)->id() == id) {
|
|
781
|
+
if (*it) {
|
|
782
|
+
dropped.push_back(std::move(*it));
|
|
783
|
+
}
|
|
784
|
+
it = list.erase(it);
|
|
785
|
+
} else {
|
|
786
|
+
++it;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
751
791
|
});
|
|
752
792
|
return session;
|
|
753
793
|
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
23
23
|
#include "core/columnar/background_bootstrap_listener.hxx"
|
|
24
24
|
#endif
|
|
25
|
+
#include "configuration_belongs_to_session.hxx"
|
|
25
26
|
#include "core/config_listener.hxx"
|
|
26
27
|
#include "core/diagnostics.hxx"
|
|
27
28
|
#include "core/impl/bootstrap_error.hxx"
|
|
@@ -634,14 +635,9 @@ class mcbp_session_impl
|
|
|
634
635
|
}
|
|
635
636
|
}
|
|
636
637
|
std::optional<topology::configuration> config = req.body().config();
|
|
637
|
-
if (session_ && config.has_value()
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
// TODO(CXXCBC-549)
|
|
641
|
-
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
642
|
-
session_->bucket_name_.value() == req.body().bucket())) {
|
|
643
|
-
session_->update_configuration(std::move(config.value()));
|
|
644
|
-
}
|
|
638
|
+
if (session_ && config.has_value() &&
|
|
639
|
+
configuration_belongs_to_session(config->bucket, session_->bucket_name_)) {
|
|
640
|
+
session_->update_configuration(std::move(config.value()));
|
|
645
641
|
}
|
|
646
642
|
} break;
|
|
647
643
|
default:
|
|
@@ -824,14 +820,9 @@ class mcbp_session_impl
|
|
|
824
820
|
}
|
|
825
821
|
}
|
|
826
822
|
std::optional<topology::configuration> config = req.body().config();
|
|
827
|
-
if (session_ && config.has_value()
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
// TODO(CXXCBC-549)
|
|
831
|
-
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
832
|
-
session_->bucket_name_.value() == req.body().bucket())) {
|
|
833
|
-
session_->update_configuration(std::move(config.value()));
|
|
834
|
-
}
|
|
823
|
+
if (session_ && config.has_value() &&
|
|
824
|
+
configuration_belongs_to_session(config->bucket, session_->bucket_name_)) {
|
|
825
|
+
session_->update_configuration(std::move(config.value()));
|
|
835
826
|
}
|
|
836
827
|
} break;
|
|
837
828
|
default:
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#pragma once
|
|
19
19
|
|
|
20
20
|
#include <couchbase/error_codes.hxx>
|
|
21
|
+
#include <couchbase/node_id.hxx>
|
|
21
22
|
|
|
22
23
|
#include "core/error_context/key_value.hxx"
|
|
23
24
|
#include "core/impl/get_replica.hxx"
|
|
@@ -40,6 +41,7 @@ struct get_all_replicas_response {
|
|
|
40
41
|
couchbase::cas cas{};
|
|
41
42
|
std::uint32_t flags{};
|
|
42
43
|
bool replica{ true };
|
|
44
|
+
couchbase::node_id dispatched_to_node_id{};
|
|
43
45
|
};
|
|
44
46
|
key_value_error_context ctx{};
|
|
45
47
|
std::vector<entry> entries{};
|
|
@@ -156,8 +158,12 @@ struct get_all_replicas_request {
|
|
|
156
158
|
return;
|
|
157
159
|
}
|
|
158
160
|
} else {
|
|
159
|
-
ctx->result_.emplace_back(
|
|
160
|
-
std::move(resp.value),
|
|
161
|
+
ctx->result_.emplace_back(
|
|
162
|
+
get_all_replicas_response::entry{ std::move(resp.value),
|
|
163
|
+
resp.cas,
|
|
164
|
+
resp.flags,
|
|
165
|
+
true /* replica */,
|
|
166
|
+
resp.ctx.last_dispatched_to_node_id() });
|
|
161
167
|
}
|
|
162
168
|
if (ctx->expected_responses_ == 0) {
|
|
163
169
|
ctx->done_ = true;
|
|
@@ -203,8 +209,12 @@ struct get_all_replicas_request {
|
|
|
203
209
|
return;
|
|
204
210
|
}
|
|
205
211
|
} else {
|
|
206
|
-
ctx->result_.emplace_back(
|
|
207
|
-
std::move(resp.value),
|
|
212
|
+
ctx->result_.emplace_back(
|
|
213
|
+
get_all_replicas_response::entry{ std::move(resp.value),
|
|
214
|
+
resp.cas,
|
|
215
|
+
resp.flags,
|
|
216
|
+
false /* active */,
|
|
217
|
+
resp.ctx.last_dispatched_to_node_id() });
|
|
208
218
|
}
|
|
209
219
|
if (ctx->expected_responses_ == 0) {
|
|
210
220
|
ctx->done_ = true;
|
|
@@ -210,14 +210,13 @@ get_projected_request::make_response(key_value_error_context&& ctx,
|
|
|
210
210
|
response.ctx.override_ec(errc::common::parsing_failure);
|
|
211
211
|
return response;
|
|
212
212
|
}
|
|
213
|
-
tao::json::value new_doc;
|
|
213
|
+
tao::json::value new_doc = tao::json::empty_object;
|
|
214
214
|
for (const auto& projection : projections) {
|
|
215
215
|
if (auto value_to_apply = subdoc_lookup(full_doc, projection)) {
|
|
216
216
|
subdoc_apply_projection(new_doc, projection, *value_to_apply, preserve_array_indexes);
|
|
217
|
-
} else {
|
|
218
|
-
response.ctx.override_ec(errc::key_value::path_not_found);
|
|
219
|
-
return response;
|
|
220
217
|
}
|
|
218
|
+
// We ignore paths that were not found, similar to how we ignore them in the subdoc
|
|
219
|
+
// multi-lookup below.
|
|
221
220
|
}
|
|
222
221
|
response.value = utils::json::generate_binary(new_doc);
|
|
223
222
|
}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
#include <couchbase/codec/encoded_value.hxx>
|
|
30
30
|
#include <couchbase/error_codes.hxx>
|
|
31
|
+
#include <couchbase/node_id.hxx>
|
|
31
32
|
|
|
32
33
|
#include <functional>
|
|
33
34
|
#include <memory>
|
|
@@ -50,6 +51,7 @@ struct lookup_in_all_replicas_response {
|
|
|
50
51
|
couchbase::cas cas{};
|
|
51
52
|
bool deleted{ false };
|
|
52
53
|
bool is_replica{ true };
|
|
54
|
+
couchbase::node_id dispatched_to_node_id{};
|
|
53
55
|
};
|
|
54
56
|
subdocument_error_context ctx{};
|
|
55
57
|
std::vector<entry> entries{};
|
|
@@ -203,6 +205,7 @@ struct lookup_in_all_replicas_request {
|
|
|
203
205
|
top_entry.cas = resp.cas;
|
|
204
206
|
top_entry.deleted = resp.deleted;
|
|
205
207
|
top_entry.is_replica = true;
|
|
208
|
+
top_entry.dispatched_to_node_id = resp.ctx.last_dispatched_to_node_id();
|
|
206
209
|
for (auto& field : resp.fields) {
|
|
207
210
|
lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
|
|
208
211
|
lookup_in_entry.path = field.path;
|
|
@@ -267,6 +270,7 @@ struct lookup_in_all_replicas_request {
|
|
|
267
270
|
top_entry.cas = resp.cas;
|
|
268
271
|
top_entry.deleted = resp.deleted;
|
|
269
272
|
top_entry.is_replica = false;
|
|
273
|
+
top_entry.dispatched_to_node_id = resp.ctx.last_dispatched_to_node_id();
|
|
270
274
|
for (auto& field : resp.fields) {
|
|
271
275
|
lookup_in_all_replicas_response::entry::lookup_in_entry lookup_in_entry{};
|
|
272
276
|
lookup_in_entry.path = field.path;
|