mongo 2.13.2 → 2.13.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/mongo/collection.rb +2 -0
  4. data/lib/mongo/database.rb +14 -2
  5. data/lib/mongo/grid/fs_bucket.rb +37 -37
  6. data/lib/mongo/operation/parallel_scan/command.rb +1 -2
  7. data/lib/mongo/operation/shared/read_preference_supported.rb +38 -36
  8. data/lib/mongo/operation/shared/sessions_supported.rb +3 -2
  9. data/lib/mongo/protocol/msg.rb +2 -2
  10. data/lib/mongo/protocol/query.rb +11 -11
  11. data/lib/mongo/server_selector/secondary_preferred.rb +2 -7
  12. data/lib/mongo/version.rb +1 -1
  13. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  14. data/spec/integration/sdam_events_spec.rb +3 -5
  15. data/spec/integration/secondary_reads_spec.rb +102 -0
  16. data/spec/mongo/index/view_spec.rb +4 -2
  17. data/spec/mongo/operation/read_preference_legacy_spec.rb +9 -19
  18. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  19. data/spec/mongo/server/app_metadata_shared.rb +33 -7
  20. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  21. data/spec/runners/transactions/operation.rb +13 -2
  22. data/spec/shared/bin/get-mongodb-download-url +17 -0
  23. data/spec/shared/lib/mrss/cluster_config.rb +221 -0
  24. data/spec/shared/lib/mrss/constraints.rb +43 -0
  25. data/spec/shared/lib/mrss/docker_runner.rb +265 -0
  26. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  27. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  28. data/spec/shared/lib/mrss/spec_organizer.rb +3 -0
  29. data/spec/shared/lib/mrss/utils.rb +15 -0
  30. data/spec/shared/share/Dockerfile.erb +231 -0
  31. data/spec/shared/shlib/distro.sh +73 -0
  32. data/spec/shared/shlib/server.sh +290 -0
  33. data/spec/shared/shlib/set_env.sh +128 -0
  34. data/spec/support/client_registry.rb +8 -4
  35. data/spec/support/client_registry_macros.rb +14 -5
  36. data/spec/support/spec_config.rb +12 -0
  37. data/spec/support/spec_setup.rb +48 -38
  38. data.tar.gz.sig +0 -0
  39. metadata +998 -978
  40. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1730c9274095cd8113fe9ac6d8f48b82ea3228aeb0b327988ab580105c745e3
4
- data.tar.gz: d8a224877facc2e078dceb63c01eb9351f558ec9ea8a42b9264b909a77855d56
3
+ metadata.gz: 41f37eb33cbd1ebf2c916187b4610eefd895885042eeb3949882ce0a8c4ed069
4
+ data.tar.gz: db734108633f8b5988eaa15de33165182233ba5064f0b805298747256fb578b1
5
5
  SHA512:
6
- metadata.gz: 93e0f291aa4e52e81decda048473b8330eac80c17251cde83fb66592800d066d14010142893fabb00d896cdf409aa0bc7dd25feba3ca27b443d94cd44a6542e6
7
- data.tar.gz: 6e19b5c2a3f846ae7ffb0feab92b92bbfb0d88d21536d8e66eaccfc63f3dc8236cf7672b6aa23e9d51696135138529819f7fd3fff122c4d1608f8bf6081086b8
6
+ metadata.gz: 27c0c7ca84b6b7983767eb652f6aa5968362d8b42a4adf3eab3ee4a11fe4e74d36096b636436ac5075b31207470a67a8c5ecb00eac7f27a3889966ee30d84db9
7
+ data.tar.gz: 86c9fb09b3fb9c9512db2e7caed2f21ac2d53ee75f671d1cbcc92b89afa0d471cc1217552c3971ee9641f90b4c710692ab885c454f8701371726623e16114604
checksums.yaml.gz.sig CHANGED
Binary file
@@ -573,6 +573,8 @@ module Mongo
573
573
  # @param [ Array<Hash> ] documents The documents to insert.
574
574
  # @param [ Hash ] options The insert options.
575
575
  #
576
+ # @option options [ true | false ] :ordered Whether the operations
577
+ # should be executed in order.
576
578
  # @option options [ Session ] :session The session to use for the operation.
577
579
  #
578
580
  # @return [ Result ] The database response wrapper.
@@ -315,8 +315,20 @@ module Mongo
315
315
 
316
316
  # Get the Grid "filesystem" for this database.
317
317
  #
318
- # @example Get the GridFS.
319
- # database.fs
318
+ # @param [ Hash ] options The GridFS options.
319
+ #
320
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
321
+ # collections.
322
+ # @option options [ Integer ] :chunk_size Override the default chunk
323
+ # size.
324
+ # @option options [ String ] :fs_name The prefix for the files and chunks
325
+ # collections.
326
+ # @option options [ String ] :read The read preference.
327
+ # @option options [ Session ] :session The session to use.
328
+ # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
329
+ # option.
330
+ # @option options [ Hash ] :write_concern The write concern options.
331
+ # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
320
332
  #
321
333
  # @return [ Grid::FSBucket ] The GridFS for the database.
322
334
  #
@@ -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
@@ -170,9 +170,10 @@ module Mongo
170
170
  elsif connection.description.mongos?
171
171
  # When server is a mongos:
172
172
  # - $readPreference is never sent when mode is 'primary'
173
- # - When mode is 'secondaryPreferred' $readPreference is only sent
174
- # when a non-mode field (i.e. tag_sets) is present
175
173
  # - Otherwise $readPreference is sent
174
+ # When mode is 'secondaryPreferred' $readPreference is currently
175
+ # required to only be sent when a non-mode field (i.e. tag_sets)
176
+ # is present, but this causes wrong behavior (DRIVERS-1642).
176
177
  if read
177
178
  doc = read.to_mongos
178
179
  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}"
@@ -86,13 +86,8 @@ module Mongo
86
86
  #
87
87
  # @since 2.0.0
88
88
  def to_mongos
89
- if tag_sets.empty? && max_staleness.nil? && hedge.nil?
90
- # The server preference is not sent to mongos as part of the query
91
- # selector if there are no tag sets, for maximum backwards compatibility.
92
- nil
93
- else
94
- to_doc
95
- end
89
+ # Always send the read preference to mongos: DRIVERS-1642.
90
+ to_doc
96
91
  end
97
92
 
98
93
  private
data/lib/mongo/version.rb CHANGED
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.13.2'.freeze
20
+ VERSION = '2.13.3'.freeze
21
21
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'SDAM error handling' do
4
- clean_slate_for_all
4
+ clean_slate
5
5
 
6
6
  after do
7
7
  # Close all clients after every test to avoid leaking expectations into
@@ -67,10 +67,8 @@ describe 'SDAM events' do
67
67
  started_events.length.should <= 10
68
68
 
69
69
  succeeded_events = subscriber.select_succeeded_events(Mongo::Monitoring::Event::ServerHeartbeatSucceeded)
70
- # Since we gracefully close the client, we expect each heartbeat
71
- # to complete.
72
70
  started_events.length.should > 1
73
- (succeeded_events.length-1..succeeded_events.length).should include(started_events.length)
71
+ (succeeded_events.length..succeeded_events.length+1).should include(started_events.length)
74
72
  end
75
73
  end
76
74
 
@@ -109,9 +107,9 @@ describe 'SDAM events' do
109
107
  # There may be in-flight ismasters that don't complete, both
110
108
  # regular and awaited.
111
109
  started_awaited.length.should > 1
112
- (succeeded_awaited.length-1..succeeded_awaited.length).should include(started_awaited.length)
110
+ (succeeded_awaited.length..succeeded_awaited.length+1).should include(started_awaited.length)
113
111
  started_regular.length.should > 1
114
- (succeeded_regular.length-1..succeeded_regular.length).should include(started_regular.length)
112
+ (succeeded_regular.length..succeeded_regular.length+1).should include(started_regular.length)
115
113
  end
116
114
  end
117
115
  end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Secondary reads' do
4
+ before do
5
+ root_authorized_client.use('sr')['secondary_reads'].drop
6
+ root_authorized_client.use('sr')['secondary_reads'].insert_one(test: 1)
7
+ end
8
+
9
+ shared_examples 'performs reads as per read preference' do
10
+
11
+ %i(primary primary_preferred).each do |mode|
12
+
13
+ context mode.inspect do
14
+
15
+ let(:client) do
16
+ root_authorized_client.with(read: {mode: mode}).use('sr')
17
+ end
18
+
19
+ it 'reads from primary' do
20
+ start_stats = get_read_counters
21
+
22
+ 30.times do
23
+ client['secondary_reads'].find.to_a
24
+ end
25
+
26
+ end_stats = get_read_counters
27
+
28
+ end_stats[:secondary].should be_within(10).of(start_stats[:secondary])
29
+ end_stats[:primary].should >= start_stats[:primary] + 30
30
+ end
31
+ end
32
+ end
33
+
34
+ %i(secondary secondary_preferred).each do |mode|
35
+
36
+ context mode.inspect do
37
+ let(:client) do
38
+ root_authorized_client.with(read: {mode: mode}).use('sr')
39
+ end
40
+
41
+ it 'reads from secondaries' do
42
+ start_stats = get_read_counters
43
+
44
+ 30.times do
45
+ client['secondary_reads'].find.to_a
46
+ end
47
+
48
+ end_stats = get_read_counters
49
+
50
+ end_stats[:primary].should be_within(10).of(start_stats[:primary])
51
+ end_stats[:secondary].should >= start_stats[:secondary] + 30
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ context 'replica set' do
58
+ require_topology :replica_set
59
+
60
+ include_examples 'performs reads as per read preference'
61
+ end
62
+
63
+ context 'sharded cluster' do
64
+ require_topology :sharded
65
+
66
+ include_examples 'performs reads as per read preference'
67
+ end
68
+
69
+ def get_read_counters
70
+ client = ClientRegistry.instance.global_client('root_authorized')
71
+ addresses = []
72
+ if client.cluster.sharded?
73
+ doc = client.use('admin').command(listShards: 1).documents.first
74
+ doc['shards'].each do |shard|
75
+ addresses += shard['host'].split('/').last.split(',')
76
+ end
77
+ else
78
+ client.cluster.servers.each do |server|
79
+ next unless server.primary? || server.secondary?
80
+ addresses << server.address.seed
81
+ end
82
+ end
83
+ stats = Hash.new(0)
84
+ addresses.each do |address|
85
+ ClientRegistry.instance.new_local_client(
86
+ [address],
87
+ SpecConfig.instance.all_test_options.merge(connect: :direct),
88
+ ) do |c|
89
+ server = c.cluster.servers.first
90
+ next unless server.primary? || server.secondary?
91
+ stat = c.command(serverStatus: 1).documents.first
92
+ queries = stat['opcounters']['query']
93
+ if server.primary?
94
+ stats[:primary] += queries
95
+ else
96
+ stats[:secondary] += queries
97
+ end
98
+ end
99
+ end
100
+ stats
101
+ end
102
+ end