mongo 2.4.1 → 2.4.2

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 (62) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +3 -3
  5. data/lib/mongo/client.rb +16 -7
  6. data/lib/mongo/cluster.rb +9 -4
  7. data/lib/mongo/cluster/cursor_reaper.rb +1 -0
  8. data/lib/mongo/cluster/topology.rb +1 -1
  9. data/lib/mongo/collection.rb +0 -3
  10. data/lib/mongo/collection/view.rb +12 -5
  11. data/lib/mongo/collection/view/builder/find_command.rb +2 -2
  12. data/lib/mongo/collection/view/readable.rb +4 -4
  13. data/lib/mongo/collection/view/writable.rb +12 -10
  14. data/lib/mongo/cursor.rb +1 -0
  15. data/lib/mongo/error.rb +1 -0
  16. data/lib/mongo/error/invalid_min_pool_size.rb +35 -0
  17. data/lib/mongo/error/operation_failure.rb +24 -5
  18. data/lib/mongo/operation/write/bulk/update/result.rb +1 -10
  19. data/lib/mongo/operation/write/update/result.rb +26 -7
  20. data/lib/mongo/retryable.rb +30 -35
  21. data/lib/mongo/socket.rb +1 -1
  22. data/lib/mongo/socket/tcp.rb +1 -0
  23. data/lib/mongo/version.rb +1 -1
  24. data/spec/mongo/bulk_write_spec.rb +113 -43
  25. data/spec/mongo/client_spec.rb +253 -0
  26. data/spec/mongo/cluster/topology_spec.rb +37 -0
  27. data/spec/mongo/cluster_spec.rb +1 -1
  28. data/spec/mongo/collection/view/aggregation_spec.rb +2 -2
  29. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  30. data/spec/mongo/collection_spec.rb +55 -19
  31. data/spec/mongo/command_monitoring_spec.rb +1 -1
  32. data/spec/mongo/database_spec.rb +5 -5
  33. data/spec/mongo/grid/stream/write_spec.rb +2 -2
  34. data/spec/mongo/index/view_spec.rb +5 -5
  35. data/spec/mongo/max_staleness_spec.rb +0 -4
  36. data/spec/mongo/operation/write/update_spec.rb +15 -3
  37. data/spec/mongo/retryable_spec.rb +26 -22
  38. data/spec/mongo/server/connection_spec.rb +26 -0
  39. data/spec/mongo/shell_examples_spec.rb +981 -0
  40. data/spec/mongo/socket/ssl_spec.rb +51 -18
  41. data/spec/spec_helper.rb +26 -16
  42. data/spec/support/authorization.rb +38 -16
  43. data/spec/support/connection_string.rb +3 -3
  44. data/spec/support/crud.rb +38 -10
  45. data/spec/support/crud/write.rb +6 -3
  46. data/spec/support/crud_tests/write/findOneAndReplace-upsert.yml +48 -4
  47. data/spec/support/crud_tests/write/findOneAndReplace-upsert_pre_2.6.yml +88 -0
  48. data/spec/support/crud_tests/write/findOneAndReplace.yml +0 -29
  49. data/spec/support/crud_tests/write/findOneAndUpdate.yml +4 -1
  50. data/spec/support/crud_tests/write/replaceOne-collation.yml +1 -0
  51. data/spec/support/crud_tests/write/replaceOne-pre_2.6.yml +98 -0
  52. data/spec/support/crud_tests/write/replaceOne.yml +21 -5
  53. data/spec/support/crud_tests/write/updateMany-collation.yml +1 -0
  54. data/spec/support/crud_tests/write/updateMany-pre_2.6.yml +86 -0
  55. data/spec/support/crud_tests/write/updateMany.yml +5 -0
  56. data/spec/support/crud_tests/write/updateOne-collation.yml +1 -0
  57. data/spec/support/crud_tests/write/updateOne-pre_2.6.yml +83 -0
  58. data/spec/support/crud_tests/write/updateOne.yml +7 -2
  59. data/spec/support/gridfs.rb +1 -0
  60. metadata +13 -4
  61. metadata.gz.sig +2 -1
  62. data/spec/support/helpers.rb +0 -140
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9112b76649bf922ef773b7a9018ad8b46de12760
4
- data.tar.gz: d152ad097652a5c4ec4351f9c86a58de77c44977
3
+ metadata.gz: 3730fbcd3d5fa8d756b32b8004613c056760607a
4
+ data.tar.gz: b0d53fb243bab427ae7b1be710aec139791c4952
5
5
  SHA512:
6
- metadata.gz: dbdc8eeb2763a4ce969c20323188997bdc37e7f8b1f0b54fe8ef4288141ec3a43bd61f1e3f8d89b46bae2c8ae875068bc583e3b17eff1631319da2342c283c86
7
- data.tar.gz: 6ca6c80afad5e7882d5c31f5a6c2446a01fb5ed46c1de13f5d8df6dd78d47636ce9253522150dcbf7844f768a07f56670a15bcb264e5c4a9c03475437f302c22
6
+ metadata.gz: 0d0da5cfbaaa055dbccd7f26c8a8ed1887ca81d18fb3ec13c12c86281a6dbc37edb8cdf986fd5d009264e9975b78ba4ba135c7159fd1784c57e6321e42f5ae15
7
+ data.tar.gz: ae9f827422793dc99cf945c4464ac0431648e40a27572602c47d2401e9def79512e70238b79802ac0db7123c28d422d85fcb434ea7179eb9eaa92c74e8462724
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -77,9 +77,9 @@ License
77
77
  See the License for the specific language governing permissions and
78
78
  limitations under the License.
79
79
 
80
- [rubygems-img]: https://badge.fury.io/rb/mongo.png
80
+ [rubygems-img]: https://badge.fury.io/rb/mongo.svg
81
81
  [rubygems-url]: http://badge.fury.io/rb/mongo
82
- [travis-img]: https://secure.travis-ci.org/mongodb/mongo-ruby-driver.png?branch=master
82
+ [travis-img]: https://secure.travis-ci.org/mongodb/mongo-ruby-driver.svg?branch=master
83
83
  [travis-url]: http://travis-ci.org/mongodb/mongo-ruby-driver?branch=master
84
- [codeclimate-img]: https://codeclimate.com/github/mongodb/mongo-ruby-driver.png?branch=master
84
+ [codeclimate-img]: https://codeclimate.com/github/mongodb/mongo-ruby-driver.svg?branch=master
85
85
  [codeclimate-url]: https://codeclimate.com/github/mongodb/mongo-ruby-driver?branch=master
@@ -62,7 +62,7 @@ module Mongo
62
62
  :ssl_cert_string,
63
63
  :ssl_cert_object,
64
64
  :ssl_key,
65
- :ssk_key_string,
65
+ :ssl_key_string,
66
66
  :ssl_key_object,
67
67
  :ssl_key_pass_phrase,
68
68
  :ssl_verify,
@@ -230,9 +230,9 @@ module Mongo
230
230
  def initialize(addresses_or_uri, options = Options::Redacted.new)
231
231
  @monitoring = Monitoring.new(options)
232
232
  if addresses_or_uri.is_a?(::String)
233
- create_from_uri(addresses_or_uri, validate_options(options))
233
+ create_from_uri(addresses_or_uri, validate_options!(options))
234
234
  else
235
- create_from_addresses(addresses_or_uri, validate_options(options))
235
+ create_from_addresses(addresses_or_uri, validate_options!(options))
236
236
  end
237
237
  yield(self) if block_given?
238
238
  end
@@ -291,7 +291,7 @@ module Mongo
291
291
  # @since 2.0.0
292
292
  def with(new_options = Options::Redacted.new)
293
293
  clone.tap do |client|
294
- opts = validate_options(new_options)
294
+ opts = validate_options!(new_options)
295
295
  client.options.update(opts)
296
296
  Database.create(client)
297
297
  # We can't use the same cluster if some options that would affect it
@@ -374,6 +374,7 @@ module Mongo
374
374
  def create_from_uri(connection_string, opts = Options::Redacted.new)
375
375
  uri = URI.new(connection_string, opts)
376
376
  @options = Database::DEFAULT_OPTIONS.merge(uri.client_options.merge(opts)).freeze
377
+ validate_options!(@options)
377
378
  @cluster = Cluster.new(uri.servers, @monitoring, options)
378
379
  @database = Database.new(self, options[:database], options)
379
380
  end
@@ -395,16 +396,24 @@ module Mongo
395
396
  end
396
397
  end
397
398
 
398
- def validate_options(opts = Options::Redacted.new)
399
+ def validate_options!(opts = Options::Redacted.new)
399
400
  return Options::Redacted.new unless opts
400
401
  Options::Redacted.new(opts.select do |o|
401
- if VALID_OPTIONS.include?(o)
402
- true
402
+ if VALID_OPTIONS.include?(o.to_sym)
403
+ validate_max_min_pool_size!(o.to_sym, opts) and true
403
404
  else
404
405
  log_warn("Unsupported client option '#{o}'. It will be ignored.")
405
406
  false
406
407
  end
407
408
  end)
408
409
  end
410
+
411
+ def validate_max_min_pool_size!(option, opts)
412
+ if option == :min_pool_size
413
+ max = opts[:max_pool_size] || Server::ConnectionPool::Queue::MAX_SIZE
414
+ raise Error::InvalidMinPoolSize.new(opts[:min_pool_size], max) unless opts[:min_pool_size] <= max
415
+ end
416
+ true
417
+ end
409
418
  end
410
419
  end
@@ -33,6 +33,11 @@ module Mongo
33
33
  # @since 2.1.1
34
34
  MAX_READ_RETRIES = 1
35
35
 
36
+ # The default number of mongos write retries.
37
+ #
38
+ # @since 2.4.2
39
+ MAX_WRITE_RETRIES = 1
40
+
36
41
  # The default mongos read retry interval, in seconds.
37
42
  #
38
43
  # @since 2.1.1
@@ -174,7 +179,7 @@ module Mongo
174
179
  @cursor_reaper = CursorReaper.new
175
180
  @cursor_reaper.run!
176
181
 
177
- ObjectSpace.define_finalizer(self, self.class.finalize(pools))
182
+ ObjectSpace.define_finalizer(self, self.class.finalize(pools, @cursor_reaper))
178
183
  end
179
184
 
180
185
  # Finalize the cluster for garbage collection. Disconnects all the scoped
@@ -189,10 +194,10 @@ module Mongo
189
194
  # @return [ Proc ] The Finalizer.
190
195
  #
191
196
  # @since 2.2.0
192
- def self.finalize(pools)
197
+ def self.finalize(pools, cursor_reaper)
193
198
  proc do
194
- begin; @cursor_reaper.kill_cursors; rescue; end
195
- @cursor_reaper.stop!
199
+ begin; cursor_reaper.kill_cursors; rescue; end
200
+ cursor_reaper.stop!
196
201
  pools.values.each do |pool|
197
202
  pool.disconnect!
198
203
  end
@@ -42,6 +42,7 @@ module Mongo
42
42
  @to_kill = {}
43
43
  @active_cursors = Set.new
44
44
  @mutex = Mutex.new
45
+ @thread = nil
45
46
  end
46
47
 
47
48
  # Start the cursor reaper's thread.
@@ -49,7 +49,7 @@ module Mongo
49
49
  # @since 2.0.0
50
50
  def initial(seeds, monitoring, options)
51
51
  if options.has_key?(:connect)
52
- OPTIONS.fetch(options[:connect]).new(options, monitoring, seeds)
52
+ OPTIONS.fetch(options[:connect].to_sym).new(options, monitoring, seeds)
53
53
  elsif options.has_key?(:replica_set)
54
54
  ReplicaSet.new(options, monitoring, options)
55
55
  else
@@ -50,9 +50,6 @@ module Mongo
50
50
  # Delegate to the cluster for the next primary.
51
51
  def_delegators :cluster, :next_primary
52
52
 
53
- # Convenience delegators to find.
54
- def_delegators :find, :parallel_scan
55
-
56
53
  # Options that can be updated on a new Collection instance via the #with method.
57
54
  #
58
55
  # @since 2.1.0
@@ -60,8 +60,7 @@ module Mongo
60
60
  :client,
61
61
  :cluster,
62
62
  :database,
63
- :read_preference,
64
- :write_concern
63
+ :read_preference
65
64
 
66
65
  # Delegate to the cluster for the next primary.
67
66
  def_delegators :cluster, :next_primary
@@ -154,12 +153,20 @@ module Mongo
154
153
  " @filter=#{filter.to_s} @options=#{options.to_s}>"
155
154
  end
156
155
 
157
- private
158
-
156
+ # Get the write concern on this +View+.
157
+ #
158
+ # @example Get the write concern.
159
+ # view.write_concern
160
+ #
161
+ # @return [ Mongo::WriteConcern ] The write concern.
162
+ #
163
+ # @since 2.0.0
159
164
  def write_concern
160
165
  WriteConcern.get(options[:write] || options[:write_concern] || collection.write_concern)
161
166
  end
162
167
 
168
+ private
169
+
163
170
  def initialize_copy(other)
164
171
  @collection = other.collection
165
172
  @options = other.options.dup
@@ -178,7 +185,7 @@ module Mongo
178
185
  end
179
186
 
180
187
  def apply_collation!(doc, server, opts = {})
181
- if coll = opts[:collation] || opts['collation'] || collation
188
+ if coll = doc[:collation] || opts[:collation] || opts['collation'] || collation
182
189
  validate_collation!(server, coll)
183
190
  doc[:collation] = coll
184
191
  end
@@ -126,8 +126,8 @@ module Mongo
126
126
  return options if options.empty?
127
127
  opts = options.dup
128
128
  opts.delete(:cursor_type)
129
- Flags.map_flags(options).reduce(opts) do |opts, key|
130
- opts.merge!(key => true)
129
+ Flags.map_flags(options).reduce(opts) do |o, key|
130
+ o.merge!(key => true)
131
131
  end
132
132
  end
133
133
  end
@@ -134,9 +134,9 @@ module Mongo
134
134
  cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
135
135
  cmd[:readConcern] = collection.read_concern if collection.read_concern
136
136
  preference = ServerSelector.get(opts[:read] || read)
137
- server = preference.select_server(cluster)
138
- apply_collation!(cmd, server, opts)
139
137
  read_with_retry do
138
+ server = preference.select_server(cluster, false)
139
+ apply_collation!(cmd, server, opts)
140
140
  Operation::Commands::Command.new({
141
141
  :selector => cmd,
142
142
  :db_name => database.name,
@@ -170,9 +170,9 @@ module Mongo
170
170
  cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
171
171
  cmd[:readConcern] = collection.read_concern if collection.read_concern
172
172
  preference = ServerSelector.get(opts[:read] || read)
173
- server = preference.select_server(cluster)
174
- apply_collation!(cmd, server, opts)
175
173
  read_with_retry do
174
+ server = preference.select_server(cluster, false)
175
+ apply_collation!(cmd, server, opts)
176
176
  Operation::Commands::Command.new({
177
177
  :selector => cmd,
178
178
  :db_name => database.name,
@@ -41,10 +41,10 @@ module Mongo
41
41
  cmd[:maxTimeMS] = max_time_ms if max_time_ms
42
42
  cmd[:writeConcern] = write_concern.options if write_concern
43
43
 
44
- server = next_primary
45
- apply_collation!(cmd, server, opts)
46
-
47
44
  write_with_retry do
45
+ server = next_primary
46
+ apply_collation!(cmd, server, opts)
47
+
48
48
  Operation::Commands::Command.new({
49
49
  :selector => cmd,
50
50
  :db_name => database.name
@@ -108,10 +108,10 @@ module Mongo
108
108
  cmd[:bypassDocumentValidation] = !!opts[:bypass_document_validation]
109
109
  cmd[:writeConcern] = write_concern.options if write_concern
110
110
 
111
- server = next_primary
112
- apply_collation!(cmd, server, opts)
113
-
114
111
  value = write_with_retry do
112
+ server = next_primary
113
+ apply_collation!(cmd, server, opts)
114
+
115
115
  Operation::Commands::Command.new({
116
116
  :selector => cmd,
117
117
  :db_name => database.name
@@ -213,9 +213,10 @@ module Mongo
213
213
 
214
214
  def remove(value, opts = {})
215
215
  delete_doc = { Operation::Q => filter, Operation::LIMIT => value }
216
- server = next_primary
217
- apply_collation!(delete_doc, server, opts)
218
216
  write_with_retry do
217
+ server = next_primary
218
+ apply_collation!(delete_doc, server, opts)
219
+
219
220
  Operation::Write::Delete.new(
220
221
  :delete => delete_doc,
221
222
  :db_name => collection.database.name,
@@ -230,9 +231,10 @@ module Mongo
230
231
  Operation::U => spec,
231
232
  Operation::MULTI => multi,
232
233
  Operation::UPSERT => !!opts[:upsert] }
233
- server = next_primary
234
- apply_collation!(update_doc, server, opts)
235
234
  write_with_retry do
235
+ server = next_primary
236
+ apply_collation!(update_doc, server, opts)
237
+
236
238
  Operation::Write::Update.new(
237
239
  :update => update_doc,
238
240
  :db_name => collection.database.name,
@@ -58,6 +58,7 @@ module Mongo
58
58
  @initial_result = result
59
59
  @remaining = limit if limited?
60
60
  @cursor_id = result.cursor_id
61
+ @coll_name = nil
61
62
  register
62
63
  ObjectSpace.define_finalizer(self, self.class.finalize(result.cursor_id,
63
64
  cluster,
@@ -83,6 +83,7 @@ require 'mongo/error/invalid_database_name'
83
83
  require 'mongo/error/invalid_document'
84
84
  require 'mongo/error/invalid_file'
85
85
  require 'mongo/error/invalid_file_revision'
86
+ require 'mongo/error/invalid_min_pool_size'
86
87
  require 'mongo/error/invalid_application_name'
87
88
  require 'mongo/error/invalid_nonce'
88
89
  require 'mongo/error/invalid_replacement_document'
@@ -0,0 +1,35 @@
1
+ # Copyright (C) 2014-2016 MongoDB, 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 Mongo
16
+ class Error
17
+
18
+ # Exception that is raised when trying to create a client with an invalid
19
+ # min_pool_size option.
20
+ #
21
+ # @since 2.4.2
22
+ class InvalidMinPoolSize < Error
23
+
24
+ # Instantiate the new exception.
25
+ #
26
+ # @example Instantiate the exception.
27
+ # Mongo::Error::InvalidMinPoolSize.new(10, 5)
28
+ #
29
+ # @since 2.4.2
30
+ def initialize(min, max)
31
+ super("Invalid min pool size: #{min}. Please ensure that it is less than the max size: #{max}. ")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -20,19 +20,26 @@ module Mongo
20
20
  # @since 2.0.0
21
21
  class OperationFailure < Error
22
22
 
23
+ # These are magic error messages that could indicate a master change.
24
+ #
25
+ # @since 2.4.2
26
+ WRITE_RETRY_MESSAGES = [
27
+ 'no master',
28
+ 'not master',
29
+ 'could not contact primary',
30
+ 'Not primary'
31
+ ].freeze
32
+
23
33
  # These are magic error messages that could indicate a cluster
24
34
  # reconfiguration behind a mongos. We cannot check error codes as they
25
35
  # change between versions, for example 15988 which has 2 completely
26
36
  # different meanings between 2.4 and 3.0.
27
37
  #
28
38
  # @since 2.1.1
29
- RETRY_MESSAGES = [
39
+ RETRY_MESSAGES = WRITE_RETRY_MESSAGES + [
30
40
  'transport error',
31
41
  'socket exception',
32
42
  "can't connect",
33
- 'no master',
34
- 'not master',
35
- 'could not contact primary',
36
43
  'connect failed',
37
44
  'error querying',
38
45
  'could not get last error',
@@ -42,7 +49,7 @@ module Mongo
42
49
  'dbclient error communicating with server'
43
50
  ].freeze
44
51
 
45
- # Can the operation that caused the error be retried?
52
+ # Can the read operation that caused the error be retried?
46
53
  #
47
54
  # @example Is the error retryable?
48
55
  # error.retryable?
@@ -53,6 +60,18 @@ module Mongo
53
60
  def retryable?
54
61
  RETRY_MESSAGES.any?{ |m| message.include?(m) }
55
62
  end
63
+
64
+ # Can the write operation that caused the error be retried?
65
+ #
66
+ # @example Is the error retryable for writes?
67
+ # error.write_retryable?
68
+ #
69
+ # @return [ true, false ] If the error is retryable.
70
+ #
71
+ # @since 2.4.2
72
+ def write_retryable?
73
+ WRITE_RETRY_MESSAGES.any? { |m| message.include?(m) }
74
+ end
56
75
  end
57
76
  end
58
77
  end
@@ -176,16 +176,7 @@ module Mongo
176
176
  # @return [ Integer ] The number of documents modified.
177
177
  #
178
178
  # @since 2.2.3
179
- def n_modified
180
- return 0 unless acknowledged?
181
- @replies.reduce(0) do |n, reply|
182
- if upsert?(reply)
183
- n
184
- else
185
- updated_existing?(reply) ? n += reply.documents.first[N] : n
186
- end
187
- end
188
- end
179
+ def n_modified; end
189
180
 
190
181
  private
191
182
 
@@ -76,6 +76,18 @@ module Mongo
76
76
  upsert?.first['_id']
77
77
  end
78
78
 
79
+ # Returns the number of documents upserted.
80
+ #
81
+ # @example Get the number of upserted documents.
82
+ # result.upserted_count
83
+ #
84
+ # @return [ Integer ] The number upserted.
85
+ #
86
+ # @since 2.4.2
87
+ def upserted_count
88
+ upsert? ? n : 0
89
+ end
90
+
79
91
  private
80
92
 
81
93
  def upsert?
@@ -121,15 +133,10 @@ module Mongo
121
133
  # @example Get the modified count.
122
134
  # result.modified_count
123
135
  #
124
- # @return [ Integer ] The modified count.
136
+ # @return [ nil ] Always omitted for legacy versions.
125
137
  #
126
138
  # @since 2.0.0
127
- def modified_count
128
- return 0 unless acknowledged?
129
- return n if updated_existing?
130
- return 0 if upsert?
131
- n
132
- end
139
+ def modified_count; end
133
140
 
134
141
  # The identifier of the inserted document if an upsert
135
142
  # took place.
@@ -144,6 +151,18 @@ module Mongo
144
151
  first[UPSERTED] if upsert?
145
152
  end
146
153
 
154
+ # Returns the number of documents upserted.
155
+ #
156
+ # @example Get the number of upserted documents.
157
+ # result.upserted_count
158
+ #
159
+ # @return [ Integer ] The number upserted.
160
+ #
161
+ # @since 2.4.2
162
+ def upserted_count
163
+ upsert? ? n : 0
164
+ end
165
+
147
166
  private
148
167
 
149
168
  def upsert?