couchbase 3.5.0-arm64-darwin-22
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 +7 -0
- data/LICENSE.txt +202 -0
- data/README.md +154 -0
- data/ext/extconf.rb +0 -0
- data/lib/active_support/cache/couchbase_store.rb +339 -0
- data/lib/couchbase/3.0/libcouchbase.bundle +0 -0
- data/lib/couchbase/3.1/libcouchbase.bundle +0 -0
- data/lib/couchbase/3.2/libcouchbase.bundle +0 -0
- data/lib/couchbase/3.3/libcouchbase.bundle +0 -0
- data/lib/couchbase/analytics_options.rb +107 -0
- data/lib/couchbase/authenticator.rb +64 -0
- data/lib/couchbase/binary_collection.rb +128 -0
- data/lib/couchbase/binary_collection_options.rb +24 -0
- data/lib/couchbase/bucket.rb +144 -0
- data/lib/couchbase/cluster.rb +460 -0
- data/lib/couchbase/cluster_registry.rb +49 -0
- data/lib/couchbase/collection.rb +705 -0
- data/lib/couchbase/collection_options.rb +399 -0
- data/lib/couchbase/config_profiles.rb +55 -0
- data/lib/couchbase/configuration.rb +56 -0
- data/lib/couchbase/datastructures/couchbase_list.rb +160 -0
- data/lib/couchbase/datastructures/couchbase_map.rb +194 -0
- data/lib/couchbase/datastructures/couchbase_queue.rb +134 -0
- data/lib/couchbase/datastructures/couchbase_set.rb +128 -0
- data/lib/couchbase/datastructures.rb +24 -0
- data/lib/couchbase/diagnostics.rb +181 -0
- data/lib/couchbase/errors.rb +376 -0
- data/lib/couchbase/json_transcoder.rb +39 -0
- data/lib/couchbase/key_value_scan.rb +117 -0
- data/lib/couchbase/libcouchbase.rb +6 -0
- data/lib/couchbase/logger.rb +85 -0
- data/lib/couchbase/management/analytics_index_manager.rb +1127 -0
- data/lib/couchbase/management/bucket_manager.rb +443 -0
- data/lib/couchbase/management/collection_manager.rb +470 -0
- data/lib/couchbase/management/collection_query_index_manager.rb +222 -0
- data/lib/couchbase/management/query_index_manager.rb +617 -0
- data/lib/couchbase/management/scope_search_index_manager.rb +198 -0
- data/lib/couchbase/management/search_index_manager.rb +424 -0
- data/lib/couchbase/management/user_manager.rb +468 -0
- data/lib/couchbase/management/view_index_manager.rb +237 -0
- data/lib/couchbase/management.rb +29 -0
- data/lib/couchbase/mutation_state.rb +63 -0
- data/lib/couchbase/options.rb +2837 -0
- data/lib/couchbase/protostellar/binary_collection.rb +55 -0
- data/lib/couchbase/protostellar/bucket.rb +51 -0
- data/lib/couchbase/protostellar/client.rb +99 -0
- data/lib/couchbase/protostellar/cluster.rb +163 -0
- data/lib/couchbase/protostellar/collection.rb +152 -0
- data/lib/couchbase/protostellar/connect_options.rb +63 -0
- data/lib/couchbase/protostellar/error_handling.rb +203 -0
- data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +61 -0
- data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +35 -0
- data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +57 -0
- data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +36 -0
- data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +61 -0
- data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +37 -0
- data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +72 -0
- data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +44 -0
- data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +52 -0
- data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +30 -0
- data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +70 -0
- data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +36 -0
- data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +97 -0
- data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +46 -0
- data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +57 -0
- data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +30 -0
- data/lib/couchbase/protostellar/generated/routing/v1/routing_pb.rb +52 -0
- data/lib/couchbase/protostellar/generated/routing/v1/routing_services_pb.rb +30 -0
- data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +99 -0
- data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +30 -0
- data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +57 -0
- data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +36 -0
- data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +51 -0
- data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +30 -0
- data/lib/couchbase/protostellar/generated.rb +9 -0
- data/lib/couchbase/protostellar/management/bucket_manager.rb +67 -0
- data/lib/couchbase/protostellar/management/collection_manager.rb +94 -0
- data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +124 -0
- data/lib/couchbase/protostellar/management/query_index_manager.rb +112 -0
- data/lib/couchbase/protostellar/management.rb +24 -0
- data/lib/couchbase/protostellar/request.rb +78 -0
- data/lib/couchbase/protostellar/request_behaviour.rb +42 -0
- data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +124 -0
- data/lib/couchbase/protostellar/request_generator/admin/collection.rb +94 -0
- data/lib/couchbase/protostellar/request_generator/admin/query.rb +130 -0
- data/lib/couchbase/protostellar/request_generator/admin.rb +24 -0
- data/lib/couchbase/protostellar/request_generator/kv.rb +474 -0
- data/lib/couchbase/protostellar/request_generator/query.rb +133 -0
- data/lib/couchbase/protostellar/request_generator/search.rb +387 -0
- data/lib/couchbase/protostellar/request_generator.rb +26 -0
- data/lib/couchbase/protostellar/response_converter/admin/bucket.rb +55 -0
- data/lib/couchbase/protostellar/response_converter/admin/collection.rb +42 -0
- data/lib/couchbase/protostellar/response_converter/admin/query.rb +59 -0
- data/lib/couchbase/protostellar/response_converter/admin.rb +24 -0
- data/lib/couchbase/protostellar/response_converter/kv.rb +151 -0
- data/lib/couchbase/protostellar/response_converter/query.rb +84 -0
- data/lib/couchbase/protostellar/response_converter/search.rb +136 -0
- data/lib/couchbase/protostellar/response_converter.rb +26 -0
- data/lib/couchbase/protostellar/retry/action.rb +38 -0
- data/lib/couchbase/protostellar/retry/orchestrator.rb +60 -0
- data/lib/couchbase/protostellar/retry/reason.rb +67 -0
- data/lib/couchbase/protostellar/retry/strategies/best_effort.rb +49 -0
- data/lib/couchbase/protostellar/retry/strategies.rb +26 -0
- data/lib/couchbase/protostellar/retry.rb +28 -0
- data/lib/couchbase/protostellar/scope.rb +57 -0
- data/lib/couchbase/protostellar/timeout_defaults.rb +30 -0
- data/lib/couchbase/protostellar/timeouts.rb +83 -0
- data/lib/couchbase/protostellar.rb +29 -0
- data/lib/couchbase/query_options.rb +120 -0
- data/lib/couchbase/railtie.rb +45 -0
- data/lib/couchbase/raw_binary_transcoder.rb +37 -0
- data/lib/couchbase/raw_json_transcoder.rb +38 -0
- data/lib/couchbase/raw_string_transcoder.rb +40 -0
- data/lib/couchbase/scope.rb +256 -0
- data/lib/couchbase/search_options.rb +1622 -0
- data/lib/couchbase/subdoc.rb +290 -0
- data/lib/couchbase/transcoder_flags.rb +62 -0
- data/lib/couchbase/utils/generic_logger_adapter.rb +38 -0
- data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
- data/lib/couchbase/utils/time.rb +69 -0
- data/lib/couchbase/utils.rb +21 -0
- data/lib/couchbase/version.rb +23 -0
- data/lib/couchbase/view_options.rb +65 -0
- data/lib/couchbase.rb +28 -0
- data/lib/rails/generators/couchbase/config/config_generator.rb +27 -0
- metadata +191 -0
@@ -0,0 +1,339 @@
|
|
1
|
+
# Copyright 2020-2021 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
|
+
require "digest/sha2"
|
17
|
+
require "active_support/cache"
|
18
|
+
|
19
|
+
module ActiveSupport
|
20
|
+
module Cache
|
21
|
+
# A cache store implementation which stores data in Couchbase: https://couchbase.com
|
22
|
+
#
|
23
|
+
# * Local cache. Hot in-memory primary cache within block/middleware scope.
|
24
|
+
# * +read_multi+ and +write_multi+ support.
|
25
|
+
# * +delete_matched+ support using N1QL queries.
|
26
|
+
# * +clear+ for erasing whole collection (optionally can flush the bucket).
|
27
|
+
#
|
28
|
+
# To use this store, add the select it in application config
|
29
|
+
#
|
30
|
+
# config.cache_store = :couchbase_store, {
|
31
|
+
# connection_string: "couchbase://localhost",
|
32
|
+
# username: "app_cache_user",
|
33
|
+
# password: "s3cret",
|
34
|
+
# bucket: "app_cache"
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
# @see https://guides.rubyonrails.org/caching_with_rails.html#cache-stores
|
38
|
+
class CouchbaseStore < Store
|
39
|
+
MAX_KEY_BYTESIZE = 250
|
40
|
+
DEFAULT_ERROR_HANDLER = lambda do |method:, returning:, exception:, logger: CouchbaseStore.logger|
|
41
|
+
logger&.error { "CouchbaseStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Advertise cache versioning support.
|
45
|
+
def self.supports_cache_versioning?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
prepend Strategy::LocalCache
|
50
|
+
|
51
|
+
def initialize(options = nil)
|
52
|
+
super
|
53
|
+
@error_handler = @options.delete(:error_handler) { DEFAULT_ERROR_HANDLER }
|
54
|
+
@couchbase_options = {}
|
55
|
+
@couchbase_options[:connection_string] =
|
56
|
+
@options.delete(:connection_string) do
|
57
|
+
raise ArgumentError, "Missing connection string for Couchbase cache store. Use :connection_string in the store options"
|
58
|
+
end
|
59
|
+
@couchbase_options[:username] =
|
60
|
+
@options.delete(:username) do
|
61
|
+
raise ArgumentError, "Missing username for Couchbase cache store. Use :username in the store options"
|
62
|
+
end
|
63
|
+
@couchbase_options[:password] =
|
64
|
+
@options.delete(:password) do
|
65
|
+
raise ArgumentError, "Missing password for Couchbase cache store. Use :password in the store options"
|
66
|
+
end
|
67
|
+
@couchbase_options[:bucket] =
|
68
|
+
@options.delete(:bucket) { raise ArgumentError, "Missing bucket for Couchbase cache store. Use :bucket in the store options" }
|
69
|
+
@couchbase_options[:scope] = @options.delete(:scope) if @options.key?(:scope)
|
70
|
+
@couchbase_options[:collection] = @options.delete(:collection) if @options.key?(:collection)
|
71
|
+
@last_mutation_token = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def collection
|
75
|
+
@collection ||= build_collection
|
76
|
+
end
|
77
|
+
|
78
|
+
def cluster
|
79
|
+
@cluster ||= build_cluster
|
80
|
+
end
|
81
|
+
|
82
|
+
def inspect
|
83
|
+
"#<#{self.class} options=#{options.inspect} collection=#{@collection.inspect}>"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Deletes all entries with keys matching the regular expression.
|
87
|
+
#
|
88
|
+
# The +matcher+ must be valid pattern for N1QL +REGEXP_MATCHES+ function. More info at
|
89
|
+
# https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/patternmatchingfun.html#section_regex_matches
|
90
|
+
#
|
91
|
+
# Because the operation performed on query engine, and it might take time to propagate changes
|
92
|
+
# from key/value engine to the indexer. Therefore the keys, that were created a moment ago
|
93
|
+
# might not be deleted.
|
94
|
+
#
|
95
|
+
# Also this method assumes, that primary index created on the target bucket
|
96
|
+
def delete_matched(matcher, _options = nil)
|
97
|
+
pattern =
|
98
|
+
case matcher
|
99
|
+
when Regexp
|
100
|
+
matcher.inspect[1..-2]
|
101
|
+
when String
|
102
|
+
matcher.tr("?", ".").gsub("*", ".*")
|
103
|
+
else
|
104
|
+
raise NotImplementedError, "Unable to convert #{matcher.inspect} to Regexp pattern"
|
105
|
+
end
|
106
|
+
operation_options = ::Couchbase::Options::Query(named_parameters: {"pattern" => pattern}, metrics: true)
|
107
|
+
operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
|
108
|
+
begin
|
109
|
+
result = cluster.query("DELETE FROM #{scope_qualifier} cache WHERE REGEXP_MATCHES(META(cache).id, $pattern)", operation_options)
|
110
|
+
result.meta_data.metrics.mutation_count
|
111
|
+
rescue ::Couchbase::Error::ParsingFailure, ::Couchbase::Error::ServiceNotAvailable
|
112
|
+
raise NotImplementedError, "The server does not support delete_matched operation"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Increments an integer value in the cache.
|
117
|
+
#
|
118
|
+
# Note that it uses binary collection interface, therefore will fail if the value is not
|
119
|
+
# represented as ASCII-encoded number using +:raw+.
|
120
|
+
def increment(name, amount = 1, expires_in: nil, initial: nil, **options)
|
121
|
+
instrument :increment, name, amount: amount do
|
122
|
+
failsafe :increment do
|
123
|
+
key = normalize_key(name, options)
|
124
|
+
res = collection.binary.increment(
|
125
|
+
key, ::Couchbase::Options::Increment(delta: amount, expiry: expires_in, initial: initial)
|
126
|
+
)
|
127
|
+
@last_mutation_token = res.mutation_token
|
128
|
+
res.content
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Decrements an integer value in the cache.
|
134
|
+
#
|
135
|
+
# Note that it uses binary collection interface, therefore will fail if the value is not
|
136
|
+
# represented as ASCII-encoded number using +:raw+.
|
137
|
+
#
|
138
|
+
# Note that the counter represented by non-negative number on the server, and it will not
|
139
|
+
# cycle to maximum integer when decrementing zero.
|
140
|
+
def decrement(name, amount = 1, expires_in: nil, initial: nil, **_options)
|
141
|
+
instrument :decrement, name, amount: amount do
|
142
|
+
failsafe :decrement do
|
143
|
+
key = normalize_key(name, options)
|
144
|
+
res = collection.binary.decrement(
|
145
|
+
key, ::Couchbase::Options::Decrement(delta: amount, expiry: expires_in, initial: initial)
|
146
|
+
)
|
147
|
+
@last_mutation_token = res.mutation_token
|
148
|
+
res.content
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Clears the entire cache. Be careful with this method since it could
|
154
|
+
# affect other processes if shared cache is being used.
|
155
|
+
#
|
156
|
+
# When +use_flush+ option set to +true+ it will flush the bucket. Otherwise, it uses N1QL query
|
157
|
+
# and relies on default index.
|
158
|
+
def clear(use_flush: false, **_options)
|
159
|
+
failsafe(:clear) do
|
160
|
+
if use_flush
|
161
|
+
cluster.buckets.flush_bucket(@couchbase_options[:bucket_name])
|
162
|
+
else
|
163
|
+
operation_options = ::Couchbase::Options::Query.new
|
164
|
+
operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
|
165
|
+
cluster.query("DELETE FROM #{scope_qualifier}", operation_options)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def deserialize_entry(payload, raw: false, **)
|
173
|
+
if payload && raw
|
174
|
+
Entry.new(payload, compress: false)
|
175
|
+
else
|
176
|
+
super(payload)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def serialize_entry(entry, raw: false, **options)
|
181
|
+
if raw
|
182
|
+
entry.value.to_s
|
183
|
+
else
|
184
|
+
super(entry, **options)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def serialize_entries(entries, **options)
|
189
|
+
entries.transform_values do |entry|
|
190
|
+
serialize_entry(entry, **options)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Reads an entry from the cache
|
195
|
+
def read_entry(key, **options)
|
196
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def read_serialized_entry(key, **)
|
200
|
+
failsafe(:read_entry, returning: nil) do
|
201
|
+
res = collection.get(key, ::Couchbase::Options::Get(transcoder: nil))
|
202
|
+
res.content
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Reads multiple entries from the cache implementation. Subclasses MAY
|
207
|
+
# implement this method.
|
208
|
+
def read_multi_entries(names, **options)
|
209
|
+
return {} if names.empty?
|
210
|
+
|
211
|
+
keys = names.map { |name| normalize_key(name, options) }
|
212
|
+
return_value = {}
|
213
|
+
failsafe(:read_multi_entries, returning: return_value) do
|
214
|
+
results = collection.get_multi(keys, ::Couchbase::Options::GetMulti(transcoder: nil))
|
215
|
+
results.each_with_index do |result, index|
|
216
|
+
next unless result.success?
|
217
|
+
|
218
|
+
entry = deserialize_entry(result.content, raw: options[:raw])
|
219
|
+
unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(names[index], options))
|
220
|
+
return_value[names[index]] = entry.value
|
221
|
+
end
|
222
|
+
end
|
223
|
+
return_value
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Writes an entry to the cache
|
228
|
+
def write_entry(key, entry, raw: false, **options)
|
229
|
+
write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
|
230
|
+
end
|
231
|
+
|
232
|
+
def write_serialized_entry(key, payload, expires_in: nil, race_condition_ttl: nil, raw: false, **)
|
233
|
+
if race_condition_ttl && expires_in && expires_in.positive? && !raw
|
234
|
+
# Add few minutes to expiry in the future to support race condition TTLs on read
|
235
|
+
expires_in += 5.minutes
|
236
|
+
end
|
237
|
+
failsafe(:write_entry, returning: false) do
|
238
|
+
res = collection.upsert(key, payload, ::Couchbase::Options::Upsert(transcoder: nil, expiry: expires_in))
|
239
|
+
@last_mutation_token = res.mutation_token
|
240
|
+
true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def write_multi_entries(entries, **options)
|
245
|
+
return 0 if entries.empty?
|
246
|
+
|
247
|
+
return super if local_cache
|
248
|
+
|
249
|
+
expires_in = options[:expires_in]
|
250
|
+
if options[:race_condition_ttl] && expires_in && expires_in.positive?
|
251
|
+
# Add few minutes to expiry in the future to support race condition TTLs on read
|
252
|
+
expires_in += 5.minutes
|
253
|
+
end
|
254
|
+
operation_options = ::Couchbase::Options::UpsertMulti(transcoder: nil, expiry: expires_in)
|
255
|
+
failsafe(:write_multi_entries, returning: nil) do
|
256
|
+
successful = collection.upsert_multi(serialize_entries(entries, **options), operation_options).select(&:success?)
|
257
|
+
return 0 if successful.empty?
|
258
|
+
|
259
|
+
@last_mutation_token = successful.max_by { |r| r.mutation_token.sequence_number }
|
260
|
+
successful.count
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Deletes an entry from the cache implementation. Subclasses must
|
265
|
+
# implement this method.
|
266
|
+
def delete_entry(key, **_options)
|
267
|
+
failsafe(:delete_entry, returning: false) do
|
268
|
+
res = collection.remove(key)
|
269
|
+
@last_mutation_token = res.mutation_token
|
270
|
+
true
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Deletes multiple entries in the cache. Returns the number of entries deleted.
|
275
|
+
def delete_multi_entries(entries, **_options)
|
276
|
+
return if entries.empty?
|
277
|
+
|
278
|
+
failsafe(:delete_multi_entries, returning: nil) do
|
279
|
+
successful = collection.remove_multi(entries).select(&:success?)
|
280
|
+
return 0 if successful.empty?
|
281
|
+
|
282
|
+
@last_mutation_token = successful.max_by { |r| r.mutation_token.sequence_number }
|
283
|
+
successful.count
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def failsafe(method, returning: nil)
|
288
|
+
yield
|
289
|
+
rescue ::Couchbase::Error::CouchbaseError => e
|
290
|
+
ActiveSupport.error_reporter&.report(e, handled: true, severity: :warning)
|
291
|
+
@error_handler&.call(method: method, exception: e, returning: returning)
|
292
|
+
returning
|
293
|
+
end
|
294
|
+
|
295
|
+
# Truncate keys that exceed 250 characters
|
296
|
+
def normalize_key(key, options)
|
297
|
+
truncate_key super
|
298
|
+
end
|
299
|
+
|
300
|
+
def truncate_key(key)
|
301
|
+
if key && key.bytesize > MAX_KEY_BYTESIZE
|
302
|
+
suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
|
303
|
+
truncate_at = MAX_KEY_BYTESIZE - suffix.bytesize
|
304
|
+
"#{key.mb_chars.limit(truncate_at)}#{suffix}"
|
305
|
+
else
|
306
|
+
key
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
# Connects to the Couchbase cluster
|
311
|
+
def build_cluster
|
312
|
+
::Couchbase::Cluster.connect(
|
313
|
+
@couchbase_options[:connection_string],
|
314
|
+
::Couchbase::Options::Cluster(authenticator: ::Couchbase::PasswordAuthenticator.new(
|
315
|
+
@couchbase_options[:username], @couchbase_options[:password]
|
316
|
+
))
|
317
|
+
)
|
318
|
+
end
|
319
|
+
|
320
|
+
# Connects to the Couchbase cluster, opens specified bucket and returns collection object.
|
321
|
+
def build_collection
|
322
|
+
bucket = cluster.bucket(@couchbase_options[:bucket])
|
323
|
+
if @couchbase_options[:scope] && @couchbase_options[:collection]
|
324
|
+
bucket.scope(@couchbase_options[:scope]).collection(@couchbase_options[:collection])
|
325
|
+
else
|
326
|
+
bucket.default_collection
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def scope_qualifier
|
331
|
+
if @couchbase_options[:scope] && @couchbase_options[:collection]
|
332
|
+
"`#{@couchbase_options[:bucket]}`.#{@couchbase_options[:scope]}.#{@couchbase_options[:collection]}"
|
333
|
+
else
|
334
|
+
"`#{@couchbase_options[:bucket]}`"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Copyright 2020-2021 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/json_transcoder"
|
16
|
+
|
17
|
+
module Couchbase
|
18
|
+
class Cluster
|
19
|
+
class AnalyticsWarning
|
20
|
+
# @return [Integer]
|
21
|
+
attr_accessor :code
|
22
|
+
|
23
|
+
# @return [String]
|
24
|
+
attr_accessor :message
|
25
|
+
|
26
|
+
# @param [Integer] code
|
27
|
+
# @param [String] message
|
28
|
+
def initialize(code, message)
|
29
|
+
@code = code
|
30
|
+
@message = message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class AnalyticsMetrics
|
35
|
+
# @return [Integer] duration in milliseconds
|
36
|
+
attr_accessor :elapsed_time
|
37
|
+
|
38
|
+
# @return [Integer] duration in milliseconds
|
39
|
+
attr_accessor :execution_time
|
40
|
+
|
41
|
+
# @return [Integer]
|
42
|
+
attr_accessor :result_count
|
43
|
+
|
44
|
+
# @return [Integer]
|
45
|
+
attr_accessor :result_size
|
46
|
+
|
47
|
+
# @return [Integer]
|
48
|
+
attr_accessor :error_count
|
49
|
+
|
50
|
+
# @return [Integer]
|
51
|
+
attr_accessor :processed_objects
|
52
|
+
|
53
|
+
# @return [Integer]
|
54
|
+
attr_accessor :warning_count
|
55
|
+
|
56
|
+
# @yieldparam [AnalyticsMetrics] self
|
57
|
+
def initialize
|
58
|
+
yield self if block_given?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class AnalyticsMetaData
|
63
|
+
# @return [String]
|
64
|
+
attr_accessor :request_id
|
65
|
+
|
66
|
+
# @return [String]
|
67
|
+
attr_accessor :client_context_id
|
68
|
+
|
69
|
+
# @return [:running, :success, :errors, :completed, :stopped, :timeout, :closed, :fatal, :aborted, :unknown]
|
70
|
+
attr_accessor :status
|
71
|
+
|
72
|
+
# @return [Hash] returns the signature as returned by the query engine which is then decoded as JSON object
|
73
|
+
attr_accessor :signature
|
74
|
+
|
75
|
+
# @return [Array<AnalyticsWarning>]
|
76
|
+
attr_accessor :warnings
|
77
|
+
|
78
|
+
# @return [AnalyticsMetrics]
|
79
|
+
attr_accessor :metrics
|
80
|
+
|
81
|
+
# @yieldparam [AnalyticsMetaData] self
|
82
|
+
def initialize
|
83
|
+
yield self if block_given?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class AnalyticsResult
|
88
|
+
# @return [AnalyticsMetaData]
|
89
|
+
attr_accessor :meta_data
|
90
|
+
|
91
|
+
attr_accessor :transcoder
|
92
|
+
|
93
|
+
# Returns all rows converted using a transcoder
|
94
|
+
#
|
95
|
+
# @return [Array]
|
96
|
+
def rows(transcoder = self.transcoder)
|
97
|
+
@rows.lazy.map { |row| transcoder.decode(row, 0) }
|
98
|
+
end
|
99
|
+
|
100
|
+
# @yieldparam [AnalyticsResult] self
|
101
|
+
def initialize
|
102
|
+
@transcoder = JsonTranscoder.new
|
103
|
+
yield self if block_given?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright 2020-2021 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
|
+
module Couchbase
|
16
|
+
# Authenticator for username/password credentials
|
17
|
+
class PasswordAuthenticator
|
18
|
+
DEFAULT_SASL_MECHANISMS = [:scram_sha512, :scram_sha256, :scram_sha1].freeze
|
19
|
+
|
20
|
+
attr_accessor :username
|
21
|
+
attr_accessor :password
|
22
|
+
attr_accessor :allowed_sasl_mechanisms
|
23
|
+
|
24
|
+
# Creates a new password authenticator with the default settings.
|
25
|
+
#
|
26
|
+
# @param [String] password the username to use for all authentication requests
|
27
|
+
# @param [String] username the password
|
28
|
+
def initialize(username, password)
|
29
|
+
@username = username
|
30
|
+
@password = password
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a LDAP compatible password authenticator which is INSECURE if not used with TLS.
|
34
|
+
#
|
35
|
+
# Please note that this is INSECURE and will leak user credentials on the wire to eavesdroppers. This should
|
36
|
+
# only be enabled in trusted environments.
|
37
|
+
#
|
38
|
+
# @param [String] username the username to use for all authentication.
|
39
|
+
# @param [String] password the password to use alongside the username.
|
40
|
+
# @return [PasswordAuthenticator]
|
41
|
+
def self.ldap_compatible(username, password)
|
42
|
+
new(username, password).tap do |auth|
|
43
|
+
auth.allowed_sasl_mechanisms = [:plain]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Authenticator for TLS client certificate
|
49
|
+
#
|
50
|
+
# @see https://docs.couchbase.com/server/current/manage/manage-security/configure-client-certificates.html
|
51
|
+
class CertificateAuthenticator
|
52
|
+
attr_accessor :certificate_path
|
53
|
+
attr_accessor :key_path
|
54
|
+
|
55
|
+
# Creates a new authenticator with certificate and key paths
|
56
|
+
#
|
57
|
+
# @param [String] certificate_path path to certificate
|
58
|
+
# @param [String] key_path path to private key
|
59
|
+
def initialize(certificate_path, key_path)
|
60
|
+
@certificate_path = certificate_path
|
61
|
+
@key_path = key_path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# Copyright 2020-2021 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/options"
|
16
|
+
require "couchbase/binary_collection_options"
|
17
|
+
|
18
|
+
module Couchbase
|
19
|
+
# Allows to perform certain operations on non-JSON documents.
|
20
|
+
class BinaryCollection
|
21
|
+
alias inspect to_s
|
22
|
+
|
23
|
+
# @param [Couchbase::Collection] collection parent collection
|
24
|
+
def initialize(collection)
|
25
|
+
@collection = collection
|
26
|
+
@backend = collection.instance_variable_get(:@backend)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Appends binary content to the document
|
30
|
+
#
|
31
|
+
# @param [String] id the document id which is used to uniquely identify it
|
32
|
+
# @param [String] content the binary content to append to the document
|
33
|
+
# @param [Options::Append] options custom options to customize the request
|
34
|
+
#
|
35
|
+
# @example Append "bar" to the content of the existing document
|
36
|
+
# collection.upsert("mydoc", "foo")
|
37
|
+
# collection.binary.append("mydoc", "bar", Options::Append(timeout: 3_000))
|
38
|
+
# collection.get("mydoc", Options::Get(transcoder: nil)).content #=> "foobar"
|
39
|
+
#
|
40
|
+
# @return [Collection::MutationResult]
|
41
|
+
def append(id, content, options = Options::Append::DEFAULT)
|
42
|
+
resp = @backend.document_append(@collection.bucket_name, @collection.scope_name, @collection.name,
|
43
|
+
id, content, options.to_backend)
|
44
|
+
Collection::MutationResult.new do |res|
|
45
|
+
res.cas = resp[:cas]
|
46
|
+
res.mutation_token = @collection.send(:extract_mutation_token, resp)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Prepends binary content to the document
|
51
|
+
#
|
52
|
+
# @param [String] id the document id which is used to uniquely identify it
|
53
|
+
# @param [String] content the binary content to prepend to the document
|
54
|
+
# @param [Options::Prepend] options custom options to customize the request
|
55
|
+
#
|
56
|
+
# @example Prepend "bar" to the content of the existing document
|
57
|
+
# collection.upsert("mydoc", "foo")
|
58
|
+
# collection.binary.prepend("mydoc", "bar", Options::Prepend(timeout: 3_000))
|
59
|
+
# collection.get("mydoc", Options::Get(transcoder: nil)).content #=> "barfoo"
|
60
|
+
#
|
61
|
+
# @return [Collection::MutationResult]
|
62
|
+
def prepend(id, content, options = Options::Prepend::DEFAULT)
|
63
|
+
resp = @backend.document_prepend(@collection.bucket_name, @collection.scope_name, @collection.name,
|
64
|
+
id, content, options.to_backend)
|
65
|
+
Collection::MutationResult.new do |res|
|
66
|
+
res.cas = resp[:cas]
|
67
|
+
res.mutation_token = @collection.send(:extract_mutation_token, resp)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Increments the counter document by one of the number defined in the options
|
72
|
+
#
|
73
|
+
# @param [String] id the document id which is used to uniquely identify it
|
74
|
+
# @param [Options::Increment] options custom options to customize the request
|
75
|
+
#
|
76
|
+
# @example Increment value by 10, and initialize to 0 if it does not exist
|
77
|
+
# res = collection.binary.increment("raw_counter", Options::Increment(delta: 10, initial: 0))
|
78
|
+
# res.content #=> 0
|
79
|
+
# res = collection.binary.increment("raw_counter", Options::Increment(delta: 10, initial: 0))
|
80
|
+
# res.content #=> 10
|
81
|
+
#
|
82
|
+
# @return [CounterResult]
|
83
|
+
def increment(id, options = Options::Increment::DEFAULT)
|
84
|
+
resp = @backend.document_increment(@collection.bucket_name, @collection.scope_name, @collection.name, id,
|
85
|
+
options.to_backend)
|
86
|
+
CounterResult.new do |res|
|
87
|
+
res.cas = resp[:cas]
|
88
|
+
res.content = resp[:content]
|
89
|
+
res.mutation_token = @collection.send(:extract_mutation_token, resp)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Decrements the counter document by one of the number defined in the options
|
94
|
+
#
|
95
|
+
# @param [String] id the document id which is used to uniquely identify it
|
96
|
+
# @param [Options::Decrement] options custom options to customize the request
|
97
|
+
#
|
98
|
+
# @example Decrement value by 2, and initialize to 100 if it does not exist
|
99
|
+
# res = collection.binary.decrement("raw_counter", Options::Decrement(delta: 2, initial: 100))
|
100
|
+
# res.value #=> 100
|
101
|
+
# res = collection.binary.decrement("raw_counter", Options::Decrement(delta: 2, initial: 100))
|
102
|
+
# res.value #=> 98
|
103
|
+
#
|
104
|
+
# @return [CounterResult]
|
105
|
+
def decrement(id, options = Options::Decrement::DEFAULT)
|
106
|
+
resp = @backend.document_decrement(@collection.bucket_name, @collection.scope_name, @collection.name, id,
|
107
|
+
options.to_backend)
|
108
|
+
CounterResult.new do |res|
|
109
|
+
res.cas = resp[:cas]
|
110
|
+
res.content = resp[:content]
|
111
|
+
res.mutation_token = @collection.send(:extract_mutation_token, resp)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
# TODO: deprecate in 3.1
|
117
|
+
AppendOptions = ::Couchbase::Options::Append
|
118
|
+
# @api private
|
119
|
+
# TODO: deprecate in 3.1
|
120
|
+
PrependOptions = ::Couchbase::Options::Prepend
|
121
|
+
# @api private
|
122
|
+
# TODO: deprecate in 3.1
|
123
|
+
IncrementOptions = ::Couchbase::Options::Increment
|
124
|
+
# @api private
|
125
|
+
# TODO: deprecate in 3.1
|
126
|
+
DecrementOptions = ::Couchbase::Options::Decrement
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright 2020-2021 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/collection_options"
|
16
|
+
|
17
|
+
module Couchbase
|
18
|
+
class BinaryCollection
|
19
|
+
class CounterResult < ::Couchbase::Collection::MutationResult
|
20
|
+
# @return [Integer] current value of the counter
|
21
|
+
attr_accessor :content
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|