mongo 2.14.0 → 2.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/mongo/background_thread.rb +11 -0
  4. data/lib/mongo/cluster/sdam_flow.rb +14 -0
  5. data/lib/mongo/cluster.rb +0 -26
  6. data/lib/mongo/collection/view/iterable.rb +16 -6
  7. data/lib/mongo/collection.rb +2 -0
  8. data/lib/mongo/cursor.rb +10 -0
  9. data/lib/mongo/database.rb +14 -2
  10. data/lib/mongo/grid/fs_bucket.rb +37 -37
  11. data/lib/mongo/operation/parallel_scan/command.rb +1 -2
  12. data/lib/mongo/operation/shared/read_preference_supported.rb +38 -36
  13. data/lib/mongo/operation/shared/sessions_supported.rb +3 -2
  14. data/lib/mongo/protocol/msg.rb +2 -2
  15. data/lib/mongo/protocol/query.rb +11 -11
  16. data/lib/mongo/server/connection_pool.rb +0 -2
  17. data/lib/mongo/server.rb +0 -14
  18. data/lib/mongo/server_selector/secondary_preferred.rb +2 -7
  19. data/lib/mongo/srv/monitor.rb +0 -11
  20. data/lib/mongo/version.rb +1 -1
  21. data/spec/integration/query_cache_spec.rb +45 -0
  22. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  23. data/spec/integration/sdam_events_spec.rb +3 -5
  24. data/spec/integration/secondary_reads_spec.rb +102 -0
  25. data/spec/mongo/cluster_spec.rb +2 -18
  26. data/spec/mongo/index/view_spec.rb +4 -2
  27. data/spec/mongo/operation/read_preference_legacy_spec.rb +9 -19
  28. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  29. data/spec/mongo/server/app_metadata_shared.rb +33 -7
  30. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  31. data/spec/runners/transactions/operation.rb +13 -2
  32. data/spec/shared/bin/get-mongodb-download-url +17 -0
  33. data/spec/shared/lib/mrss/cluster_config.rb +221 -0
  34. data/spec/shared/lib/mrss/constraints.rb +43 -0
  35. data/spec/shared/lib/mrss/docker_runner.rb +265 -0
  36. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  37. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  38. data/spec/shared/lib/mrss/spec_organizer.rb +3 -0
  39. data/spec/shared/lib/mrss/utils.rb +15 -0
  40. data/spec/shared/share/Dockerfile.erb +231 -0
  41. data/spec/shared/shlib/distro.sh +73 -0
  42. data/spec/shared/shlib/server.sh +290 -0
  43. data/spec/shared/shlib/set_env.sh +128 -0
  44. data/spec/solo/clean_exit_spec.rb +21 -0
  45. data/spec/support/client_registry.rb +8 -4
  46. data/spec/support/client_registry_macros.rb +4 -4
  47. data/spec/support/spec_config.rb +12 -0
  48. data/spec/support/spec_setup.rb +48 -38
  49. data.tar.gz.sig +0 -0
  50. metadata +1005 -983
  51. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bfb22d052a842e256744a88c7e3cbd3b958643cc3e0f4425bf97be8f913f167
4
- data.tar.gz: b63f3605ad8223e99a5f6a62aabff94a8c8600095b84265b6b4deb38e7223013
3
+ metadata.gz: 02e085995ec501175b551369fff92fd59b44787da27cb9f84b93dafef4d0762b
4
+ data.tar.gz: f7ac041c7d89ad98207a6038ab44263aed36ae4fbdc925107a0f74772889ff48
5
5
  SHA512:
6
- metadata.gz: 85566c0080b9bcfa67438a365114dd062425df557040e05c0db0448aedd37a0f52dae5b8612b08af196a9e35c1e5b41553a89607d780d187d4e2e28602471c76
7
- data.tar.gz: 5172ab2d71a8d7e048fcb2d87e7219024a2028c3909f2cce48081d1b3014071df13b02426019508bda6542e4870992ca869cd3393caf3aaf6483ad4c74f1abf3
6
+ metadata.gz: 61512bc1653f1b2d9e5d73db7acbfd0725931cf7e234aad7b709086e76037f287da12e6c893c6ae84429113fd83cf195a296e0f3f01fa3974670106d1b19e8d4
7
+ data.tar.gz: 198f03ef56d8f280962ca212e411bf97a04cd518d7e1f82628f3bf4c274cf897c872e1d109af1ee94627803de2d3d8b37afbd239f87fe1c6a74e6813a2f65201
checksums.yaml.gz.sig CHANGED
Binary file
@@ -20,6 +20,17 @@ module Mongo
20
20
  # compatibility reasons. However using these methods outside of the driver
21
21
  # is deprecated.
22
22
  #
23
+ # @note Do not start or stop background threads in finalizers. See
24
+ # https://jira.mongodb.org/browse/RUBY-2453 and
25
+ # https://bugs.ruby-lang.org/issues/16288. When interpreter exits,
26
+ # background threads are stopped first and finalizers are invoked next,
27
+ # and MRI's internal data structures are basically corrupt at this point
28
+ # if threads are being referenced. Prior to interpreter shutdown this
29
+ # means threads cannot be stopped by objects going out of scope, but
30
+ # most likely the threads hold references to said objects anyway if
31
+ # work is being performed thus the objects wouldn't go out of scope in
32
+ # the first place.
33
+ #
23
34
  # @api private
24
35
  module BackgroundThread
25
36
  include Loggable
@@ -178,6 +178,7 @@ class Mongo::Cluster
178
178
  raise ArgumentError, "Unknown topology #{topology.class}"
179
179
  end
180
180
 
181
+ verify_invariants
181
182
  commit_changes
182
183
  disconnect_servers
183
184
  end
@@ -363,6 +364,9 @@ class Mongo::Cluster
363
364
  end
364
365
  end
365
366
  end
367
+
368
+ verify_invariants
369
+
366
370
  added_servers
367
371
  end
368
372
 
@@ -599,5 +603,15 @@ class Mongo::Cluster
599
603
 
600
604
  @previous_server_descriptions != server_descriptions
601
605
  end
606
+
607
+ def verify_invariants
608
+ if Mongo::Lint.enabled?
609
+ if cluster.topology.single?
610
+ if cluster.servers_list.length > 1
611
+ raise Mongo::Error::LintError, "Trying to create a single topology with multiple servers: #{cluster.servers_list}"
612
+ end
613
+ end
614
+ end
615
+ end
602
616
  end
603
617
  end
data/lib/mongo/cluster.rb CHANGED
@@ -199,9 +199,6 @@ module Mongo
199
199
  @cursor_reaper, @socket_reaper,
200
200
  ], options)
201
201
 
202
- ObjectSpace.define_finalizer(self, self.class.finalize(
203
- {}, @periodic_executor, @session_pool))
204
-
205
202
  @periodic_executor.run!
206
203
  end
207
204
 
@@ -445,25 +442,6 @@ module Mongo
445
442
  # @api private
446
443
  attr_reader :server_selection_semaphore
447
444
 
448
- # Finalize the cluster for garbage collection.
449
- #
450
- # @example Finalize the cluster.
451
- # Cluster.finalize(pools)
452
- #
453
- # @param [ Hash<Address, Server::ConnectionPool> ] pools Ignored.
454
- # @param [ PeriodicExecutor ] periodic_executor The periodic executor.
455
- # @param [ SessionPool ] session_pool The session pool.
456
- #
457
- # @return [ Proc ] The Finalizer.
458
- #
459
- # @since 2.2.0
460
- def self.finalize(pools, periodic_executor, session_pool)
461
- proc do
462
- session_pool.end_sessions
463
- periodic_executor.stop!
464
- end
465
- end
466
-
467
445
  # Closes the cluster.
468
446
  #
469
447
  # @note Applications should call Client#close to disconnect from
@@ -953,10 +931,6 @@ module Mongo
953
931
  monitor_options = Utils.shallow_symbolize_keys(options.merge(
954
932
  timeout: options[:connect_timeout] || Server::CONNECT_TIMEOUT))
955
933
  @srv_monitor = _srv_monitor = Srv::Monitor.new(self, **monitor_options)
956
- finalizer = lambda do
957
- _srv_monitor.stop!
958
- end
959
- ObjectSpace.define_finalizer(self, finalizer)
960
934
  end
961
935
  @srv_monitor.run!
962
936
  end
@@ -35,18 +35,28 @@ module Mongo
35
35
  #
36
36
  # @yieldparam [ Hash ] Each matching document.
37
37
  def each
38
- @cursor = if use_query_cache? && cached_cursor
38
+ # If the caching cursor is closed and was not fully iterated,
39
+ # the documents we have in it are not the complete result set and
40
+ # we have no way of completing that iteration.
41
+ # Therefore, discard that cursor and start iteration again.
42
+ # The case of the caching cursor not being closed and not having
43
+ # been fully iterated isn't tested - see RUBY-2773.
44
+ @cursor = if use_query_cache? && cached_cursor && (
45
+ cached_cursor.fully_iterated? || !cached_cursor.closed?
46
+ )
39
47
  cached_cursor
40
48
  else
41
49
  session = client.send(:get_session, @options)
42
- select_cursor(session)
50
+ select_cursor(session).tap do |cursor|
51
+ if use_query_cache?
52
+ # No need to store the cursor in the query cache if there is
53
+ # already a cached cursor stored at this key.
54
+ QueryCache.set(cursor, **cache_options)
55
+ end
56
+ end
43
57
  end
44
58
 
45
59
  if use_query_cache?
46
- # No need to store the cursor in the query cache if there is
47
- # already a cached cursor stored at this key.
48
- QueryCache.set(@cursor, **cache_options) unless cached_cursor
49
-
50
60
  # If a query with a limit is performed, the query cache will
51
61
  # re-use results from an earlier query with the same or larger
52
62
  # limit, and then impose the lower limit during iteration.
@@ -593,6 +593,8 @@ module Mongo
593
593
  # @param [ Array<Hash> ] documents The documents to insert.
594
594
  # @param [ Hash ] options The insert options.
595
595
  #
596
+ # @option options [ true | false ] :ordered Whether the operations
597
+ # should be executed in order.
596
598
  # @option options [ Session ] :session The session to use for the operation.
597
599
  #
598
600
  # @return [ Result ] The database response wrapper.
data/lib/mongo/cursor.rb CHANGED
@@ -209,10 +209,12 @@ module Mongo
209
209
  unless closed?
210
210
  if exhausted?
211
211
  close
212
+ @fully_iterated = true
212
213
  raise StopIteration
213
214
  end
214
215
  @documents = get_more
215
216
  else
217
+ @fully_iterated = true
216
218
  raise StopIteration
217
219
  end
218
220
  else
@@ -229,6 +231,9 @@ module Mongo
229
231
  # over the last document, or if the batch is empty
230
232
  if @documents.size <= 1
231
233
  cache_batch_resume_token
234
+ if closed?
235
+ @fully_iterated = true
236
+ end
232
237
  end
233
238
 
234
239
  return @documents.shift
@@ -348,6 +353,11 @@ module Mongo
348
353
  process(get_more_operation.execute(@server, client: client))
349
354
  end
350
355
 
356
+ # @api private
357
+ def fully_iterated?
358
+ !!@fully_iterated
359
+ end
360
+
351
361
  private
352
362
 
353
363
  def exhausted?
@@ -321,8 +321,20 @@ module Mongo
321
321
 
322
322
  # Get the Grid "filesystem" for this database.
323
323
  #
324
- # @example Get the GridFS.
325
- # database.fs
324
+ # @param [ Hash ] options The GridFS options.
325
+ #
326
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
327
+ # collections.
328
+ # @option options [ Integer ] :chunk_size Override the default chunk
329
+ # size.
330
+ # @option options [ String ] :fs_name The prefix for the files and chunks
331
+ # collections.
332
+ # @option options [ String ] :read The read preference.
333
+ # @option options [ Session ] :session The session to use.
334
+ # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
335
+ # option.
336
+ # @option options [ Hash ] :write_concern The write concern options.
337
+ # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
326
338
  #
327
339
  # @return [ Grid::FSBucket ] The GridFS for the database.
328
340
  #
@@ -36,6 +36,43 @@ module Mongo
36
36
  # @since 2.1.0
37
37
  FILES_INDEX = { filename: 1, uploadDate: 1 }.freeze
38
38
 
39
+ # Create the GridFS.
40
+ #
41
+ # @example Create the GridFS.
42
+ # Grid::FSBucket.new(database)
43
+ #
44
+ # @param [ Database ] database The database the files reside in.
45
+ # @param [ Hash ] options The GridFS options.
46
+ #
47
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
48
+ # collections.
49
+ # @option options [ Integer ] :chunk_size Override the default chunk
50
+ # size.
51
+ # @option options [ String ] :fs_name The prefix for the files and chunks
52
+ # collections.
53
+ # @option options [ String ] :read The read preference.
54
+ # @option options [ Session ] :session The session to use.
55
+ # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
56
+ # option.
57
+ # @option options [ Hash ] :write_concern The write concern options.
58
+ # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
59
+ #
60
+ # @since 2.0.0
61
+ def initialize(database, options = {})
62
+ @database = database
63
+ @options = options.dup
64
+ =begin WriteConcern object support
65
+ if @options[:write_concern].is_a?(WriteConcern::Base)
66
+ # Cache the instance so that we do not needlessly reconstruct it.
67
+ @write_concern = @options[:write_concern]
68
+ @options[:write_concern] = @write_concern.options
69
+ end
70
+ =end
71
+ @options.freeze
72
+ @chunks_collection = database[chunks_name]
73
+ @files_collection = database[files_name]
74
+ end
75
+
39
76
  # @return [ Collection ] chunks_collection The chunks collection.
40
77
  #
41
78
  # @since 2.0.0
@@ -133,43 +170,6 @@ module Mongo
133
170
  file.id
134
171
  end
135
172
 
136
- # Create the GridFS.
137
- #
138
- # @example Create the GridFS.
139
- # Grid::FSBucket.new(database)
140
- #
141
- # @param [ Database ] database The database the files reside in.
142
- # @param [ Hash ] options The GridFS options.
143
- #
144
- # @option options [ String ] :fs_name The prefix for the files and chunks
145
- # collections.
146
- # @option options [ String ] :bucket_name The prefix for the files and chunks
147
- # collections.
148
- # @option options [ Integer ] :chunk_size Override the default chunk
149
- # size.
150
- # @option options [ String ] :read The read preference.
151
- # @option options [ Session ] :session The session to use.
152
- # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
153
- # option.
154
- # @option options [ Hash ] :write_concern The write concern options.
155
- # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
156
- #
157
- # @since 2.0.0
158
- def initialize(database, options = {})
159
- @database = database
160
- @options = options.dup
161
- =begin WriteConcern object support
162
- if @options[:write_concern].is_a?(WriteConcern::Base)
163
- # Cache the instance so that we do not needlessly reconstruct it.
164
- @write_concern = @options[:write_concern]
165
- @options[:write_concern] = @write_concern.options
166
- end
167
- =end
168
- @options.freeze
169
- @chunks_collection = database[chunks_name]
170
- @files_collection = database[files_name]
171
- end
172
-
173
173
  # Get the prefix for the GridFS
174
174
  #
175
175
  # @example Get the prefix.
@@ -37,8 +37,7 @@ module Mongo
37
37
  read_concern)
38
38
  end
39
39
  sel[:maxTimeMS] = max_time_ms if max_time_ms
40
- update_selector_for_read_pref(sel, connection)
41
- sel
40
+ add_read_preference_legacy(sel, connection)
42
41
  end
43
42
 
44
43
  def message(connection)
@@ -36,47 +36,44 @@ module Mongo
36
36
  #
37
37
  # @since 2.0.0
38
38
  def options(connection)
39
- add_slave_ok_flag_maybe(super, connection)
39
+ options = super
40
+ if add_slave_ok_flag?(connection)
41
+ flags = options[:flags]&.dup || []
42
+ flags << :slave_ok
43
+ options = options.merge(flags: flags)
44
+ end
45
+ options
40
46
  end
41
47
 
42
- # Adds :slave_ok flag to options based on the read preference specified
43
- # in the operation or implied by the topology that the connection's
44
- # server is a part of.
48
+ # Whether to add the :slave_ok flag to the request based on the
49
+ # read preference specified in the operation or implied by the topology
50
+ # that the connection's server is a part of.
45
51
  #
46
- # @param [ Hash ] options The options calculated so far.
47
52
  # @param [ Server::Connection ] connection The connection that the
48
53
  # operation will be executed on.
49
54
  #
50
- # @return [ Hash ] The new options.
51
- def add_slave_ok_flag_maybe(options, connection)
52
- add_flag =
53
- # https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
54
- if connection.description.standalone?
55
- # Read preference is never sent to standalones.
56
- false
57
- elsif connection.server.cluster.single?
58
- # In Single topology the driver forces primaryPreferred read
59
- # preference mode (via the slave_ok flag, in case of old servers)
60
- # so that the query is satisfied.
61
- true
62
- else
63
- # In replica sets and sharded clusters, read preference is passed
64
- # to the server if one is specified by the application, and there
65
- # is no default.
66
- read && read.slave_ok?
67
- end
68
-
69
- if add_flag
70
- options= options.dup
71
- (options[:flags] ||= []) << :slave_ok
55
+ # @return [ true | false ] Whether the :slave_ok flag should be added.
56
+ def add_slave_ok_flag?(connection)
57
+ # https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
58
+ if connection.description.standalone?
59
+ # Read preference is never sent to standalones.
60
+ false
61
+ elsif connection.server.cluster.single?
62
+ # In Single topology the driver forces primaryPreferred read
63
+ # preference mode (via the slave_ok flag, in case of old servers)
64
+ # so that the query is satisfied.
65
+ true
66
+ else
67
+ # In replica sets and sharded clusters, read preference is passed
68
+ # to the server if one is specified by the application, and there
69
+ # is no default.
70
+ read && read.slave_ok? || false
72
71
  end
73
-
74
- options
75
72
  end
76
73
 
77
74
  def command(connection)
78
75
  sel = super
79
- update_selector_for_read_pref(sel, connection)
76
+ add_read_preference_legacy(sel, connection)
80
77
  end
81
78
 
82
79
  # Adds $readPreference field to the command document.
@@ -95,14 +92,19 @@ module Mongo
95
92
  # operation will be executed on.
96
93
  #
97
94
  # @return [ Hash ] New command document to send to the server.
98
- def update_selector_for_read_pref(sel, connection)
95
+ def add_read_preference_legacy(sel, connection)
99
96
  if read && connection.description.mongos? && read_pref = read.to_mongos
100
- Mongo::Lint.validate_camel_case_read_preference(read_pref)
101
- sel = sel[:$query] ? sel : {:$query => sel}
102
- sel = sel.merge(:$readPreference => read_pref)
103
- else
104
- sel
97
+ # If the read preference contains only mode and mode is secondary
98
+ # preferred and we are sending to a pre-OP_MSG server, this read
99
+ # preference is indicated by the :slave_ok wire protocol flag
100
+ # and $readPreference command parameter isn't sent.
101
+ if read_pref != {mode: 'secondaryPreferred'}
102
+ Mongo::Lint.validate_camel_case_read_preference(read_pref)
103
+ sel = sel[:$query] ? sel : {:$query => sel}
104
+ sel = sel.merge(:$readPreference => read_pref)
105
+ end
105
106
  end
107
+ sel
106
108
  end
107
109
  end
108
110
  end
@@ -171,9 +171,10 @@ module Mongo
171
171
  elsif connection.description.mongos?
172
172
  # When server is a mongos:
173
173
  # - $readPreference is never sent when mode is 'primary'
174
- # - When mode is 'secondaryPreferred' $readPreference is only sent
175
- # when a non-mode field (i.e. tag_sets) is present
176
174
  # - Otherwise $readPreference is sent
175
+ # When mode is 'secondaryPreferred' $readPreference is currently
176
+ # required to only be sent when a non-mode field (i.e. tag_sets)
177
+ # is present, but this causes wrong behavior (DRIVERS-1642).
177
178
  if read
178
179
  doc = read.to_mongos
179
180
  if doc
@@ -43,8 +43,8 @@ module Mongo
43
43
  # Msg.new([:more_to_come], {}, { ismaster: 1 },
44
44
  # { type: 1, payload: { identifier: 'documents', sequence: [..] } })
45
45
  #
46
- # @param [ Array<Symbol> ] flags The flag bits. Current supported values
47
- # are :more_to_come and :checksum_present.
46
+ # @param [ Array<Symbol> ] flags The flag bits. Currently supported
47
+ # values are :more_to_come and :checksum_present.
48
48
  # @param [ Hash ] options The options.
49
49
  # @param [ BSON::Document, Hash ] main_document The document that will
50
50
  # become the payload type 0 section. Can contain global args as they
@@ -46,18 +46,18 @@ module Mongo
46
46
  # @example Find all user ids.
47
47
  # Query.new('xgen', 'users', {}, :fields => {:id => 1})
48
48
  #
49
- # @param database [String, Symbol] The database to query.
50
- # @param collection [String, Symbol] The collection to query.
51
- # @param selector [Hash] The query selector.
52
- # @param options [Hash] The additional query options.
49
+ # @param [ String, Symbol ] database The database to query.
50
+ # @param [ String, Symbol ] collection The collection to query.
51
+ # @param [ Hash ] selector The query selector.
52
+ # @param [ Hash ] options The additional query options.
53
53
  #
54
- # @option options :project [Hash] The projection.
55
- # @option options :skip [Integer] The number of documents to skip.
56
- # @option options :limit [Integer] The number of documents to return.
57
- # @option options :flags [Array] The flags for the query message.
58
- #
59
- # Supported flags: +:tailable_cursor+, +:slave_ok+, +:oplog_replay+,
60
- # +:no_cursor_timeout+, +:await_data+, +:exhaust+, +:partial+
54
+ # @option options [ Array<Symbol> ] :flags The flag bits.
55
+ # Currently supported values are :await_data, :exhaust,
56
+ # :no_cursor_timeout, :oplog_replay, :partial, :slave_ok,
57
+ # :tailable_cursor.
58
+ # @option options [ Integer ] :limit The number of documents to return.
59
+ # @option options [ Hash ] :project The projection.
60
+ # @option options [ Integer ] :skip The number of documents to skip.
61
61
  def initialize(database, collection, selector, options = {})
62
62
  @database = database
63
63
  @namespace = "#{database}.#{collection}"