couchbase 3.9.0-aarch64-linux-musl

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.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +202 -0
  3. data/README.md +154 -0
  4. data/ext/extconf.rb +0 -0
  5. data/lib/active_support/cache/couchbase_store.rb +407 -0
  6. data/lib/couchbase/3.2/libcouchbase.so +0 -0
  7. data/lib/couchbase/3.3/libcouchbase.so +0 -0
  8. data/lib/couchbase/3.4/libcouchbase.so +0 -0
  9. data/lib/couchbase/4.0/libcouchbase.so +0 -0
  10. data/lib/couchbase/analytics_options.rb +109 -0
  11. data/lib/couchbase/authenticator.rb +80 -0
  12. data/lib/couchbase/binary_collection.rb +145 -0
  13. data/lib/couchbase/binary_collection_options.rb +26 -0
  14. data/lib/couchbase/bucket.rb +173 -0
  15. data/lib/couchbase/cluster.rb +555 -0
  16. data/lib/couchbase/cluster_registry.rb +49 -0
  17. data/lib/couchbase/collection.rb +770 -0
  18. data/lib/couchbase/collection_options.rb +401 -0
  19. data/lib/couchbase/config_profiles.rb +57 -0
  20. data/lib/couchbase/configuration.rb +58 -0
  21. data/lib/couchbase/datastructures/couchbase_list.rb +192 -0
  22. data/lib/couchbase/datastructures/couchbase_map.rb +231 -0
  23. data/lib/couchbase/datastructures/couchbase_queue.rb +161 -0
  24. data/lib/couchbase/datastructures/couchbase_set.rb +145 -0
  25. data/lib/couchbase/datastructures.rb +26 -0
  26. data/lib/couchbase/deprecations.rb +61 -0
  27. data/lib/couchbase/diagnostics.rb +183 -0
  28. data/lib/couchbase/errors.rb +438 -0
  29. data/lib/couchbase/fork_hooks.rb +41 -0
  30. data/lib/couchbase/get_replica_strategy.rb +89 -0
  31. data/lib/couchbase/json_transcoder.rb +41 -0
  32. data/lib/couchbase/key_value_scan.rb +119 -0
  33. data/lib/couchbase/libcouchbase.rb +6 -0
  34. data/lib/couchbase/logger.rb +87 -0
  35. data/lib/couchbase/management/analytics_index_manager.rb +1160 -0
  36. data/lib/couchbase/management/bucket_manager.rb +479 -0
  37. data/lib/couchbase/management/collection_manager.rb +515 -0
  38. data/lib/couchbase/management/collection_query_index_manager.rb +247 -0
  39. data/lib/couchbase/management/query_index_manager.rb +671 -0
  40. data/lib/couchbase/management/scope_search_index_manager.rb +267 -0
  41. data/lib/couchbase/management/search_index_manager.rb +649 -0
  42. data/lib/couchbase/management/user_manager.rb +639 -0
  43. data/lib/couchbase/management/view_index_manager.rb +332 -0
  44. data/lib/couchbase/management.rb +31 -0
  45. data/lib/couchbase/metrics/logging_meter.rb +108 -0
  46. data/lib/couchbase/metrics/logging_value_recorder.rb +50 -0
  47. data/lib/couchbase/metrics/meter.rb +27 -0
  48. data/lib/couchbase/metrics/noop_meter.rb +30 -0
  49. data/lib/couchbase/metrics/noop_value_recorder.rb +27 -0
  50. data/lib/couchbase/metrics/value_recorder.rb +25 -0
  51. data/lib/couchbase/mutation_state.rb +65 -0
  52. data/lib/couchbase/options.rb +3097 -0
  53. data/lib/couchbase/protostellar/binary_collection.rb +55 -0
  54. data/lib/couchbase/protostellar/bucket.rb +59 -0
  55. data/lib/couchbase/protostellar/client.rb +97 -0
  56. data/lib/couchbase/protostellar/cluster.rb +182 -0
  57. data/lib/couchbase/protostellar/collection.rb +156 -0
  58. data/lib/couchbase/protostellar/connect_options.rb +63 -0
  59. data/lib/couchbase/protostellar/error_handling.rb +204 -0
  60. data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_pb.rb +54 -0
  61. data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_services_pb.rb +51 -0
  62. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +42 -0
  63. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +51 -0
  64. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +38 -0
  65. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +52 -0
  66. data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +42 -0
  67. data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +55 -0
  68. data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +51 -0
  69. data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +67 -0
  70. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +31 -0
  71. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +40 -0
  72. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +51 -0
  73. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +54 -0
  74. data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_pb.rb +46 -0
  75. data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_services_pb.rb +56 -0
  76. data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +74 -0
  77. data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +93 -0
  78. data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +35 -0
  79. data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +40 -0
  80. data/lib/couchbase/protostellar/generated/routing/v2/routing_pb.rb +26 -0
  81. data/lib/couchbase/protostellar/generated/routing/v2/routing_services_pb.rb +43 -0
  82. data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +78 -0
  83. data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +41 -0
  84. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +36 -0
  85. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +66 -0
  86. data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +30 -0
  87. data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +39 -0
  88. data/lib/couchbase/protostellar/generated.rb +9 -0
  89. data/lib/couchbase/protostellar/management/bucket_manager.rb +67 -0
  90. data/lib/couchbase/protostellar/management/collection_manager.rb +94 -0
  91. data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +124 -0
  92. data/lib/couchbase/protostellar/management/query_index_manager.rb +112 -0
  93. data/lib/couchbase/protostellar/management.rb +24 -0
  94. data/lib/couchbase/protostellar/request.rb +79 -0
  95. data/lib/couchbase/protostellar/request_behaviour.rb +42 -0
  96. data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +124 -0
  97. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +96 -0
  98. data/lib/couchbase/protostellar/request_generator/admin/query.rb +132 -0
  99. data/lib/couchbase/protostellar/request_generator/admin.rb +24 -0
  100. data/lib/couchbase/protostellar/request_generator/kv.rb +488 -0
  101. data/lib/couchbase/protostellar/request_generator/query.rb +133 -0
  102. data/lib/couchbase/protostellar/request_generator/search.rb +403 -0
  103. data/lib/couchbase/protostellar/request_generator.rb +26 -0
  104. data/lib/couchbase/protostellar/response_converter/admin/bucket.rb +55 -0
  105. data/lib/couchbase/protostellar/response_converter/admin/collection.rb +42 -0
  106. data/lib/couchbase/protostellar/response_converter/admin/query.rb +59 -0
  107. data/lib/couchbase/protostellar/response_converter/admin.rb +24 -0
  108. data/lib/couchbase/protostellar/response_converter/kv.rb +151 -0
  109. data/lib/couchbase/protostellar/response_converter/query.rb +84 -0
  110. data/lib/couchbase/protostellar/response_converter/search.rb +136 -0
  111. data/lib/couchbase/protostellar/response_converter.rb +26 -0
  112. data/lib/couchbase/protostellar/retry/action.rb +38 -0
  113. data/lib/couchbase/protostellar/retry/orchestrator.rb +61 -0
  114. data/lib/couchbase/protostellar/retry/reason.rb +67 -0
  115. data/lib/couchbase/protostellar/retry/strategies/best_effort.rb +49 -0
  116. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  117. data/lib/couchbase/protostellar/retry/strategies.rb +27 -0
  118. data/lib/couchbase/protostellar/retry.rb +28 -0
  119. data/lib/couchbase/protostellar/scope.rb +61 -0
  120. data/lib/couchbase/protostellar/timeout_defaults.rb +30 -0
  121. data/lib/couchbase/protostellar/timeouts.rb +83 -0
  122. data/lib/couchbase/protostellar.rb +29 -0
  123. data/lib/couchbase/query_options.rb +122 -0
  124. data/lib/couchbase/railtie.rb +47 -0
  125. data/lib/couchbase/raw_binary_transcoder.rb +39 -0
  126. data/lib/couchbase/raw_json_transcoder.rb +40 -0
  127. data/lib/couchbase/raw_string_transcoder.rb +42 -0
  128. data/lib/couchbase/scope.rb +272 -0
  129. data/lib/couchbase/search_options.rb +1744 -0
  130. data/lib/couchbase/subdoc.rb +293 -0
  131. data/lib/couchbase/tracing/noop_span.rb +29 -0
  132. data/lib/couchbase/tracing/noop_tracer.rb +29 -0
  133. data/lib/couchbase/tracing/request_span.rb +34 -0
  134. data/lib/couchbase/tracing/request_tracer.rb +28 -0
  135. data/lib/couchbase/tracing/threshold_logging_span.rb +112 -0
  136. data/lib/couchbase/tracing/threshold_logging_tracer.rb +231 -0
  137. data/lib/couchbase/transcoder_flags.rb +64 -0
  138. data/lib/couchbase/utils/generic_logger_adapter.rb +40 -0
  139. data/lib/couchbase/utils/hdr_histogram.rb +55 -0
  140. data/lib/couchbase/utils/observability.rb +263 -0
  141. data/lib/couchbase/utils/observability_constants.rb +200 -0
  142. data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
  143. data/lib/couchbase/utils/time.rb +71 -0
  144. data/lib/couchbase/utils.rb +23 -0
  145. data/lib/couchbase/version.rb +25 -0
  146. data/lib/couchbase/view_options.rb +67 -0
  147. data/lib/couchbase.rb +32 -0
  148. data/lib/rails/generators/couchbase/config/config_generator.rb +29 -0
  149. metadata +228 -0
@@ -0,0 +1,407 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020-2021 Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require "couchbase"
18
+ require "digest/sha2"
19
+ require "active_support/cache"
20
+
21
+ module ActiveSupport
22
+ module Cache
23
+ # A cache store implementation which stores data in Couchbase: https://couchbase.com
24
+ #
25
+ # * Local cache. Hot in-memory primary cache within block/middleware scope.
26
+ # * +read_multi+ and +write_multi+ support.
27
+ # * +delete_matched+ support using N1QL queries.
28
+ # * +clear+ for erasing whole collection (optionally can flush the bucket).
29
+ #
30
+ # To use this store, add the select it in application config
31
+ #
32
+ # config.cache_store = :couchbase_store, {
33
+ # connection_string: "couchbase://localhost",
34
+ # username: "app_cache_user",
35
+ # password: "s3cret",
36
+ # bucket: "app_cache"
37
+ # }
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
+ #
46
+ # @see https://guides.rubyonrails.org/caching_with_rails.html#cache-stores
47
+ class CouchbaseStore < Store
48
+ MAX_KEY_BYTESIZE = 250
49
+ DEFAULT_ERROR_HANDLER = lambda do |method:, returning:, exception:, logger: CouchbaseStore.logger|
50
+ logger&.error { "CouchbaseStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
51
+ end
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
+
98
+ # Advertise cache versioning support.
99
+ def self.supports_cache_versioning?
100
+ true
101
+ end
102
+
103
+ prepend Strategy::LocalCache
104
+
105
+ def initialize(options = nil)
106
+ super
107
+ @error_handler = @options.delete(:error_handler) { DEFAULT_ERROR_HANDLER }
108
+ @couchbase_options = {}
109
+ @couchbase_options[:connection_string] =
110
+ @options.delete(:connection_string) do
111
+ raise ArgumentError, "Missing connection string for Couchbase cache store. Use :connection_string in the store options"
112
+ end
113
+ @couchbase_options[:username] =
114
+ @options.delete(:username) do
115
+ raise ArgumentError, "Missing username for Couchbase cache store. Use :username in the store options"
116
+ end
117
+ @couchbase_options[:password] =
118
+ @options.delete(:password) do
119
+ raise ArgumentError, "Missing password for Couchbase cache store. Use :password in the store options"
120
+ end
121
+ @couchbase_options[:bucket] =
122
+ @options.delete(:bucket) { raise ArgumentError, "Missing bucket for Couchbase cache store. Use :bucket in the store options" }
123
+ @couchbase_options[:scope] = @options.delete(:scope) if @options.key?(:scope)
124
+ @couchbase_options[:collection] = @options.delete(:collection) if @options.key?(:collection)
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
136
+ end
137
+
138
+ def collection
139
+ @collection ||= build_collection
140
+ end
141
+
142
+ def cluster
143
+ @cluster ||= build_cluster
144
+ end
145
+
146
+ def inspect
147
+ "#<#{self.class} options=#{options.inspect} collection=#{@collection.inspect}>"
148
+ end
149
+
150
+ # Deletes all entries with keys matching the regular expression.
151
+ #
152
+ # The +matcher+ must be valid pattern for N1QL +REGEXP_MATCHES+ function. More info at
153
+ # https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/patternmatchingfun.html#section_regex_matches
154
+ #
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.
159
+ #
160
+ # Also this method assumes, that primary index created on the target bucket
161
+ def delete_matched(matcher, _options = nil)
162
+ pattern =
163
+ case matcher
164
+ when Regexp
165
+ matcher.inspect[1..-2]
166
+ when String
167
+ matcher.tr("?", ".").gsub("*", ".*")
168
+ else
169
+ raise NotImplementedError, "Unable to convert #{matcher.inspect} to Regexp pattern"
170
+ end
171
+ operation_options = ::Couchbase::Options::Query(named_parameters: {"pattern" => pattern}, metrics: true)
172
+ mutation_state = @mutations.mutation_state
173
+ operation_options.consistent_with(mutation_state) if mutation_state
174
+ begin
175
+ result = cluster.query("DELETE FROM #{scope_qualifier} cache_store_ WHERE REGEXP_MATCHES(META(cache_store_).id, $pattern)",
176
+ operation_options)
177
+ result.meta_data.metrics.mutation_count
178
+ rescue ::Couchbase::Error::ParsingFailure, ::Couchbase::Error::ServiceNotAvailable
179
+ raise NotImplementedError, "The server does not support delete_matched operation"
180
+ end
181
+ end
182
+
183
+ # Increments an integer value in the cache.
184
+ #
185
+ # Note that it uses binary collection interface, therefore will fail if the value is not
186
+ # represented as ASCII-encoded number using +:raw+.
187
+ def increment(name, amount = 1, expires_in: nil, initial: nil, **options)
188
+ instrument :increment, name, amount: amount do
189
+ failsafe :increment do
190
+ key = normalize_key(name, options)
191
+ res = collection.binary.increment(
192
+ key, ::Couchbase::Options::Increment(delta: amount, expiry: expires_in, initial: initial)
193
+ )
194
+ @mutations.record(res.mutation_token)
195
+ res.content
196
+ end
197
+ end
198
+ end
199
+
200
+ # Decrements an integer value in the cache.
201
+ #
202
+ # Note that it uses binary collection interface, therefore will fail if the value is not
203
+ # represented as ASCII-encoded number using +:raw+.
204
+ #
205
+ # Note that the counter represented by non-negative number on the server, and it will not
206
+ # cycle to maximum integer when decrementing zero.
207
+ def decrement(name, amount = 1, expires_in: nil, initial: nil, **_options)
208
+ instrument :decrement, name, amount: amount do
209
+ failsafe :decrement do
210
+ key = normalize_key(name, options)
211
+ res = collection.binary.decrement(
212
+ key, ::Couchbase::Options::Decrement(delta: amount, expiry: expires_in, initial: initial)
213
+ )
214
+ @mutations.record(res.mutation_token)
215
+ res.content
216
+ end
217
+ end
218
+ end
219
+
220
+ # Clears the entire cache. Be careful with this method since it could
221
+ # affect other processes if shared cache is being used.
222
+ #
223
+ # When +use_flush+ option set to +true+ it will flush the bucket. Otherwise, it uses N1QL query
224
+ # and relies on default index.
225
+ def clear(use_flush: false, **_options)
226
+ failsafe(:clear) do
227
+ if use_flush
228
+ cluster.buckets.flush_bucket(@couchbase_options[:bucket_name])
229
+ else
230
+ operation_options = ::Couchbase::Options::Query.new
231
+ mutation_state = @mutations.mutation_state
232
+ operation_options.consistent_with(mutation_state) if mutation_state
233
+ cluster.query("DELETE FROM #{scope_qualifier}", operation_options)
234
+ end
235
+ end
236
+ end
237
+
238
+ private
239
+
240
+ def deserialize_entry(payload, raw: false, **)
241
+ if payload && raw
242
+ Entry.new(payload, compress: false)
243
+ else
244
+ super(payload)
245
+ end
246
+ end
247
+
248
+ def serialize_entry(entry, raw: false, **)
249
+ if raw
250
+ entry.value.to_s
251
+ else
252
+ super(entry, **)
253
+ end
254
+ end
255
+
256
+ def serialize_entries(entries, **options)
257
+ entries.transform_values do |entry|
258
+ serialize_entry(entry, **options)
259
+ end
260
+ end
261
+
262
+ # Reads an entry from the cache
263
+ def read_entry(key, **)
264
+ deserialize_entry(read_serialized_entry(key, **), **)
265
+ end
266
+
267
+ def read_serialized_entry(key, **)
268
+ failsafe(:read_entry, returning: nil) do
269
+ res = collection.get(key, ::Couchbase::Options::Get(transcoder: nil))
270
+ res.content
271
+ end
272
+ end
273
+
274
+ # Reads multiple entries from the cache implementation. Subclasses MAY
275
+ # implement this method.
276
+ def read_multi_entries(names, **options)
277
+ return {} if names.empty?
278
+
279
+ keys = names.map { |name| normalize_key(name, options) }
280
+ return_value = {}
281
+ failsafe(:read_multi_entries, returning: return_value) do
282
+ results = collection.get_multi(keys, ::Couchbase::Options::GetMulti(transcoder: nil))
283
+ results.each_with_index do |result, index|
284
+ next unless result.success?
285
+
286
+ entry = deserialize_entry(result.content, raw: options[:raw])
287
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(names[index], options))
288
+ return_value[names[index]] = entry.value
289
+ end
290
+ end
291
+ return_value
292
+ end
293
+ end
294
+
295
+ # Writes an entry to the cache
296
+ def write_entry(key, entry, raw: false, **)
297
+ write_serialized_entry(key, serialize_entry(entry, raw: raw, **), raw: raw, **)
298
+ end
299
+
300
+ def write_serialized_entry(key, payload, expires_in: nil, race_condition_ttl: nil, raw: false, **)
301
+ if race_condition_ttl && expires_in&.positive? && !raw
302
+ # Add few minutes to expiry in the future to support race condition TTLs on read
303
+ expires_in += 5.minutes
304
+ end
305
+ failsafe(:write_entry, returning: false) do
306
+ res = collection.upsert(key, payload, ::Couchbase::Options::Upsert(transcoder: nil, expiry: expires_in))
307
+ @mutations.record(res.mutation_token)
308
+ true
309
+ end
310
+ end
311
+
312
+ def write_multi_entries(entries, **options)
313
+ return 0 if entries.empty?
314
+
315
+ return super if local_cache
316
+
317
+ expires_in = options[:expires_in]
318
+ if options[:race_condition_ttl] && expires_in&.positive?
319
+ # Add few minutes to expiry in the future to support race condition TTLs on read
320
+ expires_in += 5.minutes
321
+ end
322
+ operation_options = ::Couchbase::Options::UpsertMulti(transcoder: nil, expiry: expires_in)
323
+ failsafe(:write_multi_entries, returning: nil) do
324
+ successful = collection.upsert_multi(serialize_entries(entries, **options), operation_options).select(&:success?)
325
+ return 0 if successful.empty?
326
+
327
+ @mutations.record(*successful.map(&:mutation_token))
328
+ successful.count
329
+ end
330
+ end
331
+
332
+ # Deletes an entry from the cache implementation. Subclasses must
333
+ # implement this method.
334
+ def delete_entry(key, **_options)
335
+ failsafe(:delete_entry, returning: false) do
336
+ res = collection.remove(key)
337
+ @mutations.record(res.mutation_token)
338
+ true
339
+ end
340
+ end
341
+
342
+ # Deletes multiple entries in the cache. Returns the number of entries deleted.
343
+ def delete_multi_entries(entries, **_options)
344
+ return if entries.empty?
345
+
346
+ failsafe(:delete_multi_entries, returning: nil) do
347
+ successful = collection.remove_multi(entries).select(&:success?)
348
+ return 0 if successful.empty?
349
+
350
+ @mutations.record(*successful.map(&:mutation_token))
351
+ successful.count
352
+ end
353
+ end
354
+
355
+ def failsafe(method, returning: nil)
356
+ yield
357
+ rescue ::Couchbase::Error::CouchbaseError => e
358
+ ActiveSupport.error_reporter&.report(e, handled: true, severity: :warning)
359
+ @error_handler&.call(method: method, exception: e, returning: returning)
360
+ returning
361
+ end
362
+
363
+ # Truncate keys that exceed 250 characters
364
+ def normalize_key(key, options)
365
+ truncate_key super
366
+ end
367
+
368
+ def truncate_key(key)
369
+ if key && key.bytesize > MAX_KEY_BYTESIZE
370
+ suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
371
+ truncate_at = MAX_KEY_BYTESIZE - suffix.bytesize
372
+ "#{key.mb_chars.limit(truncate_at)}#{suffix}"
373
+ else
374
+ key
375
+ end
376
+ end
377
+
378
+ # Connects to the Couchbase cluster
379
+ def build_cluster
380
+ ::Couchbase::Cluster.connect(
381
+ @couchbase_options[:connection_string],
382
+ ::Couchbase::Options::Cluster(authenticator: ::Couchbase::PasswordAuthenticator.new(
383
+ @couchbase_options[:username], @couchbase_options[:password]
384
+ )),
385
+ )
386
+ end
387
+
388
+ # Connects to the Couchbase cluster, opens specified bucket and returns collection object.
389
+ def build_collection
390
+ bucket = cluster.bucket(@couchbase_options[:bucket])
391
+ if @couchbase_options[:scope] && @couchbase_options[:collection]
392
+ bucket.scope(@couchbase_options[:scope]).collection(@couchbase_options[:collection])
393
+ else
394
+ bucket.default_collection
395
+ end
396
+ end
397
+
398
+ def scope_qualifier
399
+ if @couchbase_options[:scope] && @couchbase_options[:collection]
400
+ "`#{@couchbase_options[:bucket]}`.#{@couchbase_options[:scope]}.#{@couchbase_options[:collection]}"
401
+ else
402
+ "`#{@couchbase_options[:bucket]}`"
403
+ end
404
+ end
405
+ end
406
+ end
407
+ end
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020-2021 Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require "couchbase/json_transcoder"
18
+
19
+ module Couchbase
20
+ class Cluster
21
+ class AnalyticsWarning
22
+ # @return [Integer]
23
+ attr_accessor :code
24
+
25
+ # @return [String]
26
+ attr_accessor :message
27
+
28
+ # @param [Integer] code
29
+ # @param [String] message
30
+ def initialize(code, message)
31
+ @code = code
32
+ @message = message
33
+ end
34
+ end
35
+
36
+ class AnalyticsMetrics
37
+ # @return [Integer] duration in milliseconds
38
+ attr_accessor :elapsed_time
39
+
40
+ # @return [Integer] duration in milliseconds
41
+ attr_accessor :execution_time
42
+
43
+ # @return [Integer]
44
+ attr_accessor :result_count
45
+
46
+ # @return [Integer]
47
+ attr_accessor :result_size
48
+
49
+ # @return [Integer]
50
+ attr_accessor :error_count
51
+
52
+ # @return [Integer]
53
+ attr_accessor :processed_objects
54
+
55
+ # @return [Integer]
56
+ attr_accessor :warning_count
57
+
58
+ # @yieldparam [AnalyticsMetrics] self
59
+ def initialize
60
+ yield self if block_given?
61
+ end
62
+ end
63
+
64
+ class AnalyticsMetaData
65
+ # @return [String]
66
+ attr_accessor :request_id
67
+
68
+ # @return [String]
69
+ attr_accessor :client_context_id
70
+
71
+ # @return [:running, :success, :errors, :completed, :stopped, :timeout, :closed, :fatal, :aborted, :unknown]
72
+ attr_accessor :status
73
+
74
+ # @return [Hash] returns the signature as returned by the query engine which is then decoded as JSON object
75
+ attr_accessor :signature
76
+
77
+ # @return [Array<AnalyticsWarning>]
78
+ attr_accessor :warnings
79
+
80
+ # @return [AnalyticsMetrics]
81
+ attr_accessor :metrics
82
+
83
+ # @yieldparam [AnalyticsMetaData] self
84
+ def initialize
85
+ yield self if block_given?
86
+ end
87
+ end
88
+
89
+ class AnalyticsResult
90
+ # @return [AnalyticsMetaData]
91
+ attr_accessor :meta_data
92
+
93
+ attr_accessor :transcoder
94
+
95
+ # Returns all rows converted using a transcoder
96
+ #
97
+ # @return [Array]
98
+ def rows(transcoder = self.transcoder)
99
+ @rows.lazy.map { |row| transcoder.decode(row, 0) }
100
+ end
101
+
102
+ # @yieldparam [AnalyticsResult] self
103
+ def initialize
104
+ @transcoder = JsonTranscoder.new
105
+ yield self if block_given?
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020-2021 Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Couchbase
18
+ # Authenticator for username/password credentials
19
+ class PasswordAuthenticator
20
+ DEFAULT_SASL_MECHANISMS = [:scram_sha512, :scram_sha256, :scram_sha1].freeze
21
+
22
+ attr_accessor :username
23
+ attr_accessor :password
24
+ attr_accessor :allowed_sasl_mechanisms
25
+
26
+ # Creates a new password authenticator with the default settings.
27
+ #
28
+ # @param [String] password the username to use for all authentication requests
29
+ # @param [String] username the password
30
+ def initialize(username, password)
31
+ @username = username
32
+ @password = password
33
+ end
34
+
35
+ # Creates a LDAP compatible password authenticator which is INSECURE if not used with TLS.
36
+ #
37
+ # Please note that this is INSECURE and will leak user credentials on the wire to eavesdroppers. This should
38
+ # only be enabled in trusted environments.
39
+ #
40
+ # @param [String] username the username to use for all authentication.
41
+ # @param [String] password the password to use alongside the username.
42
+ # @return [PasswordAuthenticator]
43
+ def self.ldap_compatible(username, password)
44
+ new(username, password).tap do |auth|
45
+ auth.allowed_sasl_mechanisms = [:plain]
46
+ end
47
+ end
48
+ end
49
+
50
+ # Authenticator for TLS client certificate
51
+ #
52
+ # @see https://docs.couchbase.com/server/current/manage/manage-security/configure-client-certificates.html
53
+ class CertificateAuthenticator
54
+ attr_accessor :certificate_path
55
+ attr_accessor :key_path
56
+
57
+ # Creates a new authenticator with certificate and key paths
58
+ #
59
+ # @param [String] certificate_path path to certificate
60
+ # @param [String] key_path path to private key
61
+ def initialize(certificate_path, key_path)
62
+ @certificate_path = certificate_path
63
+ @key_path = key_path
64
+ end
65
+ end
66
+
67
+ # Authenticator using a JSON Web Token (JWT)
68
+ #
69
+ # @!macro uncommitted
70
+ class JWTAuthenticator
71
+ attr_accessor :token
72
+
73
+ # Creates a new authenticator with a JSON Web Token (JWT)
74
+ #
75
+ # @param [String] token the JWT
76
+ def initialize(token)
77
+ @token = token
78
+ end
79
+ end
80
+ end