couchbase 3.0.0.beta.1-universal-darwin-19 → 3.0.0-universal-darwin-19
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/libcouchbase.bundle +0 -0
- 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 +20 -7
@@ -0,0 +1,30 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020 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 <chrono>
|
21
|
+
#include <optional>
|
22
|
+
|
23
|
+
namespace couchbase::io
|
24
|
+
{
|
25
|
+
struct retry_action {
|
26
|
+
bool retry_requested{ false };
|
27
|
+
std::chrono::milliseconds duration{};
|
28
|
+
};
|
29
|
+
|
30
|
+
} // namespace couchbase::io
|
@@ -0,0 +1,39 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020 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 <set>
|
21
|
+
#include <chrono>
|
22
|
+
|
23
|
+
#include <io/retry_reason.hxx>
|
24
|
+
#include <io/retry_strategy.hxx>
|
25
|
+
|
26
|
+
namespace couchbase::io
|
27
|
+
{
|
28
|
+
|
29
|
+
template<class RetryStrategy>
|
30
|
+
struct retry_context
|
31
|
+
{
|
32
|
+
bool idempotent;
|
33
|
+
int retry_attempts{ 0 };
|
34
|
+
std::chrono::milliseconds last_duration{ 0 };
|
35
|
+
std::set<retry_reason> reasons{};
|
36
|
+
RetryStrategy strategy{};
|
37
|
+
};
|
38
|
+
|
39
|
+
} // namespace couchbase::io
|
@@ -0,0 +1,96 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020 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 <spdlog/spdlog.h>
|
21
|
+
|
22
|
+
#include <io/retry_reason.hxx>
|
23
|
+
#include <io/retry_action.hxx>
|
24
|
+
|
25
|
+
namespace couchbase::io::retry_orchestrator
|
26
|
+
{
|
27
|
+
|
28
|
+
namespace priv
|
29
|
+
{
|
30
|
+
template<class Command>
|
31
|
+
std::chrono::milliseconds
|
32
|
+
cap_duration(std::chrono::milliseconds uncapped, std::shared_ptr<Command> command)
|
33
|
+
{
|
34
|
+
auto theoretical_deadline = std::chrono::steady_clock::now() + uncapped;
|
35
|
+
auto absolute_deadline = command->deadline.expiry();
|
36
|
+
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(theoretical_deadline - absolute_deadline);
|
37
|
+
if (delta.count() > 0) {
|
38
|
+
auto capped = uncapped - delta;
|
39
|
+
if (capped.count() < 0) {
|
40
|
+
return uncapped; // something went wrong, return the uncapped one as a safety net
|
41
|
+
}
|
42
|
+
return capped;
|
43
|
+
}
|
44
|
+
return uncapped;
|
45
|
+
}
|
46
|
+
|
47
|
+
std::chrono::milliseconds
|
48
|
+
controlled_backoff(int retry_attempts)
|
49
|
+
{
|
50
|
+
switch (retry_attempts) {
|
51
|
+
case 0:
|
52
|
+
return std::chrono::milliseconds(1);
|
53
|
+
case 1:
|
54
|
+
return std::chrono::milliseconds(10);
|
55
|
+
case 2:
|
56
|
+
return std::chrono::milliseconds(50);
|
57
|
+
case 3:
|
58
|
+
return std::chrono::milliseconds(100);
|
59
|
+
case 4:
|
60
|
+
return std::chrono::milliseconds(500);
|
61
|
+
default:
|
62
|
+
return std::chrono::milliseconds(1000);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
template<class Manager, class Command>
|
67
|
+
void
|
68
|
+
retry_with_duration(std::shared_ptr<Manager> manager,
|
69
|
+
std::shared_ptr<Command> command,
|
70
|
+
retry_reason reason,
|
71
|
+
std::chrono::milliseconds duration)
|
72
|
+
{
|
73
|
+
++command->request.retries.retry_attempts;
|
74
|
+
command->request.retries.reasons.insert(reason);
|
75
|
+
command->request.retries.last_duration = duration;
|
76
|
+
manager->schedule_for_retry(command, duration);
|
77
|
+
}
|
78
|
+
|
79
|
+
} // namespace priv
|
80
|
+
|
81
|
+
template<class Manager, class Command>
|
82
|
+
void
|
83
|
+
maybe_retry(std::shared_ptr<Manager> manager, std::shared_ptr<Command> command, retry_reason reason, std::error_code ec)
|
84
|
+
{
|
85
|
+
if (always_retry(reason)) {
|
86
|
+
return priv::retry_with_duration(manager, command, reason, priv::controlled_backoff(command->request.retries.retry_attempts));
|
87
|
+
}
|
88
|
+
|
89
|
+
retry_action action = command->request.retries.strategy.should_retry(command->request, reason);
|
90
|
+
if (action.retry_requested) {
|
91
|
+
return priv::retry_with_duration(manager, command, reason, priv::cap_duration(action.duration, command));
|
92
|
+
}
|
93
|
+
return command->invoke_handler(ec);
|
94
|
+
}
|
95
|
+
|
96
|
+
} // namespace couchbase::io::retry_orchestrator
|
@@ -0,0 +1,235 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020 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 <spdlog/fmt/fmt.h>
|
21
|
+
|
22
|
+
namespace couchbase::io
|
23
|
+
{
|
24
|
+
enum class retry_reason {
|
25
|
+
/**
|
26
|
+
* default value, e.g. when we don't need to retry
|
27
|
+
*/
|
28
|
+
do_not_retry,
|
29
|
+
|
30
|
+
/**
|
31
|
+
* All unexpected/unknown retry errors must not be retried to avoid accidental data loss and non-deterministic behavior.
|
32
|
+
*/
|
33
|
+
unknown,
|
34
|
+
|
35
|
+
/**
|
36
|
+
* The socket is not available into which the operation should’ve been written.
|
37
|
+
*/
|
38
|
+
socket_not_available,
|
39
|
+
|
40
|
+
/**
|
41
|
+
* The service on a node (i.e. kv, query) is not available.
|
42
|
+
*/
|
43
|
+
service_not_available,
|
44
|
+
|
45
|
+
/**
|
46
|
+
* The node where the operation is supposed to be dispatched to is not available.
|
47
|
+
*/
|
48
|
+
node_not_available,
|
49
|
+
|
50
|
+
/**
|
51
|
+
* A not my vbucket response has been received.
|
52
|
+
*/
|
53
|
+
kv_not_my_vbucket,
|
54
|
+
|
55
|
+
/**
|
56
|
+
* A KV response has been received which signals an outdated collection.
|
57
|
+
*/
|
58
|
+
kv_collection_outdated,
|
59
|
+
|
60
|
+
/**
|
61
|
+
* An unknown response was returned and the consulted KV error map indicated a retry.
|
62
|
+
*/
|
63
|
+
kv_error_map_retry_indicated,
|
64
|
+
|
65
|
+
kv_locked,
|
66
|
+
|
67
|
+
kv_temporary_failure,
|
68
|
+
|
69
|
+
kv_sync_write_in_progress,
|
70
|
+
|
71
|
+
kv_sync_write_re_commit_in_progress,
|
72
|
+
|
73
|
+
service_response_code_indicated,
|
74
|
+
|
75
|
+
/**
|
76
|
+
* While an operation was in-flight, the underlying socket has been closed.
|
77
|
+
*/
|
78
|
+
socket_closed_while_in_flight,
|
79
|
+
|
80
|
+
/**
|
81
|
+
* The circuit breaker is open for the given socket/endpoint and as a result the operation is not sent into it.
|
82
|
+
*/
|
83
|
+
circuit_breaker_open,
|
84
|
+
|
85
|
+
query_prepared_statement_failure,
|
86
|
+
|
87
|
+
query_index_not_found,
|
88
|
+
|
89
|
+
analytics_temporary_failure,
|
90
|
+
|
91
|
+
search_too_many_requests,
|
92
|
+
|
93
|
+
views_temporary_failure,
|
94
|
+
|
95
|
+
views_no_active_partition,
|
96
|
+
};
|
97
|
+
|
98
|
+
constexpr inline bool
|
99
|
+
allows_non_idempotent_retry(retry_reason reason)
|
100
|
+
{
|
101
|
+
switch (reason) {
|
102
|
+
case retry_reason::socket_not_available:
|
103
|
+
case retry_reason::service_not_available:
|
104
|
+
case retry_reason::node_not_available:
|
105
|
+
case retry_reason::kv_not_my_vbucket:
|
106
|
+
case retry_reason::kv_collection_outdated:
|
107
|
+
case retry_reason::kv_error_map_retry_indicated:
|
108
|
+
case retry_reason::kv_locked:
|
109
|
+
case retry_reason::kv_temporary_failure:
|
110
|
+
case retry_reason::kv_sync_write_in_progress:
|
111
|
+
case retry_reason::kv_sync_write_re_commit_in_progress:
|
112
|
+
case retry_reason::service_response_code_indicated:
|
113
|
+
case retry_reason::circuit_breaker_open:
|
114
|
+
case retry_reason::query_prepared_statement_failure:
|
115
|
+
case retry_reason::query_index_not_found:
|
116
|
+
case retry_reason::analytics_temporary_failure:
|
117
|
+
case retry_reason::search_too_many_requests:
|
118
|
+
case retry_reason::views_temporary_failure:
|
119
|
+
case retry_reason::views_no_active_partition:
|
120
|
+
return true;
|
121
|
+
case retry_reason::do_not_retry:
|
122
|
+
case retry_reason::socket_closed_while_in_flight:
|
123
|
+
case retry_reason::unknown:
|
124
|
+
return false;
|
125
|
+
}
|
126
|
+
return false;
|
127
|
+
}
|
128
|
+
|
129
|
+
constexpr inline bool
|
130
|
+
always_retry(retry_reason reason)
|
131
|
+
{
|
132
|
+
switch (reason) {
|
133
|
+
case retry_reason::kv_not_my_vbucket:
|
134
|
+
case retry_reason::kv_collection_outdated:
|
135
|
+
case retry_reason::views_no_active_partition:
|
136
|
+
return true;
|
137
|
+
case retry_reason::do_not_retry:
|
138
|
+
case retry_reason::socket_not_available:
|
139
|
+
case retry_reason::service_not_available:
|
140
|
+
case retry_reason::node_not_available:
|
141
|
+
case retry_reason::kv_error_map_retry_indicated:
|
142
|
+
case retry_reason::kv_locked:
|
143
|
+
case retry_reason::kv_temporary_failure:
|
144
|
+
case retry_reason::kv_sync_write_in_progress:
|
145
|
+
case retry_reason::kv_sync_write_re_commit_in_progress:
|
146
|
+
case retry_reason::service_response_code_indicated:
|
147
|
+
case retry_reason::socket_closed_while_in_flight:
|
148
|
+
case retry_reason::circuit_breaker_open:
|
149
|
+
case retry_reason::query_prepared_statement_failure:
|
150
|
+
case retry_reason::query_index_not_found:
|
151
|
+
case retry_reason::analytics_temporary_failure:
|
152
|
+
case retry_reason::search_too_many_requests:
|
153
|
+
case retry_reason::views_temporary_failure:
|
154
|
+
case retry_reason::unknown:
|
155
|
+
return false;
|
156
|
+
}
|
157
|
+
return false;
|
158
|
+
}
|
159
|
+
|
160
|
+
} // namespace couchbase::io
|
161
|
+
|
162
|
+
template<>
|
163
|
+
struct fmt::formatter<couchbase::io::retry_reason> : formatter<string_view> {
|
164
|
+
template<typename FormatContext>
|
165
|
+
auto format(couchbase::io::retry_reason reason, FormatContext& ctx)
|
166
|
+
{
|
167
|
+
string_view name = "unknown";
|
168
|
+
switch (reason) {
|
169
|
+
case couchbase::io::retry_reason::do_not_retry:
|
170
|
+
name = "do_not_retry";
|
171
|
+
break;
|
172
|
+
case couchbase::io::retry_reason::unknown:
|
173
|
+
name = "unknown";
|
174
|
+
break;
|
175
|
+
case couchbase::io::retry_reason::socket_not_available:
|
176
|
+
name = "socket_not_available";
|
177
|
+
break;
|
178
|
+
case couchbase::io::retry_reason::service_not_available:
|
179
|
+
name = "service_not_available";
|
180
|
+
break;
|
181
|
+
case couchbase::io::retry_reason::node_not_available:
|
182
|
+
name = "node_not_available";
|
183
|
+
break;
|
184
|
+
case couchbase::io::retry_reason::kv_not_my_vbucket:
|
185
|
+
name = "kv_not_my_vbucket";
|
186
|
+
break;
|
187
|
+
case couchbase::io::retry_reason::kv_collection_outdated:
|
188
|
+
name = "kv_collection_outdated";
|
189
|
+
break;
|
190
|
+
case couchbase::io::retry_reason::kv_error_map_retry_indicated:
|
191
|
+
name = "kv_error_map_retry_indicated";
|
192
|
+
break;
|
193
|
+
case couchbase::io::retry_reason::kv_locked:
|
194
|
+
name = "kv_locked";
|
195
|
+
break;
|
196
|
+
case couchbase::io::retry_reason::kv_temporary_failure:
|
197
|
+
name = "kv_temporary_failure";
|
198
|
+
break;
|
199
|
+
case couchbase::io::retry_reason::kv_sync_write_in_progress:
|
200
|
+
name = "kv_sync_write_in_progress";
|
201
|
+
break;
|
202
|
+
case couchbase::io::retry_reason::kv_sync_write_re_commit_in_progress:
|
203
|
+
name = "kv_sync_write_re_commit_in_progress";
|
204
|
+
break;
|
205
|
+
case couchbase::io::retry_reason::service_response_code_indicated:
|
206
|
+
name = "service_response_code_indicated";
|
207
|
+
break;
|
208
|
+
case couchbase::io::retry_reason::socket_closed_while_in_flight:
|
209
|
+
name = "socket_closed_while_in_flight";
|
210
|
+
break;
|
211
|
+
case couchbase::io::retry_reason::circuit_breaker_open:
|
212
|
+
name = "circuit_breaker_open";
|
213
|
+
break;
|
214
|
+
case couchbase::io::retry_reason::query_prepared_statement_failure:
|
215
|
+
name = "query_prepared_statement_failure";
|
216
|
+
break;
|
217
|
+
case couchbase::io::retry_reason::query_index_not_found:
|
218
|
+
name = "query_index_not_found";
|
219
|
+
break;
|
220
|
+
case couchbase::io::retry_reason::analytics_temporary_failure:
|
221
|
+
name = "analytics_temporary_failure";
|
222
|
+
break;
|
223
|
+
case couchbase::io::retry_reason::search_too_many_requests:
|
224
|
+
name = "search_too_many_requests";
|
225
|
+
break;
|
226
|
+
case couchbase::io::retry_reason::views_temporary_failure:
|
227
|
+
name = "views_temporary_failure";
|
228
|
+
break;
|
229
|
+
case couchbase::io::retry_reason::views_no_active_partition:
|
230
|
+
name = "views_no_active_partition";
|
231
|
+
break;
|
232
|
+
}
|
233
|
+
return formatter<string_view>::format(name, ctx);
|
234
|
+
}
|
235
|
+
};
|
@@ -0,0 +1,156 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020 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 <gsl/gsl_assert>
|
21
|
+
|
22
|
+
#include <io/retry_reason.hxx>
|
23
|
+
#include <io/retry_action.hxx>
|
24
|
+
|
25
|
+
namespace couchbase::io::retry_strategy
|
26
|
+
{
|
27
|
+
namespace backoff
|
28
|
+
{
|
29
|
+
struct backoff_delay {
|
30
|
+
std::chrono::milliseconds delay;
|
31
|
+
std::chrono::milliseconds min_delay;
|
32
|
+
std::chrono::milliseconds max_delay;
|
33
|
+
};
|
34
|
+
|
35
|
+
class fixed
|
36
|
+
{
|
37
|
+
private:
|
38
|
+
backoff_delay delay_;
|
39
|
+
|
40
|
+
public:
|
41
|
+
explicit fixed(std::chrono::milliseconds delay)
|
42
|
+
: delay_{ delay, delay, delay }
|
43
|
+
{
|
44
|
+
}
|
45
|
+
|
46
|
+
template<class Request>
|
47
|
+
backoff_delay operator()(Request&)
|
48
|
+
{
|
49
|
+
return delay_;
|
50
|
+
}
|
51
|
+
};
|
52
|
+
|
53
|
+
class exponential
|
54
|
+
{
|
55
|
+
public:
|
56
|
+
/**
|
57
|
+
* Backoff function with exponential backoff delay. Retries are performed after a backoff interval of
|
58
|
+
*
|
59
|
+
* first_backoff * (factor^n)
|
60
|
+
*
|
61
|
+
* where n is the iteration. If max_backoff is not zero, the maximum backoff applied will be limited to max_backoff.
|
62
|
+
*
|
63
|
+
* If based_on_previous_value is true, backoff will be calculated using
|
64
|
+
*
|
65
|
+
* prev_backoff * factor
|
66
|
+
*
|
67
|
+
* When backoffs are combined with jitter, this value will be different from the actual exponential value for the iteration.
|
68
|
+
*
|
69
|
+
* @param first_backoff First backoff duration
|
70
|
+
* @param factor The multiplicand for calculating backoff
|
71
|
+
* @param max_backoff Maximum backoff duration
|
72
|
+
* @param based_on_previous_value If true, calculation is based on previous value which may be a backoff with jitter applied
|
73
|
+
*/
|
74
|
+
exponential(std::chrono::milliseconds first_backoff,
|
75
|
+
int factor,
|
76
|
+
std::chrono::milliseconds max_backoff = {},
|
77
|
+
bool based_on_previous_value = false)
|
78
|
+
{
|
79
|
+
Expects(first_backoff.count() > 0);
|
80
|
+
if (max_backoff.count() <= 0) {
|
81
|
+
max_backoff = std::chrono::milliseconds{ std::numeric_limits<std::chrono::milliseconds::rep>::max() };
|
82
|
+
}
|
83
|
+
Expects(max_backoff > first_backoff);
|
84
|
+
first_backoff_ = first_backoff;
|
85
|
+
max_backoff_ = max_backoff;
|
86
|
+
factor_ = factor;
|
87
|
+
based_on_previous_value_ = based_on_previous_value;
|
88
|
+
}
|
89
|
+
|
90
|
+
template<class Request>
|
91
|
+
backoff_delay operator()(Request& request)
|
92
|
+
{
|
93
|
+
std::chrono::milliseconds previous_backoff = request.retries.last_duration;
|
94
|
+
std::chrono::milliseconds next_backoff;
|
95
|
+
|
96
|
+
if (based_on_previous_value_) {
|
97
|
+
if (previous_backoff >= max_backoff_) {
|
98
|
+
next_backoff = max_backoff_;
|
99
|
+
} else {
|
100
|
+
next_backoff = previous_backoff * factor_;
|
101
|
+
}
|
102
|
+
if (next_backoff < first_backoff_) {
|
103
|
+
next_backoff = first_backoff_;
|
104
|
+
}
|
105
|
+
} else {
|
106
|
+
if (previous_backoff >= max_backoff_) {
|
107
|
+
next_backoff = max_backoff_;
|
108
|
+
} else {
|
109
|
+
next_backoff = first_backoff_ * static_cast<int>(std::pow(factor_, request.retries.retry_attempts - 1));
|
110
|
+
}
|
111
|
+
}
|
112
|
+
return { next_backoff, first_backoff_, max_backoff_ };
|
113
|
+
}
|
114
|
+
|
115
|
+
private:
|
116
|
+
std::chrono::milliseconds first_backoff_;
|
117
|
+
std::chrono::milliseconds max_backoff_;
|
118
|
+
int factor_;
|
119
|
+
bool based_on_previous_value_;
|
120
|
+
};
|
121
|
+
|
122
|
+
} // namespace backoff
|
123
|
+
|
124
|
+
class best_effort
|
125
|
+
{
|
126
|
+
public:
|
127
|
+
best_effort()
|
128
|
+
: backoff_(std::chrono::milliseconds(1), 2, std::chrono::milliseconds(500))
|
129
|
+
{
|
130
|
+
}
|
131
|
+
|
132
|
+
template<class Request>
|
133
|
+
retry_action should_retry(Request& request, retry_reason reason)
|
134
|
+
{
|
135
|
+
if (request.retries.idempotent || allows_non_idempotent_retry(reason)) {
|
136
|
+
backoff::backoff_delay delay = backoff_(request);
|
137
|
+
return { true, delay.delay };
|
138
|
+
}
|
139
|
+
return { false };
|
140
|
+
}
|
141
|
+
|
142
|
+
private:
|
143
|
+
backoff::exponential backoff_;
|
144
|
+
};
|
145
|
+
|
146
|
+
class fail_fast
|
147
|
+
{
|
148
|
+
public:
|
149
|
+
template<class Request>
|
150
|
+
retry_action should_retry(Request&, retry_reason)
|
151
|
+
{
|
152
|
+
return { false };
|
153
|
+
}
|
154
|
+
};
|
155
|
+
|
156
|
+
} // namespace couchbase::io::retry_strategy
|