Skip to content

Commit f6a02d7

Browse files
authored
RCBC-263: update core and implement legacy durability (#49)
1 parent 944ad08 commit f6a02d7

File tree

12 files changed

+1335
-918
lines changed

12 files changed

+1335
-918
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
- 7.1.1
1515
- 7.0.3
1616
- 6.6.5
17-
runs-on: ubuntu-20.04
17+
runs-on: ubuntu-22.04
1818
services:
1919
couchbase:
2020
image: couchbase:enterprise-${{ matrix.server }}

ext/couchbase

ext/couchbase.cxx

Lines changed: 1023 additions & 747 deletions
Large diffs are not rendered by default.

lib/couchbase/collection.rb

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,12 @@ def remove(id, options = Options::Remove.new)
279279
#
280280
# @return [Array<MutationResult>]
281281
def remove_multi(ids, options = Options::RemoveMulti.new)
282-
resp = @backend.document_remove_multi(ids.map do |id|
282+
resp = @backend.document_remove_multi(bucket_name, @scope_name, @name, ids.map do |id|
283283
case id
284284
when String
285-
[bucket_name, @scope_name, @name, id, nil]
285+
[id, nil]
286286
when Array
287-
[bucket_name, @scope_name, @name, id[0], id[1]]
287+
id
288288
else
289289
raise ArgumentError, "id argument of remove_multi must be a String or Array<String, Integer>, given: #{id.inspect}"
290290
end
@@ -366,9 +366,9 @@ def upsert(id, content, options = Options::Upsert.new)
366366
#
367367
# @return [Array<MutationResult>]
368368
def upsert_multi(id_content, options = Options::UpsertMulti.new)
369-
resp = @backend.document_upsert_multi(id_content.map do |(id, content)|
369+
resp = @backend.document_upsert_multi(bucket_name, @scope_name, @name, id_content.map do |(id, content)|
370370
blob, flags = options.transcoder ? options.transcoder.encode(content) : [content, 0]
371-
[bucket_name, @scope_name, @name, id, blob, flags]
371+
[id, blob, flags]
372372
end, options.to_backend)
373373
resp.map do |entry|
374374
MutationResult.new do |res|
@@ -475,10 +475,7 @@ def lookup_in(id, specs, options = Options::LookupIn.new)
475475
f.exists = field[:exists]
476476
f.index = field[:index]
477477
f.path = field[:path]
478-
f.status = field[:status]
479-
f.error = field[:error]
480478
f.value = field[:value]
481-
f.type = field[:type]
482479
end
483480
end
484481
end
@@ -518,31 +515,26 @@ def mutate_in(id, specs, options = Options::MutateIn.new)
518515
}
519516
end, options.to_backend
520517
)
521-
result = MutateInResult.new do |res|
518+
MutateInResult.new do |res|
522519
res.transcoder = options.transcoder
523520
res.cas = resp[:cas]
524521
res.deleted = resp[:deleted]
525522
res.mutation_token = extract_mutation_token(resp)
526-
res.first_error_index = resp[:first_error_index]
527523
res.encoded = resp[:fields].map do |field|
528524
SubDocumentField.new do |f|
529-
f.exists = field[:exists]
525+
f.index = field[:index]
530526
f.path = field[:path]
531-
f.status = field[:status]
532-
f.error = field[:error]
533527
f.value = field[:value]
534-
f.type = field[:type]
535528
end
536529
end
537530
end
538-
raise result.first_error unless result.success?
539-
540-
result
541531
end
542532

543533
private
544534

545535
def extract_mutation_token(resp)
536+
return unless resp.key?(:mutation_token)
537+
546538
MutationToken.new do |token|
547539
token.partition_id = resp[:mutation_token][:partition_id]
548540
token.partition_uuid = resp[:mutation_token][:partition_uuid]

lib/couchbase/collection_options.rb

Lines changed: 41 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,36 @@ class LookupInResult
157157
# @return [Integer] holds the CAS value of the fetched document
158158
attr_accessor :cas
159159

160-
# Decodes the content at the given index
160+
# Decodes the content at the given index (or path)
161161
#
162-
# @param [Integer] index the index of the subdocument value to decode
162+
# @param [Integer, String] path_or_index the index (or path) of the subdocument value to decode
163163
#
164164
# @return [Object] the decoded
165-
def content(index, transcoder = self.transcoder)
166-
transcoder.decode(get_field_at_index(index).value, :json)
165+
def content(path_or_index, transcoder = self.transcoder)
166+
field = get_field_at_index(path_or_index)
167+
raise Error::PathNotFound, "Path is not found: #{path_or_index}" unless field.exists
168+
169+
transcoder.decode(field.value, :json)
167170
end
168171

169172
# Allows to check if a value at the given index exists
170173
#
171-
# @param [Integer] index the index of the subdocument value to check
174+
# @param [Integer, String] path_or_index the index (or path) of the subdocument value to check
172175
#
173176
# @return [Boolean] true if a value is present at the index, false otherwise
174-
def exists?(index)
175-
!encoded[index].nil? && encoded[index].exists
177+
def exists?(path_or_index)
178+
field =
179+
case path_or_index
180+
when String
181+
encoded.find { |f| f.path == path_or_index }
182+
else
183+
return false unless path_or_index >= 0 && path_or_index < encoded.size
184+
185+
encoded[path_or_index]
186+
end
187+
return false unless field
188+
189+
field.exists
176190
end
177191

178192
# @return [Array<SubDocumentField>] holds the encoded subdocument responses
@@ -201,29 +215,27 @@ def deleted?
201215

202216
private
203217

204-
def get_field_at_index(index)
205-
raise Error::PathInvalid, "Index is out of bounds: #{index}" unless index >= 0 && index < encoded.size
206-
207-
field = encoded[index]
208-
raise field.error unless field.success?
218+
def get_field_at_index(path_or_index)
219+
case path_or_index
220+
when String
221+
encoded.find { |field| field.path == path_or_index } or raise Error::PathInvalid, "Path is not found: #{path_or_index}"
222+
else
223+
raise Error::PathInvalid, "Index is out of bounds: #{path_or_index}" unless path_or_index >= 0 && path_or_index < encoded.size
209224

210-
field
225+
encoded[path_or_index]
226+
end
211227
end
212228
end
213229

214230
class MutateInResult < MutationResult
215231
# Decodes the content at the given index
216232
#
217-
# @param [Integer] index the index of the subdocument value to decode
233+
# @param [Integer, String] path_or_index the index (or path) of the subdocument value to decode
218234
#
219235
# @return [Object] the decoded
220-
def content(index, transcoder = self.transcoder)
221-
field = get_field_at_index(index)
222-
if field.type == :counter
223-
field.value
224-
else
225-
transcoder.decode(field.value, :json)
226-
end
236+
def content(path_or_index, transcoder = self.transcoder)
237+
field = get_field_at_index(path_or_index)
238+
transcoder.decode(field.value, :json)
227239
end
228240

229241
# @yieldparam [MutateInResult] self
@@ -232,24 +244,10 @@ def initialize
232244
yield self if block_given?
233245
end
234246

235-
# @api private
236-
def success?
237-
first_error_index.nil?
238-
end
239-
240-
# @api private
241-
def first_error
242-
encoded[first_error_index].error unless success?
243-
end
244-
245247
# @return [Array<SubDocumentField>] holds the encoded subdocument responses
246248
# @api private
247249
attr_accessor :encoded
248250

249-
# @return [Integer, nil] index of first operation entry that generated an error
250-
# @api private
251-
attr_accessor :first_error_index
252-
253251
# @return [JsonTranscoder] The default transcoder which should be used
254252
attr_accessor :transcoder
255253

@@ -267,13 +265,15 @@ def deleted?
267265

268266
private
269267

270-
def get_field_at_index(index)
271-
raise Error::PathInvalid, "Index is out of bounds: #{index}" unless index >= 0 && index < encoded.size
272-
273-
field = encoded[index]
274-
raise field.error unless field.success?
268+
def get_field_at_index(path_or_index)
269+
case path_or_index
270+
when String
271+
encoded.find { |field| field.path == path_or_index } or raise Error::PathInvalid, "Path is not found: #{path_or_index}"
272+
else
273+
raise Error::PathInvalid, "Index is out of bounds: #{path_or_index}" unless path_or_index >= 0 && path_or_index < encoded.size
275274

276-
field
275+
encoded[path_or_index]
276+
end
277277
end
278278
end
279279

@@ -291,64 +291,10 @@ class SubDocumentField
291291
# @return [String] path
292292
attr_accessor :path
293293

294-
# Operation type
295-
#
296-
# * +:set_doc+
297-
# * +:counter+
298-
# * +:replace+
299-
# * +:dict_add+
300-
# * +:dict_upsert+
301-
# * +:array_push_first+
302-
# * +:array_push_last+
303-
# * +:array_add_unique+
304-
# * +:array_insert+
305-
# * +:delete+
306-
# * +:get+
307-
# * +:exists+
308-
# * +:count+
309-
# * +:get_doc+
310-
#
311-
# @return [Symbol]
312-
attr_accessor :type
313-
314-
# Status of the subdocument path operation.
315-
#
316-
# [+:success+] Indicates a successful response in general.
317-
# [+:path_not_found+] The provided path does not exist in the document
318-
# [+:path_mismatch+] One of path components treats a non-dictionary as a dictionary, or a non-array as an array, or value the path
319-
# points to is not a number
320-
# [+:path_invalid+] The path's syntax was incorrect
321-
# [+:path_too_big+] The path provided is too large: either the string is too long, or it contains too many components
322-
# [+:value_cannot_insert+] The value provided will invalidate the JSON if inserted
323-
# [+:doc_not_json+] The existing document is not valid JSON
324-
# [+:num_range+] The existing number is out of the valid range for arithmetic operations
325-
# [+:delta_invalid+] The operation would result in a number outside the valid range
326-
# [+:path_exists+] The requested operation requires the path to not already exist, but it exists
327-
# [+:value_too_deep+] Inserting the value would cause the document to be too deep
328-
# [+:invalid_combo+] An invalid combination of commands was specified
329-
# [+:xattr_invalid_flag_combo+] An invalid combination of operations, using macros when not using extended attributes
330-
# [+:xattr_invalid_key_combo+] Only single xattr key may be accessed at the same time
331-
# [+:xattr_unknown_macro+] The server has no knowledge of the requested macro
332-
# [+:xattr_unknown_vattr+] Unknown virtual attribute.
333-
# [+:xattr_cannot_modify_vattr+] Cannot modify this virtual attribute.
334-
# [+:unknown+] Unknown error.
335-
#
336-
# @return [Symbol]
337-
attr_accessor :status
338-
339-
# @return [nil, Exception]
340-
attr_accessor :error
341-
342294
# @yieldparam [SubDocumentField] self
343295
def initialize
344-
@status = :unknown
345296
yield self if block_given?
346297
end
347-
348-
# @return [Boolean] true if the path does not have associated error
349-
def success?
350-
status == :success
351-
end
352298
end
353299
end
354300
end

lib/couchbase/management/analytics_index_manager.rb

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def initialize(ignore_if_exists: false,
4747
# @api private
4848
def to_backend
4949
{
50-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
50+
timeout: Utils::Time.extract_duration(@timeout),
5151
ignore_if_exists: @ignore_if_exists,
5252
}
5353
end
@@ -81,7 +81,7 @@ def initialize(ignore_if_does_not_exist: false,
8181
# @api private
8282
def to_backend
8383
{
84-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
84+
timeout: Utils::Time.extract_duration(@timeout),
8585
ignore_if_does_not_exist: @ignore_if_does_not_exist,
8686
}
8787
end
@@ -123,7 +123,7 @@ def initialize(ignore_if_exists: false,
123123
# @api private
124124
def to_backend
125125
{
126-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
126+
timeout: Utils::Time.extract_duration(@timeout),
127127
condition: @condition,
128128
dataverse_name: @dataverse_name,
129129
ignore_if_exists: @ignore_if_exists,
@@ -163,7 +163,7 @@ def initialize(ignore_if_does_not_exist: false,
163163
# @api private
164164
def to_backend
165165
{
166-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
166+
timeout: Utils::Time.extract_duration(@timeout),
167167
dataverse_name: @dataverse_name,
168168
ignore_if_does_not_exist: @ignore_if_does_not_exist,
169169
}
@@ -221,7 +221,7 @@ def initialize(ignore_if_exists: false,
221221
# @api private
222222
def to_backend
223223
{
224-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
224+
timeout: Utils::Time.extract_duration(@timeout),
225225
dataverse_name: @dataverse_name,
226226
ignore_if_exists: @ignore_if_exists,
227227
}
@@ -260,7 +260,7 @@ def initialize(ignore_if_does_not_exist: false,
260260
# @api private
261261
def to_backend
262262
{
263-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
263+
timeout: Utils::Time.extract_duration(@timeout),
264264
dataverse_name: @dataverse_name,
265265
ignore_if_does_not_exist: @ignore_if_does_not_exist,
266266
}
@@ -322,7 +322,7 @@ def initialize(link_name: "Local",
322322
# @api private
323323
def to_backend
324324
{
325-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
325+
timeout: Utils::Time.extract_duration(@timeout),
326326
link_name: @link_name,
327327
force: @force,
328328
dataverse_name: @dataverse_name,
@@ -361,7 +361,7 @@ def initialize(link_name: "Local",
361361
# @api private
362362
def to_backend
363363
{
364-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
364+
timeout: Utils::Time.extract_duration(@timeout),
365365
link_name: @link_name,
366366
dataverse_name: @dataverse_name,
367367
}
@@ -408,7 +408,7 @@ def initialize(timeout: nil,
408408
# @api private
409409
def to_backend
410410
{
411-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
411+
timeout: Utils::Time.extract_duration(@timeout),
412412
}
413413
end
414414
end
@@ -434,7 +434,7 @@ def initialize(timeout: nil,
434434
# @api private
435435
def to_backend
436436
{
437-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
437+
timeout: Utils::Time.extract_duration(@timeout),
438438
}
439439
end
440440
end
@@ -460,7 +460,7 @@ def initialize(timeout: nil,
460460
# @api private
461461
def to_backend
462462
{
463-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
463+
timeout: Utils::Time.extract_duration(@timeout),
464464
}
465465
end
466466
end
@@ -503,7 +503,7 @@ def initialize(link_type: nil,
503503
# @api private
504504
def to_backend
505505
{
506-
timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
506+
timeout: Utils::Time.extract_duration(@timeout),
507507
link_type: @link_type.to_s,
508508
dataverse: @dataverse,
509509
name: @name,

0 commit comments

Comments
 (0)