mongo 2.4.1 → 2.4.2

Sign up to get free protection for your applications and to get access to all the features.
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?