couchbase 3.0.0.beta.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +227 -0
- data/.rubocop_todo.yml +47 -0
- data/CONTRIBUTING.md +110 -0
- data/Gemfile +4 -0
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/couchbase.gemspec +40 -39
- data/examples/analytics.rb +123 -108
- data/examples/auth.rb +33 -0
- data/examples/crud.rb +16 -2
- data/examples/managing_analytics_indexes.rb +18 -4
- data/examples/managing_buckets.rb +17 -3
- data/examples/managing_collections.rb +22 -9
- data/examples/managing_query_indexes.rb +38 -18
- data/examples/managing_search_indexes.rb +21 -6
- data/examples/managing_view_indexes.rb +18 -4
- data/examples/query.rb +17 -3
- data/examples/query_with_consistency.rb +30 -20
- data/examples/search.rb +116 -101
- data/examples/search_with_consistency.rb +43 -30
- data/examples/subdocument.rb +42 -30
- data/examples/view.rb +19 -10
- data/ext/CMakeLists.txt +40 -2
- data/ext/build_version.hxx.in +1 -1
- data/ext/couchbase/bucket.hxx +190 -38
- data/ext/couchbase/cluster.hxx +22 -4
- data/ext/couchbase/configuration.hxx +14 -14
- data/ext/couchbase/couchbase.cxx +108 -12
- data/ext/couchbase/error_map.hxx +202 -2
- data/ext/couchbase/errors.hxx +8 -2
- data/ext/couchbase/io/dns_client.hxx +6 -6
- data/ext/couchbase/io/http_command.hxx +2 -2
- data/ext/couchbase/io/http_session.hxx +7 -11
- data/ext/couchbase/io/http_session_manager.hxx +3 -3
- data/ext/couchbase/io/mcbp_command.hxx +101 -44
- data/ext/couchbase/io/mcbp_session.hxx +144 -49
- data/ext/couchbase/io/retry_action.hxx +30 -0
- data/ext/couchbase/io/retry_context.hxx +39 -0
- data/ext/couchbase/io/retry_orchestrator.hxx +96 -0
- data/ext/couchbase/io/retry_reason.hxx +235 -0
- data/ext/couchbase/io/retry_strategy.hxx +156 -0
- data/ext/couchbase/operations/document_decrement.hxx +2 -0
- data/ext/couchbase/operations/document_exists.hxx +2 -0
- data/ext/couchbase/operations/document_get.hxx +2 -0
- data/ext/couchbase/operations/document_get_and_lock.hxx +2 -0
- data/ext/couchbase/operations/document_get_and_touch.hxx +2 -0
- data/ext/couchbase/operations/document_get_projected.hxx +2 -0
- data/ext/couchbase/operations/document_increment.hxx +2 -0
- data/ext/couchbase/operations/document_insert.hxx +2 -0
- data/ext/couchbase/operations/document_lookup_in.hxx +2 -0
- data/ext/couchbase/operations/document_mutate_in.hxx +3 -0
- data/ext/couchbase/operations/document_query.hxx +10 -0
- data/ext/couchbase/operations/document_remove.hxx +2 -0
- data/ext/couchbase/operations/document_replace.hxx +2 -0
- data/ext/couchbase/operations/document_search.hxx +8 -3
- data/ext/couchbase/operations/document_touch.hxx +2 -0
- data/ext/couchbase/operations/document_unlock.hxx +2 -0
- data/ext/couchbase/operations/document_upsert.hxx +2 -0
- data/ext/couchbase/operations/query_index_create.hxx +14 -4
- data/ext/couchbase/operations/query_index_drop.hxx +12 -2
- data/ext/couchbase/operations/query_index_get_all.hxx +11 -2
- data/ext/couchbase/origin.hxx +47 -17
- data/ext/couchbase/platform/backtrace.c +189 -0
- data/ext/couchbase/platform/backtrace.h +54 -0
- data/ext/couchbase/platform/terminate_handler.cc +122 -0
- data/ext/couchbase/platform/terminate_handler.h +36 -0
- data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +6 -1
- data/ext/couchbase/protocol/status.hxx +14 -4
- data/ext/couchbase/version.hxx +2 -2
- data/ext/extconf.rb +39 -36
- data/ext/test/main.cxx +64 -16
- data/lib/couchbase.rb +0 -1
- data/lib/couchbase/analytics_options.rb +2 -4
- data/lib/couchbase/authenticator.rb +14 -0
- data/lib/couchbase/binary_collection.rb +9 -9
- data/lib/couchbase/binary_collection_options.rb +8 -6
- data/lib/couchbase/bucket.rb +18 -18
- data/lib/couchbase/cluster.rb +121 -90
- data/lib/couchbase/collection.rb +36 -38
- data/lib/couchbase/collection_options.rb +31 -17
- data/lib/couchbase/common_options.rb +1 -1
- data/lib/couchbase/datastructures/couchbase_list.rb +16 -16
- data/lib/couchbase/datastructures/couchbase_map.rb +18 -18
- data/lib/couchbase/datastructures/couchbase_queue.rb +13 -13
- data/lib/couchbase/datastructures/couchbase_set.rb +8 -7
- data/lib/couchbase/errors.rb +10 -3
- data/lib/couchbase/json_transcoder.rb +2 -2
- data/lib/couchbase/management/analytics_index_manager.rb +37 -37
- data/lib/couchbase/management/bucket_manager.rb +25 -25
- data/lib/couchbase/management/collection_manager.rb +3 -3
- data/lib/couchbase/management/query_index_manager.rb +59 -14
- data/lib/couchbase/management/search_index_manager.rb +15 -12
- data/lib/couchbase/management/user_manager.rb +1 -1
- data/lib/couchbase/management/view_index_manager.rb +11 -5
- data/lib/couchbase/mutation_state.rb +12 -0
- data/lib/couchbase/query_options.rb +23 -9
- data/lib/couchbase/scope.rb +61 -1
- data/lib/couchbase/search_options.rb +40 -27
- data/lib/couchbase/subdoc.rb +31 -28
- data/lib/couchbase/version.rb +2 -2
- data/lib/couchbase/view_options.rb +0 -1
- metadata +22 -9
@@ -1,6 +1,20 @@
|
|
1
|
-
|
1
|
+
# Copyright 2020 Couchbase, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
2
14
|
|
3
|
-
|
15
|
+
require "couchbase"
|
16
|
+
|
17
|
+
include Couchbase # rubocop:disable Style/MixinUsage for brevity
|
4
18
|
|
5
19
|
options = Cluster::ClusterOptions.new
|
6
20
|
options.authenticate("Administrator", "password")
|
@@ -18,27 +32,27 @@ rescue Error::IndexNotFound
|
|
18
32
|
index.source_type = "couchbase"
|
19
33
|
index.source_name = bucket_name
|
20
34
|
index.params = {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
mapping: {
|
36
|
+
types: {
|
37
|
+
"knob" => {
|
38
|
+
properties: {
|
39
|
+
"name" => {
|
40
|
+
fields: [
|
41
|
+
{
|
42
|
+
name: "name",
|
43
|
+
type: "text",
|
44
|
+
include_in_all: true,
|
45
|
+
include_term_vectors: true,
|
46
|
+
index: true,
|
47
|
+
store: true,
|
48
|
+
docvalues: true,
|
49
|
+
},
|
50
|
+
],
|
51
|
+
},
|
52
|
+
},
|
53
|
+
},
|
54
|
+
},
|
55
|
+
},
|
42
56
|
}
|
43
57
|
|
44
58
|
cluster.search_indexes.upsert_index(index)
|
@@ -48,6 +62,7 @@ rescue Error::IndexNotFound
|
|
48
62
|
sleep(1)
|
49
63
|
num = cluster.search_indexes.get_indexed_documents_count(search_index_name)
|
50
64
|
break if num_indexed == num
|
65
|
+
|
51
66
|
num_indexed = num
|
52
67
|
puts "indexing #{search_index_name.inspect}: #{num_indexed} documents"
|
53
68
|
end
|
@@ -59,9 +74,9 @@ collection = cluster.bucket(bucket_name).default_collection
|
|
59
74
|
# and supply mutation tokens from those operations.
|
60
75
|
random_string = ("a".."z").to_a.sample(10).join
|
61
76
|
res = collection.upsert("user:#{random_string}", {
|
62
|
-
|
63
|
-
|
64
|
-
|
77
|
+
"name" => "Brass Doorknob",
|
78
|
+
"email" => "brass.doorknob@example.com",
|
79
|
+
"data" => random_string,
|
65
80
|
})
|
66
81
|
|
67
82
|
state = MutationState.new(res.mutation_token)
|
@@ -74,10 +89,8 @@ options.consistent_with(state)
|
|
74
89
|
res = cluster.search_query(search_index_name, query, options)
|
75
90
|
|
76
91
|
res.rows.each do |row|
|
77
|
-
if row.id == "user:#{random_string}"
|
78
|
-
|
79
|
-
end
|
80
|
-
if ENV['REMOVE_DOOR_KNOBS']
|
92
|
+
puts "--- Found our newly created document!" if row.id == "user:#{random_string}"
|
93
|
+
if ENV["REMOVE_DOOR_KNOBS"]
|
81
94
|
puts "Removing #{row.id} (requested via env)"
|
82
95
|
collection.remove(row.id)
|
83
96
|
end
|
data/examples/subdocument.rb
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Copyright 2020 Couchbase, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "couchbase"
|
16
|
+
include Couchbase # rubocop:disable Style/MixinUsage for brevity
|
3
17
|
|
4
18
|
options = Cluster::ClusterOptions.new
|
5
19
|
options.authenticate("Administrator", "password")
|
@@ -8,44 +22,42 @@ bucket = cluster.bucket("default")
|
|
8
22
|
collection = bucket.default_collection
|
9
23
|
|
10
24
|
document = {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
}
|
25
|
+
name: "Douglas Reynholm",
|
26
|
+
email: "douglas@reynholmindustries.com",
|
27
|
+
addresses: {
|
28
|
+
billing: {
|
29
|
+
line1: "123 Any Street",
|
30
|
+
line2: "Anytown",
|
31
|
+
country: "United Kingdom",
|
32
|
+
},
|
33
|
+
delivery: {
|
34
|
+
line1: "123 Any Street",
|
35
|
+
line2: "Anytown",
|
36
|
+
country: "United Kingdom",
|
24
37
|
},
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
38
|
+
},
|
39
|
+
purchases: {
|
40
|
+
complete: [339, 976, 442, 666],
|
41
|
+
abandoned: [157, 42, 999],
|
42
|
+
},
|
29
43
|
}
|
30
44
|
collection.upsert("customer123", document)
|
31
45
|
|
32
46
|
res = collection.mutate_in("customer123", [
|
33
|
-
|
34
|
-
])
|
47
|
+
MutateInSpec.upsert("fax", "311-555-0151"),
|
48
|
+
])
|
35
49
|
puts "The document has been modified successfully: cas=#{res.cas}"
|
36
50
|
|
37
51
|
res = collection.mutate_in("customer123", [
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
])
|
52
|
+
MutateInSpec.upsert("_framework.model_type", "Customer").xattr,
|
53
|
+
MutateInSpec.remove("addresses.billing[2]"),
|
54
|
+
MutateInSpec.replace("email", "dougr96@hotmail.com"),
|
55
|
+
])
|
42
56
|
puts "The document has been modified successfully: cas=#{res.cas}"
|
43
57
|
|
44
58
|
res = collection.lookup_in "customer123", [
|
45
|
-
|
46
|
-
|
59
|
+
LookupInSpec.get("addresses.delivery.country"),
|
60
|
+
LookupInSpec.exists("purchases.pending[-1]"),
|
47
61
|
]
|
48
62
|
puts "The customer's delivery country is #{res.content(0)}"
|
49
|
-
if res.exists?(1)
|
50
|
-
puts "The customer has pending purchases"
|
51
|
-
end
|
63
|
+
puts "The customer has pending purchases" if res.exists?(1)
|
data/examples/view.rb
CHANGED
@@ -1,8 +1,20 @@
|
|
1
|
-
#
|
1
|
+
# Copyright 2020 Couchbase, Inc.
|
2
2
|
#
|
3
|
-
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "couchbase"
|
4
16
|
|
5
|
-
include Couchbase
|
17
|
+
include Couchbase # rubocop:disable Style/MixinUsage for brevity
|
6
18
|
|
7
19
|
options = Cluster::ClusterOptions.new
|
8
20
|
options.authenticate("Administrator", "password")
|
@@ -26,17 +38,16 @@ options.order = :descending
|
|
26
38
|
res = bucket.view_query("beer", "brewery_beers", options)
|
27
39
|
puts "\nTotal documents in 'beer/brewery_beers' index: #{res.meta_data.total_rows}"
|
28
40
|
puts "Last #{options.limit} documents:"
|
29
|
-
res.rows.
|
41
|
+
res.rows.each do |row|
|
30
42
|
doc = collection.get(row.id)
|
31
43
|
puts "#{row.id} (type: #{doc.content['type']}, key: #{row.key})"
|
32
44
|
end
|
33
45
|
|
34
|
-
|
35
46
|
random_number = rand(0..1_000_000_000)
|
36
47
|
unique_brewery_id = "random_brewery:#{random_number}"
|
37
48
|
collection.upsert("random_brewery:#{random_number}", {
|
38
|
-
|
39
|
-
|
49
|
+
"name" => "Random brewery: #{random_number}",
|
50
|
+
"type" => "brewery",
|
40
51
|
})
|
41
52
|
puts "\nRequest with consistency. Generated brewery name: #{unique_brewery_id}"
|
42
53
|
options = Bucket::ViewOptions.new
|
@@ -44,7 +55,5 @@ options.start_key = ["random_brewery:"]
|
|
44
55
|
options.scan_consistency = :request_plus
|
45
56
|
res = bucket.view_query("beer", "brewery_beers", options)
|
46
57
|
res.rows.each do |row|
|
47
|
-
if row.id == unique_brewery_id
|
48
|
-
puts "Found newly created document: #{collection.get(row.id).content}"
|
49
|
-
end
|
58
|
+
puts "Found newly created document: #{collection.get(row.id).content}" if row.id == unique_brewery_id
|
50
59
|
end
|
data/ext/CMakeLists.txt
CHANGED
@@ -8,6 +8,36 @@ if(CCACHE)
|
|
8
8
|
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
|
9
9
|
endif()
|
10
10
|
|
11
|
+
include(CMakePushCheckState)
|
12
|
+
include(CheckSymbolExists)
|
13
|
+
|
14
|
+
cmake_push_check_state(RESET)
|
15
|
+
find_library(EXECINFO_LIBRARY NAMES execinfo)
|
16
|
+
if(EXECINFO_LIBRARY)
|
17
|
+
set(CMAKE_REQUIRED_LIBRARIES "${EXECINFO_LIBRARY}")
|
18
|
+
list(APPEND PLATFORM_LIBRARIES "${EXECINFO_LIBRARY}")
|
19
|
+
endif(EXECINFO_LIBRARY)
|
20
|
+
check_symbol_exists(backtrace execinfo.h HAVE_BACKTRACE)
|
21
|
+
cmake_pop_check_state()
|
22
|
+
|
23
|
+
cmake_push_check_state(RESET)
|
24
|
+
set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE")
|
25
|
+
find_library(DL_LIBRARY NAMES dl)
|
26
|
+
if(DL_LIBRARY)
|
27
|
+
set(CMAKE_REQUIRED_LIBRARIES "${DL_LIBRARY}")
|
28
|
+
list(APPEND PLATFORM_LIBRARIES "${DL_LIBRARY}")
|
29
|
+
endif(DL_LIBRARY)
|
30
|
+
check_symbol_exists(dladdr dlfcn.h HAVE_DLADDR)
|
31
|
+
cmake_pop_check_state()
|
32
|
+
|
33
|
+
if(HAVE_BACKTRACE)
|
34
|
+
add_definitions(-DHAVE_BACKTRACE=1)
|
35
|
+
endif()
|
36
|
+
|
37
|
+
if(HAVE_DLADDR)
|
38
|
+
add_definitions(-DHAVE_DLADDR=1)
|
39
|
+
endif()
|
40
|
+
|
11
41
|
add_subdirectory(third_party/gsl)
|
12
42
|
add_subdirectory(third_party/json)
|
13
43
|
add_subdirectory(third_party/spdlog)
|
@@ -90,6 +120,12 @@ else()
|
|
90
120
|
-Wuseless-cast # warn if you perform a cast to the same type
|
91
121
|
)
|
92
122
|
endif()
|
123
|
+
|
124
|
+
if(HAVE_BACKTRACE OR HAVE_DLADDR)
|
125
|
+
target_compile_options(project_options INTERFACE -ggdb3)
|
126
|
+
target_link_libraries(project_options INTERFACE -rdynamic)
|
127
|
+
add_definitions(-D_GNU_SOURCE=1)
|
128
|
+
endif()
|
93
129
|
endif()
|
94
130
|
|
95
131
|
# Read more at https://wiki.wireshark.org/TLS
|
@@ -102,8 +138,10 @@ message(STATUS "OPENSSL_LIBRARIES: ${OPENSSL_LIBRARIES}")
|
|
102
138
|
|
103
139
|
include_directories(${CMAKE_SOURCE_DIR}/couchbase)
|
104
140
|
|
105
|
-
add_library(
|
106
|
-
|
141
|
+
add_library(
|
142
|
+
platform OBJECT couchbase/platform/string_hex.cc couchbase/platform/uuid.cc couchbase/platform/random.cc
|
143
|
+
couchbase/platform/base64.cc couchbase/platform/backtrace.c couchbase/platform/terminate_handler.cc)
|
144
|
+
target_include_directories(platform PRIVATE ${PROJECT_BINARY_DIR}/generated)
|
107
145
|
|
108
146
|
add_library(cbcrypto OBJECT couchbase/cbcrypto/cbcrypto.cc)
|
109
147
|
|
data/ext/build_version.hxx.in
CHANGED
@@ -22,5 +22,5 @@
|
|
22
22
|
#define BACKEND_C_COMPILER "@CMAKE_C_COMPILER_ID@ @CMAKE_C_COMPILER_VERSION@"
|
23
23
|
#define BACKEND_SYSTEM "@CMAKE_SYSTEM@"
|
24
24
|
#define BACKEND_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@"
|
25
|
-
#define BACKEND_GIT_REVISION "
|
25
|
+
#define BACKEND_GIT_REVISION "a23527c044e32c037f4a001c2b2a836716d85d35"
|
26
26
|
|
data/ext/couchbase/bucket.hxx
CHANGED
@@ -42,6 +42,7 @@ class bucket : public std::enable_shared_from_this<bucket>
|
|
42
42
|
, origin_(std::move(origin))
|
43
43
|
, known_features_(known_features)
|
44
44
|
{
|
45
|
+
log_prefix_ = fmt::format("[{}/{}]", client_id_, name_);
|
45
46
|
}
|
46
47
|
|
47
48
|
~bucket()
|
@@ -54,6 +55,142 @@ class bucket : public std::enable_shared_from_this<bucket>
|
|
54
55
|
return name_;
|
55
56
|
}
|
56
57
|
|
58
|
+
/**
|
59
|
+
* copies nodes from rhs that are not in lhs to output vector
|
60
|
+
*/
|
61
|
+
static void diff_nodes(const std::vector<configuration::node>& lhs,
|
62
|
+
const std::vector<configuration::node>& rhs,
|
63
|
+
std::vector<configuration::node>& output)
|
64
|
+
{
|
65
|
+
for (const auto& re : rhs) {
|
66
|
+
bool known = false;
|
67
|
+
for (const auto& le : lhs) {
|
68
|
+
if (le.hostname == re.hostname && le.services_plain.management.value_or(0) == re.services_plain.management.value_or(0)) {
|
69
|
+
known = true;
|
70
|
+
break;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
if (!known) {
|
74
|
+
output.push_back(re);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
void update_config(const configuration& config)
|
80
|
+
{
|
81
|
+
if (!config_) {
|
82
|
+
spdlog::debug("{} initialize configuration rev={}", log_prefix_, config.rev);
|
83
|
+
} else if (config.rev > config_->rev) {
|
84
|
+
spdlog::debug("{} will update the configuration old={} -> new={}", log_prefix_, config_->rev, config.rev);
|
85
|
+
} else {
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
89
|
+
std::vector<configuration::node> added{};
|
90
|
+
std::vector<configuration::node> removed{};
|
91
|
+
if (config_) {
|
92
|
+
diff_nodes(config_->nodes, config.nodes, added);
|
93
|
+
diff_nodes(config.nodes, config_->nodes, removed);
|
94
|
+
} else {
|
95
|
+
added = config.nodes;
|
96
|
+
}
|
97
|
+
config_ = config;
|
98
|
+
if (!added.empty() || removed.empty()) {
|
99
|
+
std::map<size_t, std::shared_ptr<io::mcbp_session>> new_sessions{};
|
100
|
+
|
101
|
+
for (auto entry : sessions_) {
|
102
|
+
std::size_t new_index = config.nodes.size() + 1;
|
103
|
+
for (const auto& node : config.nodes) {
|
104
|
+
if (entry.second->bootstrap_hostname() == node.hostname_for(origin_.options().network) &&
|
105
|
+
entry.second->bootstrap_port() ==
|
106
|
+
std::to_string(node.port_or(origin_.options().network, service_type::kv, origin_.options().enable_tls, 0))) {
|
107
|
+
new_index = node.index;
|
108
|
+
break;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
if (new_index < config.nodes.size()) {
|
112
|
+
spdlog::debug(R"({} rev={}, preserve session="{}", address="{}:{}")",
|
113
|
+
log_prefix_,
|
114
|
+
config.rev,
|
115
|
+
entry.second->id(),
|
116
|
+
entry.second->bootstrap_hostname(),
|
117
|
+
entry.second->bootstrap_port());
|
118
|
+
new_sessions.emplace(new_index, std::move(entry.second));
|
119
|
+
} else {
|
120
|
+
spdlog::debug(R"({} rev={}, drop session="{}", address="{}:{}")",
|
121
|
+
log_prefix_,
|
122
|
+
config.rev,
|
123
|
+
entry.second->id(),
|
124
|
+
entry.second->bootstrap_hostname(),
|
125
|
+
entry.second->bootstrap_port());
|
126
|
+
entry.second.reset();
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
for (const auto& node : config.nodes) {
|
131
|
+
if (new_sessions.find(node.index) != new_sessions.end()) {
|
132
|
+
continue;
|
133
|
+
}
|
134
|
+
|
135
|
+
auto hostname = node.hostname_for(origin_.options().network);
|
136
|
+
auto port = node.port_or(origin_.options().network, service_type::kv, origin_.options().enable_tls, 0);
|
137
|
+
couchbase::origin origin(origin_.credentials(), hostname, port, origin_.options());
|
138
|
+
std::shared_ptr<io::mcbp_session> session;
|
139
|
+
if (origin_.options().enable_tls) {
|
140
|
+
session = std::make_shared<io::mcbp_session>(client_id_, ctx_, tls_, origin, name_, known_features_);
|
141
|
+
} else {
|
142
|
+
session = std::make_shared<io::mcbp_session>(client_id_, ctx_, origin, name_, known_features_);
|
143
|
+
}
|
144
|
+
spdlog::debug(R"({} rev={}, add session="{}", address="{}:{}")", log_prefix_, config.rev, session->id(), hostname, port);
|
145
|
+
session->bootstrap(
|
146
|
+
[self = shared_from_this(), session](std::error_code err, const configuration& cfg) {
|
147
|
+
if (!err) {
|
148
|
+
self->update_config(cfg);
|
149
|
+
session->on_configuration_update([self](const configuration& new_config) { self->update_config(new_config); });
|
150
|
+
session->on_stop([index = session->index(), self](io::retry_reason reason) {
|
151
|
+
if (reason == io::retry_reason::socket_closed_while_in_flight) {
|
152
|
+
self->restart_node(index);
|
153
|
+
}
|
154
|
+
});
|
155
|
+
}
|
156
|
+
},
|
157
|
+
true);
|
158
|
+
new_sessions.emplace(node.index, std::move(session));
|
159
|
+
}
|
160
|
+
sessions_ = new_sessions;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
void restart_node(std::size_t index)
|
165
|
+
{
|
166
|
+
auto ptr = sessions_.find(index);
|
167
|
+
if (ptr == sessions_.end()) {
|
168
|
+
spdlog::debug(R"({} requested to restart session idx={}, which does not exist, ignoring)", log_prefix_, index);
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
auto& old_session = ptr->second;
|
172
|
+
auto hostname = old_session->bootstrap_hostname();
|
173
|
+
auto port = old_session->bootstrap_port();
|
174
|
+
spdlog::debug(R"({} restarting session idx={}, id="{}", address="{}")", log_prefix_, index, old_session->id(), hostname, port);
|
175
|
+
couchbase::origin origin(origin_.credentials(), hostname, port, origin_.options());
|
176
|
+
sessions_.erase(ptr);
|
177
|
+
std::shared_ptr<io::mcbp_session> session;
|
178
|
+
if (origin_.options().enable_tls) {
|
179
|
+
session = std::make_shared<io::mcbp_session>(client_id_, ctx_, tls_, origin, name_, known_features_);
|
180
|
+
} else {
|
181
|
+
session = std::make_shared<io::mcbp_session>(client_id_, ctx_, origin, name_, known_features_);
|
182
|
+
}
|
183
|
+
session->bootstrap(
|
184
|
+
[self = shared_from_this(), session](std::error_code err, const configuration& config) {
|
185
|
+
if (!err) {
|
186
|
+
self->update_config(config);
|
187
|
+
session->on_configuration_update([self](const configuration& new_config) { self->update_config(new_config); });
|
188
|
+
}
|
189
|
+
},
|
190
|
+
true);
|
191
|
+
sessions_.emplace(index, std::move(session));
|
192
|
+
}
|
193
|
+
|
57
194
|
template<typename Handler>
|
58
195
|
void bootstrap(Handler&& handler)
|
59
196
|
{
|
@@ -66,52 +203,37 @@ class bucket : public std::enable_shared_from_this<bucket>
|
|
66
203
|
new_session->bootstrap([self = shared_from_this(), new_session, h = std::forward<Handler>(handler)](
|
67
204
|
std::error_code ec, const configuration& cfg) mutable {
|
68
205
|
if (!ec) {
|
69
|
-
self->config_ = cfg;
|
70
206
|
size_t this_index = new_session->index();
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
couchbase::origin origin(
|
76
|
-
self->origin_.get_username(),
|
77
|
-
self->origin_.get_password(),
|
78
|
-
n.hostname_for(self->origin_.options().network),
|
79
|
-
n.port_or(self->origin_.options().network, service_type::kv, self->origin_.options().enable_tls, 0),
|
80
|
-
self->origin_.options());
|
81
|
-
std::shared_ptr<io::mcbp_session> s;
|
82
|
-
if (self->origin_.options().enable_tls) {
|
83
|
-
s = std::make_shared<io::mcbp_session>(
|
84
|
-
self->client_id_, self->ctx_, self->tls_, origin, self->name_, self->known_features_);
|
85
|
-
} else {
|
86
|
-
s = std::make_shared<io::mcbp_session>(
|
87
|
-
self->client_id_, self->ctx_, origin, self->name_, self->known_features_);
|
88
|
-
}
|
89
|
-
s->bootstrap([host = n.hostname, bucket = self->name_](std::error_code err, const configuration& /*config*/) {
|
90
|
-
// TODO: retry, we know that auth is correct
|
91
|
-
if (err) {
|
92
|
-
spdlog::warn("unable to bootstrap node {} ({}): {}", host, bucket, err.message());
|
93
|
-
}
|
94
|
-
});
|
95
|
-
self->sessions_.emplace(n.index, std::move(s));
|
96
|
-
}
|
207
|
+
new_session->on_configuration_update([self](const configuration& config) { self->update_config(config); });
|
208
|
+
new_session->on_stop([this_index, self](io::retry_reason reason) {
|
209
|
+
if (reason == io::retry_reason::socket_closed_while_in_flight) {
|
210
|
+
self->restart_node(this_index);
|
97
211
|
}
|
98
|
-
}
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
212
|
+
});
|
213
|
+
|
214
|
+
self->sessions_.emplace(this_index, std::move(new_session));
|
215
|
+
self->update_config(cfg);
|
216
|
+
self->drain_deferred_queue();
|
103
217
|
}
|
104
218
|
h(ec, cfg);
|
105
219
|
});
|
106
220
|
}
|
107
221
|
|
222
|
+
void drain_deferred_queue()
|
223
|
+
{
|
224
|
+
while (!deferred_commands_.empty()) {
|
225
|
+
deferred_commands_.front()();
|
226
|
+
deferred_commands_.pop();
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
108
230
|
template<typename Request, typename Handler>
|
109
231
|
void execute(Request request, Handler&& handler)
|
110
232
|
{
|
111
233
|
if (closed_) {
|
112
234
|
return;
|
113
235
|
}
|
114
|
-
auto cmd = std::make_shared<operations::mcbp_command<Request>>(ctx_, request);
|
236
|
+
auto cmd = std::make_shared<operations::mcbp_command<bucket, Request>>(ctx_, shared_from_this(), request);
|
115
237
|
cmd->start([cmd, handler = std::forward<Handler>(handler)](std::error_code ec, std::optional<io::mcbp_message> msg) mutable {
|
116
238
|
using encoded_response_type = typename Request::encoded_response_type;
|
117
239
|
handler(make_response(ec, cmd->request, msg ? encoded_response_type(*msg) : encoded_response_type{}));
|
@@ -129,18 +251,46 @@ class bucket : public std::enable_shared_from_this<bucket>
|
|
129
251
|
return;
|
130
252
|
}
|
131
253
|
closed_ = true;
|
254
|
+
|
255
|
+
drain_deferred_queue();
|
132
256
|
for (auto& session : sessions_) {
|
133
|
-
session.second->stop();
|
257
|
+
session.second->stop(io::retry_reason::do_not_retry);
|
134
258
|
}
|
135
259
|
}
|
136
260
|
|
137
261
|
template<typename Request>
|
138
|
-
void map_and_send(std::shared_ptr<operations::mcbp_command<Request>> cmd)
|
262
|
+
void map_and_send(std::shared_ptr<operations::mcbp_command<bucket, Request>> cmd)
|
139
263
|
{
|
140
|
-
|
264
|
+
if (closed_) {
|
265
|
+
return cmd->cancel(io::retry_reason::do_not_retry);
|
266
|
+
}
|
267
|
+
std::int16_t index = 0;
|
141
268
|
std::tie(cmd->request.partition, index) = config_->map_key(cmd->request.id.key);
|
142
|
-
|
143
|
-
|
269
|
+
if (index < 0) {
|
270
|
+
return io::retry_orchestrator::maybe_retry(
|
271
|
+
cmd->manager_, cmd, io::retry_reason::node_not_available, std::make_error_code(error::common_errc::request_canceled));
|
272
|
+
}
|
273
|
+
cmd->send_to(sessions_.at(static_cast<std::size_t>(index)));
|
274
|
+
}
|
275
|
+
|
276
|
+
template<typename Request>
|
277
|
+
void schedule_for_retry(std::shared_ptr<operations::mcbp_command<bucket, Request>> cmd, std::chrono::milliseconds duration)
|
278
|
+
{
|
279
|
+
if (closed_) {
|
280
|
+
return cmd->cancel(io::retry_reason::do_not_retry);
|
281
|
+
}
|
282
|
+
cmd->retry_backoff.expires_after(duration);
|
283
|
+
cmd->retry_backoff.async_wait([self = shared_from_this(), cmd](std::error_code ec) mutable {
|
284
|
+
if (ec == asio::error::operation_aborted) {
|
285
|
+
return;
|
286
|
+
}
|
287
|
+
self->map_and_send(cmd);
|
288
|
+
});
|
289
|
+
}
|
290
|
+
|
291
|
+
[[nodiscard]] const std::string& log_prefix()
|
292
|
+
{
|
293
|
+
return log_prefix_;
|
144
294
|
}
|
145
295
|
|
146
296
|
private:
|
@@ -157,5 +307,7 @@ class bucket : public std::enable_shared_from_this<bucket>
|
|
157
307
|
|
158
308
|
bool closed_{ false };
|
159
309
|
std::map<size_t, std::shared_ptr<io::mcbp_session>> sessions_{};
|
310
|
+
|
311
|
+
std::string log_prefix_{};
|
160
312
|
};
|
161
313
|
} // namespace couchbase
|