couchbase 3.8.1-aarch64-linux → 3.9.0-aarch64-linux
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/active_support/cache/couchbase_store.rb +77 -12
- data/lib/couchbase/3.2/libcouchbase.so +0 -0
- data/lib/couchbase/3.3/libcouchbase.so +0 -0
- data/lib/couchbase/3.4/libcouchbase.so +0 -0
- data/lib/couchbase/4.0/libcouchbase.so +0 -0
- data/lib/couchbase/bucket.rb +20 -0
- data/lib/couchbase/cluster.rb +33 -14
- data/lib/couchbase/collection.rb +36 -1
- data/lib/couchbase/errors.rb +18 -0
- data/lib/couchbase/fork_hooks.rb +16 -7
- data/lib/couchbase/get_replica_strategy.rb +89 -0
- data/lib/couchbase/logger.rb +1 -1
- data/lib/couchbase/management/analytics_index_manager.rb +15 -15
- data/lib/couchbase/management/bucket_manager.rb +6 -6
- data/lib/couchbase/management/collection_manager.rb +6 -6
- data/lib/couchbase/management/query_index_manager.rb +7 -7
- data/lib/couchbase/management/search_index_manager.rb +13 -13
- data/lib/couchbase/management/user_manager.rb +10 -10
- data/lib/couchbase/management/view_index_manager.rb +5 -5
- data/lib/couchbase/options.rb +181 -44
- data/lib/couchbase/protostellar/bucket.rb +4 -0
- data/lib/couchbase/protostellar/cluster.rb +4 -0
- data/lib/couchbase/protostellar/collection.rb +4 -0
- data/lib/couchbase/protostellar/error_handling.rb +2 -1
- data/lib/couchbase/protostellar/request.rb +1 -0
- data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
- data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
- data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
- data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
- data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
- data/lib/couchbase/search_options.rb +95 -6
- data/lib/couchbase/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a4d838edd719c54da2b7b0cc4775238c38dbf42fded0ea7ea15c664bac5f875
|
|
4
|
+
data.tar.gz: d1bd00c3504a9c16408aafb7342b1a8e245ec0207098cb891fcf0578f37b5c74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74f8ddc4fc9b7e91080832a29b996552571414919182ad94eea896ba5475b9054e5ad805e1766203f0c0c4eff867411581962ccfa6ec0e6f864f6ccaef29ecbc
|
|
7
|
+
data.tar.gz: 82ecd27fa9fd9241069ba96b0edff6ea6f6bff27ddb8215c62a560ee7acc874c3dad06b6e794bd77b1293e222da76139ed2ef592315d49a8a047f2563ce67e66
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@ This repository contains the third generation of the official Couchbase SDK for
|
|
|
9
9
|
|
|
10
10
|
## Support and Feedback
|
|
11
11
|
|
|
12
|
-
If you find an issue,
|
|
12
|
+
Issues are tracked on [our JIRA issue tracker](https://jira.issues.couchbase.com/browse/RCBC). If you find an issue, you can report it by [opening an issue on GitHub](https://github.com/couchbase/couchbase-ruby-client/issues). Also, you are
|
|
13
13
|
always welcome on [our forum](https://forums.couchbase.com/c/ruby-sdk) and [Discord](https://discord.com/invite/sQ5qbPZuTh).
|
|
14
14
|
|
|
15
15
|
Please attach version information to the ticket/post. To obtain this information, use the following command:
|
|
@@ -23,7 +23,7 @@ The library has been tested with MRI 3.2, 3.3, 3.4 and 4.0. Supported platforms
|
|
|
23
23
|
Add this line to your application's Gemfile:
|
|
24
24
|
|
|
25
25
|
```ruby
|
|
26
|
-
gem "couchbase", "3.
|
|
26
|
+
gem "couchbase", "3.9.0"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
And then execute:
|
|
@@ -36,6 +36,13 @@ module ActiveSupport
|
|
|
36
36
|
# bucket: "app_cache"
|
|
37
37
|
# }
|
|
38
38
|
#
|
|
39
|
+
# +delete_matched+ and +clear+ query with +consistent_with+ the store's writes.
|
|
40
|
+
# By default that covers the last write only, which leaves earlier writes to
|
|
41
|
+
# other vBuckets out of the query until the index catches up. With
|
|
42
|
+
# +mutation_tracking: :all+ the store keeps the newest mutation token of every
|
|
43
|
+
# vBucket it wrote to, so both see every earlier write. It also accepts an
|
|
44
|
+
# object that responds to +record(*tokens)+ and +mutation_state+.
|
|
45
|
+
#
|
|
39
46
|
# @see https://guides.rubyonrails.org/caching_with_rails.html#cache-stores
|
|
40
47
|
class CouchbaseStore < Store
|
|
41
48
|
MAX_KEY_BYTESIZE = 250
|
|
@@ -43,6 +50,51 @@ module ActiveSupport
|
|
|
43
50
|
logger&.error { "CouchbaseStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
|
|
44
51
|
end
|
|
45
52
|
|
|
53
|
+
# Default +mutation_tracking+: the token of the last write.
|
|
54
|
+
class LastMutation
|
|
55
|
+
def initialize
|
|
56
|
+
@token = nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def record(*tokens)
|
|
60
|
+
latest = tokens.compact.max_by(&:sequence_number)
|
|
61
|
+
@token = latest if latest
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def mutation_state
|
|
65
|
+
token = @token
|
|
66
|
+
token && ::Couchbase::MutationState.new(token)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# +mutation_tracking: :all+: the newest token of every vBucket written to,
|
|
71
|
+
# at most one per vBucket however many writes there were.
|
|
72
|
+
class AllMutations
|
|
73
|
+
def initialize
|
|
74
|
+
@tokens = {}
|
|
75
|
+
@lock = Mutex.new
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def record(*tokens)
|
|
79
|
+
@lock.synchronize do
|
|
80
|
+
tokens.compact.each do |token|
|
|
81
|
+
key = [token.bucket_name, token.partition_id]
|
|
82
|
+
known = @tokens[key]
|
|
83
|
+
# After a failover the partition_uuid changes and sequence numbers
|
|
84
|
+
# do not compare across it, so the token recorded last is kept.
|
|
85
|
+
next if known && known.partition_uuid == token.partition_uuid && known.sequence_number >= token.sequence_number
|
|
86
|
+
|
|
87
|
+
@tokens[key] = token
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def mutation_state
|
|
93
|
+
tokens = @lock.synchronize { @tokens.values }
|
|
94
|
+
::Couchbase::MutationState.new(*tokens) unless tokens.empty?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
46
98
|
# Advertise cache versioning support.
|
|
47
99
|
def self.supports_cache_versioning?
|
|
48
100
|
true
|
|
@@ -70,7 +122,17 @@ module ActiveSupport
|
|
|
70
122
|
@options.delete(:bucket) { raise ArgumentError, "Missing bucket for Couchbase cache store. Use :bucket in the store options" }
|
|
71
123
|
@couchbase_options[:scope] = @options.delete(:scope) if @options.key?(:scope)
|
|
72
124
|
@couchbase_options[:collection] = @options.delete(:collection) if @options.key?(:collection)
|
|
73
|
-
@
|
|
125
|
+
@mutations =
|
|
126
|
+
case (tracking = @options.delete(:mutation_tracking) { :last })
|
|
127
|
+
when :last, "last" then LastMutation.new
|
|
128
|
+
when :all, "all" then AllMutations.new
|
|
129
|
+
else
|
|
130
|
+
unless tracking.respond_to?(:record) && tracking.respond_to?(:mutation_state)
|
|
131
|
+
raise ArgumentError, "mutation_tracking must be :last, :all, or respond to #record and #mutation_state"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
tracking
|
|
135
|
+
end
|
|
74
136
|
end
|
|
75
137
|
|
|
76
138
|
def collection
|
|
@@ -90,9 +152,10 @@ module ActiveSupport
|
|
|
90
152
|
# The +matcher+ must be valid pattern for N1QL +REGEXP_MATCHES+ function. More info at
|
|
91
153
|
# https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/patternmatchingfun.html#section_regex_matches
|
|
92
154
|
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# might not be deleted.
|
|
155
|
+
# The query is consistent with the writes this store recorded, as selected by
|
|
156
|
+
# +mutation_tracking+. With the default +:last+, keys written shortly before the
|
|
157
|
+
# last write to other vBuckets might not be deleted. Writes by other processes
|
|
158
|
+
# are not covered.
|
|
96
159
|
#
|
|
97
160
|
# Also this method assumes, that primary index created on the target bucket
|
|
98
161
|
def delete_matched(matcher, _options = nil)
|
|
@@ -106,7 +169,8 @@ module ActiveSupport
|
|
|
106
169
|
raise NotImplementedError, "Unable to convert #{matcher.inspect} to Regexp pattern"
|
|
107
170
|
end
|
|
108
171
|
operation_options = ::Couchbase::Options::Query(named_parameters: {"pattern" => pattern}, metrics: true)
|
|
109
|
-
|
|
172
|
+
mutation_state = @mutations.mutation_state
|
|
173
|
+
operation_options.consistent_with(mutation_state) if mutation_state
|
|
110
174
|
begin
|
|
111
175
|
result = cluster.query("DELETE FROM #{scope_qualifier} cache_store_ WHERE REGEXP_MATCHES(META(cache_store_).id, $pattern)",
|
|
112
176
|
operation_options)
|
|
@@ -127,7 +191,7 @@ module ActiveSupport
|
|
|
127
191
|
res = collection.binary.increment(
|
|
128
192
|
key, ::Couchbase::Options::Increment(delta: amount, expiry: expires_in, initial: initial)
|
|
129
193
|
)
|
|
130
|
-
@
|
|
194
|
+
@mutations.record(res.mutation_token)
|
|
131
195
|
res.content
|
|
132
196
|
end
|
|
133
197
|
end
|
|
@@ -147,7 +211,7 @@ module ActiveSupport
|
|
|
147
211
|
res = collection.binary.decrement(
|
|
148
212
|
key, ::Couchbase::Options::Decrement(delta: amount, expiry: expires_in, initial: initial)
|
|
149
213
|
)
|
|
150
|
-
@
|
|
214
|
+
@mutations.record(res.mutation_token)
|
|
151
215
|
res.content
|
|
152
216
|
end
|
|
153
217
|
end
|
|
@@ -164,7 +228,8 @@ module ActiveSupport
|
|
|
164
228
|
cluster.buckets.flush_bucket(@couchbase_options[:bucket_name])
|
|
165
229
|
else
|
|
166
230
|
operation_options = ::Couchbase::Options::Query.new
|
|
167
|
-
|
|
231
|
+
mutation_state = @mutations.mutation_state
|
|
232
|
+
operation_options.consistent_with(mutation_state) if mutation_state
|
|
168
233
|
cluster.query("DELETE FROM #{scope_qualifier}", operation_options)
|
|
169
234
|
end
|
|
170
235
|
end
|
|
@@ -239,7 +304,7 @@ module ActiveSupport
|
|
|
239
304
|
end
|
|
240
305
|
failsafe(:write_entry, returning: false) do
|
|
241
306
|
res = collection.upsert(key, payload, ::Couchbase::Options::Upsert(transcoder: nil, expiry: expires_in))
|
|
242
|
-
@
|
|
307
|
+
@mutations.record(res.mutation_token)
|
|
243
308
|
true
|
|
244
309
|
end
|
|
245
310
|
end
|
|
@@ -259,7 +324,7 @@ module ActiveSupport
|
|
|
259
324
|
successful = collection.upsert_multi(serialize_entries(entries, **options), operation_options).select(&:success?)
|
|
260
325
|
return 0 if successful.empty?
|
|
261
326
|
|
|
262
|
-
@
|
|
327
|
+
@mutations.record(*successful.map(&:mutation_token))
|
|
263
328
|
successful.count
|
|
264
329
|
end
|
|
265
330
|
end
|
|
@@ -269,7 +334,7 @@ module ActiveSupport
|
|
|
269
334
|
def delete_entry(key, **_options)
|
|
270
335
|
failsafe(:delete_entry, returning: false) do
|
|
271
336
|
res = collection.remove(key)
|
|
272
|
-
@
|
|
337
|
+
@mutations.record(res.mutation_token)
|
|
273
338
|
true
|
|
274
339
|
end
|
|
275
340
|
end
|
|
@@ -282,7 +347,7 @@ module ActiveSupport
|
|
|
282
347
|
successful = collection.remove_multi(entries).select(&:success?)
|
|
283
348
|
return 0 if successful.empty?
|
|
284
349
|
|
|
285
|
-
@
|
|
350
|
+
@mutations.record(*successful.map(&:mutation_token))
|
|
286
351
|
successful.count
|
|
287
352
|
end
|
|
288
353
|
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/couchbase/bucket.rb
CHANGED
|
@@ -149,5 +149,25 @@ module Couchbase
|
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
|
+
|
|
153
|
+
# Waits until the bucket reaches the desired state, actively pinging its services
|
|
154
|
+
#
|
|
155
|
+
# With +:online+, the wait also requires the bucket's vbucket map to be placed: every vbucket has its active copy
|
|
156
|
+
# and each replica the data nodes can hold assigned to a node. +:degraded+ only pings.
|
|
157
|
+
#
|
|
158
|
+
# +Thread#raise+ does not interrupt the wait. It ends on success, on +timeout+, or when the cluster is disconnected.
|
|
159
|
+
#
|
|
160
|
+
# @param [Integer, #in_milliseconds] timeout the maximum time to wait
|
|
161
|
+
# @param [Options::WaitUntilReady] options
|
|
162
|
+
#
|
|
163
|
+
# @return [void]
|
|
164
|
+
#
|
|
165
|
+
# @raise [Error::UnambiguousTimeout] if the desired state is not reached within +timeout+
|
|
166
|
+
# @raise [Error::ClusterClosed] if the cluster has been disconnected
|
|
167
|
+
# @raise [Error::InvalidArgument] if +timeout+ is negative or longer than 365 days, or +options+ name an
|
|
168
|
+
# unsupported desired state or service type
|
|
169
|
+
def wait_until_ready(timeout, options = Options::WaitUntilReady::DEFAULT)
|
|
170
|
+
@backend.wait_until_ready(@name, Utils::Time.extract_duration(timeout), options.to_backend)
|
|
171
|
+
end
|
|
152
172
|
end
|
|
153
173
|
end
|
data/lib/couchbase/cluster.rb
CHANGED
|
@@ -326,26 +326,45 @@ module Couchbase
|
|
|
326
326
|
#
|
|
327
327
|
# @return [PingResult]
|
|
328
328
|
def ping(options = Options::Ping::DEFAULT)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
329
|
+
@observability.record_operation(Observability::OP_PING, options.parent_span, self) do |_obs_handler|
|
|
330
|
+
resp = @backend.ping(nil, options.to_backend)
|
|
331
|
+
PingResult.new do |res|
|
|
332
|
+
res.version = resp[:version]
|
|
333
|
+
res.id = resp[:id]
|
|
334
|
+
res.sdk = resp[:sdk]
|
|
335
|
+
resp[:services].each do |type, svcs|
|
|
336
|
+
res.services[type] = svcs.map do |svc|
|
|
337
|
+
PingResult::ServiceInfo.new do |info|
|
|
338
|
+
info.id = svc[:id]
|
|
339
|
+
info.state = svc[:state]
|
|
340
|
+
info.latency = svc[:latency]
|
|
341
|
+
info.remote = svc[:remote]
|
|
342
|
+
info.local = svc[:local]
|
|
343
|
+
info.error = svc[:error]
|
|
344
|
+
end
|
|
343
345
|
end
|
|
344
346
|
end
|
|
345
347
|
end
|
|
346
348
|
end
|
|
347
349
|
end
|
|
348
350
|
|
|
351
|
+
# Waits until the cluster reaches the desired state, actively pinging its services
|
|
352
|
+
#
|
|
353
|
+
# +Thread#raise+ does not interrupt the wait. It ends on success, on +timeout+, or when the cluster is disconnected.
|
|
354
|
+
#
|
|
355
|
+
# @param [Integer, #in_milliseconds] timeout the maximum time to wait
|
|
356
|
+
# @param [Options::WaitUntilReady] options
|
|
357
|
+
#
|
|
358
|
+
# @return [void]
|
|
359
|
+
#
|
|
360
|
+
# @raise [Error::UnambiguousTimeout] if the desired state is not reached within +timeout+
|
|
361
|
+
# @raise [Error::ClusterClosed] if the cluster has been disconnected
|
|
362
|
+
# @raise [Error::InvalidArgument] if +timeout+ is negative or longer than 365 days, or +options+ name an
|
|
363
|
+
# unsupported desired state or service type
|
|
364
|
+
def wait_until_ready(timeout, options = Options::WaitUntilReady::DEFAULT)
|
|
365
|
+
@backend.wait_until_ready(nil, Utils::Time.extract_duration(timeout), options.to_backend)
|
|
366
|
+
end
|
|
367
|
+
|
|
349
368
|
private
|
|
350
369
|
|
|
351
370
|
# Initialize {Cluster} object
|
data/lib/couchbase/collection.rb
CHANGED
|
@@ -18,6 +18,7 @@ require "couchbase/errors"
|
|
|
18
18
|
require "couchbase/collection_options"
|
|
19
19
|
require "couchbase/binary_collection"
|
|
20
20
|
require "couchbase/key_value_scan"
|
|
21
|
+
require "couchbase/get_replica_strategy"
|
|
21
22
|
|
|
22
23
|
module Couchbase
|
|
23
24
|
# Provides access to all collection APIs
|
|
@@ -203,6 +204,40 @@ module Couchbase
|
|
|
203
204
|
end
|
|
204
205
|
end
|
|
205
206
|
|
|
207
|
+
# Reads the document from the replica the strategy selects.
|
|
208
|
+
#
|
|
209
|
+
# @param [String] id the document id which is used to uniquely identify it.
|
|
210
|
+
# @param [GetReplicaStrategy] strategy selects the replica to read from
|
|
211
|
+
# @param [Options::GetReplica] options request customization
|
|
212
|
+
#
|
|
213
|
+
# @example Read the document from the second replica
|
|
214
|
+
# res = collection.get_replica("airline_10", GetReplicaStrategy.from_index(:second))
|
|
215
|
+
# res.replica? #=> true
|
|
216
|
+
# res.content["name"]
|
|
217
|
+
#
|
|
218
|
+
# @raise [Error::DocumentNotFoundOnReplica] if the replica does not hold the document
|
|
219
|
+
# @raise [Error::ReplicaIndexOutOfBounds] if the bucket is not configured with the requested replica
|
|
220
|
+
# @raise [Error::ReplicaIndexCurrentlyUnavailable] if the vbucket map places the requested replica on no node
|
|
221
|
+
# @raise [Error::UnambiguousTimeout] if the replica does not answer before the timeout
|
|
222
|
+
# @raise [Error::FeatureNotAvailable] over +couchbase2://+, which does not carry the operation
|
|
223
|
+
#
|
|
224
|
+
# @since 3.9.0
|
|
225
|
+
#
|
|
226
|
+
# @return [GetReplicaResult]
|
|
227
|
+
def get_replica(id, strategy, options = Options::GetReplica::DEFAULT)
|
|
228
|
+
@observability.record_operation(Observability::OP_GET_REPLICA, options.parent_span, self, :kv) do |obs_handler|
|
|
229
|
+
resp = @backend.document_get_replica(@bucket_name, @scope_name, @name, id, strategy.to_backend,
|
|
230
|
+
options.to_backend, obs_handler)
|
|
231
|
+
GetReplicaResult.new do |res|
|
|
232
|
+
res.transcoder = options.transcoder
|
|
233
|
+
res.cas = resp[:cas]
|
|
234
|
+
res.flags = resp[:flags]
|
|
235
|
+
res.encoded = resp[:content]
|
|
236
|
+
res.is_replica = true
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
206
241
|
# Reads all available replicas and active, and returns the first found.
|
|
207
242
|
#
|
|
208
243
|
# @param [String] id the document id which is used to uniquely identify it.
|
|
@@ -210,7 +245,7 @@ module Couchbase
|
|
|
210
245
|
#
|
|
211
246
|
# @example Get document contents
|
|
212
247
|
# res = collection.get_any_replica("customer123")
|
|
213
|
-
# res.
|
|
248
|
+
# res.replica? #=> true when a replica answered first, false for the active copy
|
|
214
249
|
# res.content["addresses"]
|
|
215
250
|
#
|
|
216
251
|
# # {"billing"=>
|
data/lib/couchbase/errors.rb
CHANGED
|
@@ -185,6 +185,24 @@ module Couchbase
|
|
|
185
185
|
class DocumentNotFound < CouchbaseError
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
+
# Indicates that the replica selected by {Collection#get_replica} does not hold the document.
|
|
189
|
+
#
|
|
190
|
+
# @since 3.9.0
|
|
191
|
+
class DocumentNotFoundOnReplica < DocumentNotFound
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Indicates that {Collection#get_replica} requested a replica the bucket is not configured with.
|
|
195
|
+
#
|
|
196
|
+
# @since 3.9.0
|
|
197
|
+
class ReplicaIndexOutOfBounds < CouchbaseError
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Indicates that {Collection#get_replica} requested a configured replica. The vbucket map places it on no node.
|
|
201
|
+
#
|
|
202
|
+
# @since 3.9.0
|
|
203
|
+
class ReplicaIndexCurrentlyUnavailable < CouchbaseError
|
|
204
|
+
end
|
|
205
|
+
|
|
188
206
|
# Indicates an operation completed but no successful document was retrievable.
|
|
189
207
|
class DocumentIrretrievable < CouchbaseError
|
|
190
208
|
end
|
data/lib/couchbase/fork_hooks.rb
CHANGED
|
@@ -17,14 +17,23 @@
|
|
|
17
17
|
module Couchbase
|
|
18
18
|
module ForkHooks
|
|
19
19
|
def _fork
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
# :prepare stops the instances and only :parent or :child restarts them, so an asynchronous
|
|
21
|
+
# exception is deferred everywhere except inside the fork itself.
|
|
22
|
+
Thread.handle_interrupt(Object => :never) do
|
|
23
|
+
parent_pid = Process.pid
|
|
24
|
+
Couchbase::Backend.notify_fork(:prepare)
|
|
25
|
+
begin
|
|
26
|
+
Thread.handle_interrupt(Object => :immediate) do
|
|
27
|
+
# A check point: an interrupt deferred during :prepare lands here, before the fork.
|
|
28
|
+
Thread.pass
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
ensure
|
|
32
|
+
# Decided by pid rather than by the return value, which an exception raised after the fork
|
|
33
|
+
# never delivers.
|
|
34
|
+
Couchbase::Backend.notify_fork(Process.pid == parent_pid ? :parent : :child)
|
|
35
|
+
end
|
|
26
36
|
end
|
|
27
|
-
pid
|
|
28
37
|
end
|
|
29
38
|
end
|
|
30
39
|
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026. 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
|
+
require "couchbase/errors"
|
|
18
|
+
|
|
19
|
+
module Couchbase
|
|
20
|
+
# Selects the replica that {Collection#get_replica} reads from.
|
|
21
|
+
#
|
|
22
|
+
# @since 3.9.0
|
|
23
|
+
class GetReplicaStrategy
|
|
24
|
+
REPLICA_INDEXES = {first: 0, second: 1, third: 2}.freeze
|
|
25
|
+
private_constant :REPLICA_INDEXES
|
|
26
|
+
|
|
27
|
+
# @return [Symbol] the requested replica, one of +:first+, +:second+ or +:third+
|
|
28
|
+
attr_reader :index
|
|
29
|
+
|
|
30
|
+
# @return [Boolean] whether an index that cannot be read resolves to the next replica that can
|
|
31
|
+
attr_reader :wrap
|
|
32
|
+
|
|
33
|
+
private_class_method :new
|
|
34
|
+
|
|
35
|
+
# Selects the replica at the given index.
|
|
36
|
+
#
|
|
37
|
+
# Without +wrap+, the SDK resolves the index against the vbucket map it already holds. No network round trip
|
|
38
|
+
# is made for these two errors:
|
|
39
|
+
# * an index at or beyond the bucket's configured replica count raises {Error::ReplicaIndexOutOfBounds}
|
|
40
|
+
# * a configured replica the vbucket map places on no node raises {Error::ReplicaIndexCurrentlyUnavailable}
|
|
41
|
+
#
|
|
42
|
+
# The replica is chosen from the vbucket map, not from node health. A replica on a node that is down is still
|
|
43
|
+
# chosen. The read can then wait for that node until the read times out.
|
|
44
|
+
#
|
|
45
|
+
# @param [Symbol] index the replica to read from, one of +:first+, +:second+ or +:third+
|
|
46
|
+
# @param [Boolean] wrap if +true+, an unreadable index moves to the next readable replica. The walk starts at the
|
|
47
|
+
# index modulo the configured replica count. A bucket configured without replicas still raises
|
|
48
|
+
# {Error::ReplicaIndexOutOfBounds}. If no replica is readable, it still raises
|
|
49
|
+
# {Error::ReplicaIndexCurrentlyUnavailable}.
|
|
50
|
+
#
|
|
51
|
+
# @raise [Error::InvalidArgument] if +index+ is not one of the supported symbols, or +wrap+ is not a Boolean
|
|
52
|
+
#
|
|
53
|
+
# @example Read the first replica, or the next readable one
|
|
54
|
+
# collection.get_replica("airline_10", GetReplicaStrategy.from_index(:first, wrap: true))
|
|
55
|
+
#
|
|
56
|
+
# @example Bound the wait for a replica whose node is down, then read any copy
|
|
57
|
+
# begin
|
|
58
|
+
# collection.get_replica("airline_10", GetReplicaStrategy.from_index(:first, wrap: true),
|
|
59
|
+
# Options::GetReplica(timeout: 500))
|
|
60
|
+
# rescue Error::UnambiguousTimeout
|
|
61
|
+
# collection.get_any_replica("airline_10")
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# @return [GetReplicaStrategy]
|
|
65
|
+
def self.from_index(index, wrap: false)
|
|
66
|
+
unless REPLICA_INDEXES.key?(index)
|
|
67
|
+
raise Error::InvalidArgument, "replica index must be one of #{REPLICA_INDEXES.keys.inspect}, but given #{index.inspect}"
|
|
68
|
+
end
|
|
69
|
+
raise Error::InvalidArgument, "wrap must be a Boolean, but given #{wrap.inspect}" unless [true, false].include?(wrap)
|
|
70
|
+
|
|
71
|
+
new(index, wrap)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @api private
|
|
75
|
+
def initialize(index, wrap)
|
|
76
|
+
@index = index
|
|
77
|
+
@wrap = wrap
|
|
78
|
+
freeze
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @api private
|
|
82
|
+
def to_backend
|
|
83
|
+
{
|
|
84
|
+
replica_index: REPLICA_INDEXES.fetch(@index),
|
|
85
|
+
wrap: @wrap,
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/couchbase/logger.rb
CHANGED
|
@@ -69,7 +69,7 @@ module Couchbase
|
|
|
69
69
|
def self.set_logger(logger, adapter_class: nil, verbose: false, level: :info)
|
|
70
70
|
@logger = logger # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
71
71
|
if @logger.nil? # rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
72
|
-
Backend.install_logger_shim(nil)
|
|
72
|
+
Backend.install_logger_shim(nil, nil)
|
|
73
73
|
return
|
|
74
74
|
end
|
|
75
75
|
shim =
|
|
@@ -31,7 +31,7 @@ module Couchbase
|
|
|
31
31
|
# dataverse with the specified name already exists.
|
|
32
32
|
#
|
|
33
33
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
34
|
-
# @param [
|
|
34
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
35
35
|
# @param [Hash, nil] client_context the client context data, if set
|
|
36
36
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
37
37
|
#
|
|
@@ -65,7 +65,7 @@ module Couchbase
|
|
|
65
65
|
# if the dataverse with the specified name does not exist.
|
|
66
66
|
#
|
|
67
67
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
68
|
-
# @param [
|
|
68
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
69
69
|
# @param [Hash, nil] client_context the client context data, if set
|
|
70
70
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
71
71
|
#
|
|
@@ -103,7 +103,7 @@ module Couchbase
|
|
|
103
103
|
# @param [String] dataverse_name the name of the dataverse to use (defaults to +nil+)
|
|
104
104
|
#
|
|
105
105
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
106
|
-
# @param [
|
|
106
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
107
107
|
# @param [Hash, nil] client_context the client context data, if set
|
|
108
108
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
109
109
|
#
|
|
@@ -145,7 +145,7 @@ module Couchbase
|
|
|
145
145
|
# @param [String] dataverse_name the name of the dataverse to use (defaults to +nil+)
|
|
146
146
|
#
|
|
147
147
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
148
|
-
# @param [
|
|
148
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
149
149
|
# @param [Hash, nil] client_context the client context data, if set
|
|
150
150
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
151
151
|
#
|
|
@@ -177,7 +177,7 @@ module Couchbase
|
|
|
177
177
|
# Creates an instance of options for {AnalyticsIndexManager#get_all_datasets}
|
|
178
178
|
#
|
|
179
179
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
180
|
-
# @param [
|
|
180
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
181
181
|
# @param [Hash, nil] client_context the client context data, if set
|
|
182
182
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
183
183
|
#
|
|
@@ -203,7 +203,7 @@ module Couchbase
|
|
|
203
203
|
# @param [String] dataverse_name the name of the dataverse to use (defaults to +nil+)
|
|
204
204
|
#
|
|
205
205
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
206
|
-
# @param [
|
|
206
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
207
207
|
# @param [Hash, nil] client_context the client context data, if set
|
|
208
208
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
209
209
|
#
|
|
@@ -242,7 +242,7 @@ module Couchbase
|
|
|
242
242
|
# @param [String] dataverse_name the name of the dataverse to use (defaults to +nil+)
|
|
243
243
|
#
|
|
244
244
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
245
|
-
# @param [
|
|
245
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
246
246
|
# @param [Hash, nil] client_context the client context data, if set
|
|
247
247
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
248
248
|
#
|
|
@@ -274,7 +274,7 @@ module Couchbase
|
|
|
274
274
|
# Creates an instance of options for {AnalyticsIndexManager#get_all_indexes}
|
|
275
275
|
#
|
|
276
276
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
277
|
-
# @param [
|
|
277
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
278
278
|
# @param [Hash, nil] client_context the client context data, if set
|
|
279
279
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
280
280
|
#
|
|
@@ -302,7 +302,7 @@ module Couchbase
|
|
|
302
302
|
# @param [String] dataverse_name the name of the dataverse to use (defaults to +nil+)
|
|
303
303
|
#
|
|
304
304
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
305
|
-
# @param [
|
|
305
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
306
306
|
# @param [Hash, nil] client_context the client context data, if set
|
|
307
307
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
308
308
|
#
|
|
@@ -343,7 +343,7 @@ module Couchbase
|
|
|
343
343
|
# @param [String] dataverse_name the name of the dataverse to use (defaults to +nil+)
|
|
344
344
|
#
|
|
345
345
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
346
|
-
# @param [
|
|
346
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
347
347
|
# @param [Hash, nil] client_context the client context data, if set
|
|
348
348
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
349
349
|
#
|
|
@@ -375,7 +375,7 @@ module Couchbase
|
|
|
375
375
|
# Creates an instance of options for {AnalyticsIndexManager#get_pending_mutations}
|
|
376
376
|
#
|
|
377
377
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
378
|
-
# @param [
|
|
378
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
379
379
|
# @param [Hash, nil] client_context the client context data, if set
|
|
380
380
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
381
381
|
#
|
|
@@ -394,7 +394,7 @@ module Couchbase
|
|
|
394
394
|
# Creates an instance of options for {AnalyticsIndexManager#create_link}
|
|
395
395
|
#
|
|
396
396
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
397
|
-
# @param [
|
|
397
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
398
398
|
# @param [Hash, nil] client_context the client context data, if set
|
|
399
399
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
400
400
|
#
|
|
@@ -420,7 +420,7 @@ module Couchbase
|
|
|
420
420
|
# Creates an instance of options for {AnalyticsIndexManager#replace_link}
|
|
421
421
|
#
|
|
422
422
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
423
|
-
# @param [
|
|
423
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
424
424
|
# @param [Hash, nil] client_context the client context data, if set
|
|
425
425
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
426
426
|
#
|
|
@@ -446,7 +446,7 @@ module Couchbase
|
|
|
446
446
|
# Creates an instance of options for {AnalyticsIndexManager#drop_link}
|
|
447
447
|
#
|
|
448
448
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to complete
|
|
449
|
-
# @param [
|
|
449
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
450
450
|
# @param [Hash, nil] client_context the client context data, if set
|
|
451
451
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
452
452
|
#
|
|
@@ -483,7 +483,7 @@ module Couchbase
|
|
|
483
483
|
#
|
|
484
484
|
# @param [Integer, #in_milliseconds, nil] timeout the time in milliseconds allowed for the operation to
|
|
485
485
|
# complete
|
|
486
|
-
# @param [
|
|
486
|
+
# @param [Symbol, nil] retry_strategy ignored by this operation
|
|
487
487
|
# @param [Hash, nil] client_context the client context data, if set
|
|
488
488
|
# @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
|
|
489
489
|
#
|