mongo 2.1.0.rc0 → 2.1.0

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 (110) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +5 -2
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +2 -2
  5. data/lib/mongo.rb +2 -1
  6. data/lib/mongo/address.rb +11 -5
  7. data/lib/mongo/address/ipv4.rb +6 -1
  8. data/lib/mongo/auth/cr/conversation.rb +1 -1
  9. data/lib/mongo/auth/ldap/conversation.rb +1 -1
  10. data/lib/mongo/auth/scram/conversation.rb +1 -1
  11. data/lib/mongo/auth/user/view.rb +2 -2
  12. data/lib/mongo/auth/x509/conversation.rb +1 -1
  13. data/lib/mongo/bulk_write.rb +12 -9
  14. data/lib/mongo/bulk_write/transformable.rb +20 -5
  15. data/lib/mongo/client.rb +11 -11
  16. data/lib/mongo/cluster.rb +2 -2
  17. data/lib/mongo/collection.rb +21 -8
  18. data/lib/mongo/collection/view.rb +1 -0
  19. data/lib/mongo/collection/view/aggregation.rb +11 -5
  20. data/lib/mongo/collection/view/iterable.rb +6 -2
  21. data/lib/mongo/collection/view/map_reduce.rb +39 -5
  22. data/lib/mongo/collection/view/readable.rb +35 -30
  23. data/lib/mongo/collection/view/writable.rb +26 -18
  24. data/lib/mongo/database.rb +12 -2
  25. data/lib/mongo/database/view.rb +4 -3
  26. data/lib/mongo/dbref.rb +4 -4
  27. data/lib/mongo/grid/fs_bucket.rb +8 -1
  28. data/lib/mongo/grid/stream/read.rb +1 -1
  29. data/lib/mongo/index.rb +5 -0
  30. data/lib/mongo/index/view.rb +2 -2
  31. data/lib/mongo/monitoring/command_log_subscriber.rb +9 -3
  32. data/lib/mongo/monitoring/event.rb +1 -0
  33. data/lib/mongo/monitoring/event/command_started.rb +2 -1
  34. data/lib/mongo/monitoring/event/command_succeeded.rb +6 -3
  35. data/lib/mongo/monitoring/event/secure.rb +58 -0
  36. data/lib/mongo/operation.rb +31 -1
  37. data/lib/mongo/operation/commands/collections_info.rb +2 -0
  38. data/lib/mongo/operation/commands/collections_info/result.rb +39 -0
  39. data/lib/mongo/operation/commands/list_indexes/result.rb +2 -1
  40. data/lib/mongo/operation/commands/map_reduce/result.rb +1 -1
  41. data/lib/mongo/operation/read/query.rb +2 -0
  42. data/lib/mongo/operation/read/query/result.rb +40 -0
  43. data/lib/mongo/operation/result.rb +13 -1
  44. data/lib/mongo/operation/write/bulk/delete.rb +2 -2
  45. data/lib/mongo/operation/write/bulk/update.rb +3 -3
  46. data/lib/mongo/operation/write/delete.rb +2 -2
  47. data/lib/mongo/operation/write/update.rb +9 -4
  48. data/lib/mongo/options.rb +1 -0
  49. data/lib/mongo/options/redacted.rb +156 -0
  50. data/lib/mongo/protocol/insert.rb +25 -6
  51. data/lib/mongo/protocol/query.rb +45 -31
  52. data/lib/mongo/protocol/reply.rb +29 -6
  53. data/lib/mongo/protocol/serializers.rb +1 -1
  54. data/lib/mongo/retryable.rb +83 -0
  55. data/lib/mongo/server.rb +16 -3
  56. data/lib/mongo/server/connectable.rb +21 -3
  57. data/lib/mongo/server/connection.rb +38 -4
  58. data/lib/mongo/server/connection_pool.rb +12 -0
  59. data/lib/mongo/server/connection_pool/queue.rb +15 -0
  60. data/lib/mongo/server/monitor/connection.rb +2 -2
  61. data/lib/mongo/server_selector.rb +5 -0
  62. data/lib/mongo/server_selector/selectable.rb +16 -9
  63. data/lib/mongo/socket.rb +6 -2
  64. data/lib/mongo/uri.rb +1 -1
  65. data/lib/mongo/version.rb +1 -1
  66. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +11 -11
  67. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +10 -10
  68. data/spec/mongo/client_spec.rb +101 -18
  69. data/spec/mongo/collection_spec.rb +44 -0
  70. data/spec/mongo/connection_string_spec.rb +36 -58
  71. data/spec/mongo/database_spec.rb +20 -0
  72. data/spec/mongo/grid/fs_bucket_spec.rb +1 -1
  73. data/spec/mongo/grid/stream/write_spec.rb +2 -2
  74. data/spec/mongo/monitoring/event/command_started_spec.rb +26 -0
  75. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +26 -0
  76. data/spec/mongo/monitoring/event/secure_spec.rb +57 -0
  77. data/spec/mongo/operation/commands/aggregate_spec.rb +0 -16
  78. data/spec/mongo/operation/commands/command_spec.rb +0 -18
  79. data/spec/mongo/operation/kill_cursors_spec.rb +0 -16
  80. data/spec/mongo/operation/read/get_more_spec.rb +0 -16
  81. data/spec/mongo/operation/read/query_spec.rb +19 -16
  82. data/spec/mongo/operation/write/bulk/delete_spec.rb +16 -16
  83. data/spec/mongo/operation/write/bulk/update_spec.rb +6 -6
  84. data/spec/mongo/operation/write/command/delete_spec.rb +0 -16
  85. data/spec/mongo/operation/write/command/insert_spec.rb +0 -16
  86. data/spec/mongo/operation/write/command/update_spec.rb +0 -16
  87. data/spec/mongo/operation/write/delete_spec.rb +3 -3
  88. data/spec/mongo/operation/write/update_spec.rb +6 -6
  89. data/spec/mongo/options/redacted_spec.rb +350 -0
  90. data/spec/mongo/protocol/query_spec.rb +15 -1
  91. data/spec/mongo/retryable_spec.rb +147 -0
  92. data/spec/mongo/server/connection_pool/queue_spec.rb +16 -0
  93. data/spec/mongo/server/connection_pool_spec.rb +32 -0
  94. data/spec/mongo/server/connection_spec.rb +37 -0
  95. data/spec/mongo/server_discovery_and_monitoring_spec.rb +24 -59
  96. data/spec/mongo/server_selection_rtt_spec.rb +37 -57
  97. data/spec/mongo/server_selection_spec.rb +2 -0
  98. data/spec/mongo/server_selector/nearest_spec.rb +1 -0
  99. data/spec/mongo/server_selector/primary_preferred_spec.rb +1 -0
  100. data/spec/mongo/server_selector/primary_spec.rb +8 -2
  101. data/spec/mongo/server_selector/secondary_preferred_spec.rb +1 -0
  102. data/spec/mongo/server_selector/secondary_spec.rb +1 -0
  103. data/spec/mongo/server_spec.rb +68 -1
  104. data/spec/mongo/socket/ssl_spec.rb +29 -5
  105. data/spec/mongo/uri_spec.rb +20 -20
  106. data/spec/support/crud.rb +7 -1
  107. data/spec/support/matchers.rb +1 -1
  108. data/spec/support/shared/server_selector.rb +58 -2
  109. metadata +20 -5
  110. metadata.gz.sig +0 -0
@@ -104,6 +104,26 @@ module Mongo
104
104
  # @since 2.1.0
105
105
  class Upconverter
106
106
 
107
+ # Insert field constant.
108
+ #
109
+ # @since 2.1.0
110
+ INSERT = 'insert'.freeze
111
+
112
+ # Documents field constant.
113
+ #
114
+ # @since 2.1.0
115
+ DOCUMENTS = 'documents'.freeze
116
+
117
+ # Ordered field constant.
118
+ #
119
+ # @since 2.1.0
120
+ ORDERED = 'ordered'.freeze
121
+
122
+ # Write concern field constant.
123
+ #
124
+ # @since 2.1.0
125
+ WRITE_CONCERN = 'writeConcern'.freeze
126
+
107
127
  # @return [ String ] collection The name of the collection.
108
128
  attr_reader :collection
109
129
 
@@ -138,12 +158,11 @@ module Mongo
138
158
  #
139
159
  # @since 2.1.0
140
160
  def command
141
- document = BSON::Document.new(
142
- insert: collection,
143
- documents: documents,
144
- ordered: options.fetch(:ordered, true)
145
- )
146
- document.merge!(writeConcern: options[:write_concern].options) if options[:write_concern]
161
+ document = BSON::Document.new
162
+ document.store(INSERT, collection)
163
+ document.store(DOCUMENTS, documents)
164
+ document.store(ORDERED, options.fetch(:ordered, true))
165
+ document.merge!(WRITE_CONCERN => options[:write_concern].options) if options[:write_concern]
147
166
  document
148
167
  end
149
168
  end
@@ -158,37 +158,47 @@ module Mongo
158
158
  #
159
159
  # @since 2.1.0
160
160
  OPTION_MAPPINGS = {
161
- :project => :projection,
162
- :skip => :skip,
163
- :limit => :limit,
164
- :batch_size => :batchSize
165
- }
161
+ :project => 'projection',
162
+ :skip => 'skip',
163
+ :limit => 'limit',
164
+ :batch_size => 'batchSize'
165
+ }.freeze
166
166
 
167
167
  SPECIAL_FIELD_MAPPINGS = {
168
- :$readPreference => :readPreference,
169
- :$orderby => :sort,
170
- :$hint => :hint,
171
- :$comment => :comment,
172
- :$returnKey => :returnKey,
173
- :$snapshot => :snapshot,
174
- :$maxScan => :maxScan,
175
- :$max => :max,
176
- :$min => :min,
177
- :$maxTimeMS => :maxTimeMS,
178
- :$showDiskLoc => :showRecordId,
179
- :$explain => :explain
180
- }
168
+ :$readPreference => 'readPreference',
169
+ :$orderby => 'sort',
170
+ :$hint => 'hint',
171
+ :$comment => 'comment',
172
+ :$returnKey => 'returnKey',
173
+ :$snapshot => 'snapshot',
174
+ :$maxScan => 'maxScan',
175
+ :$max => 'max',
176
+ :$min => 'min',
177
+ :$maxTimeMS => 'maxTimeMS',
178
+ :$showDiskLoc => 'showRecordId',
179
+ :$explain => 'explain'
180
+ }.freeze
181
181
 
182
182
  # Mapping of flags to find command options.
183
183
  #
184
184
  # @since 2.1.0
185
185
  FLAG_MAPPINGS = {
186
- :tailable_cursor => :tailable,
187
- :oplog_replay => :oplogReplay,
188
- :no_cursor_timeout => :noCursorTimeout,
189
- :await_data => :awaitData,
190
- :partial => :allowPartialResults
191
- }
186
+ :tailable_cursor => 'tailable',
187
+ :oplog_replay => 'oplogReplay',
188
+ :no_cursor_timeout => 'noCursorTimeout',
189
+ :await_data => 'awaitData',
190
+ :partial => 'allowPartialResults'
191
+ }.freeze
192
+
193
+ # Find command constant.
194
+ #
195
+ # @since 2.1.0
196
+ FIND = 'find'.freeze
197
+
198
+ # Filter attribute constant.
199
+ #
200
+ # @since 2.1.0
201
+ FILTER = 'filter'.freeze
192
202
 
193
203
  # @return [ String ] collection The name of the collection.
194
204
  attr_reader :collection
@@ -242,7 +252,7 @@ module Mongo
242
252
  #
243
253
  # @since 2.1.0
244
254
  def command_name
245
- command? ? filter.keys.first : 'find'
255
+ command? ? filter.keys.first : FIND
246
256
  end
247
257
 
248
258
  private
@@ -252,21 +262,25 @@ module Mongo
252
262
  end
253
263
 
254
264
  def op_command
255
- BSON::Document.new(filter)
265
+ document = BSON::Document.new
266
+ filter.each do |field, value|
267
+ document.store(field.to_s, value)
268
+ end
269
+ document
256
270
  end
257
271
 
258
272
  def find_command
259
273
  document = BSON::Document.new
260
- document[:find] = collection
261
- document[:filter] = filter[:$query] ? filter[:$query] : filter
274
+ document.store(FIND, collection)
275
+ document.store(FILTER, filter[:$query] ? filter[:$query] : filter)
262
276
  OPTION_MAPPINGS.each do |legacy, option|
263
- document[option] = options[legacy] unless options[legacy].nil?
277
+ document.store(option, options[legacy]) unless options[legacy].nil?
264
278
  end
265
279
  SPECIAL_FIELD_MAPPINGS.each do |special, normal|
266
- document[normal] = filter[special] unless filter[special].nil?
280
+ document.store(normal, filter[special]) unless filter[special].nil?
267
281
  end
268
282
  FLAG_MAPPINGS.each do |legacy, flag|
269
- document[flag] = true if flags.include?(legacy)
283
+ document.store(flag, true) if flags.include?(legacy)
270
284
  end
271
285
  document
272
286
  end
@@ -101,6 +101,26 @@ module Mongo
101
101
  # @since 2.1.0
102
102
  class Upconverter
103
103
 
104
+ # Next batch constant.
105
+ #
106
+ # @since 2.1.0
107
+ NEXT_BATCH = 'nextBatch'.freeze
108
+
109
+ # First batch constant.
110
+ #
111
+ # @since 2.1.0
112
+ FIRST_BATCH = 'firstBatch'.freeze
113
+
114
+ # Cursor field constant.
115
+ #
116
+ # @since 2.1.0
117
+ CURSOR = 'cursor'.freeze
118
+
119
+ # Id field constant.
120
+ #
121
+ # @since 2.1.0
122
+ ID = 'id'.freeze
123
+
104
124
  # @return [ Array<BSON::Document> ] documents The documents.
105
125
  attr_reader :documents
106
126
 
@@ -141,18 +161,21 @@ module Mongo
141
161
  private
142
162
 
143
163
  def batch_field
144
- starting_from > 0 ? :nextBatch : :firstBatch
164
+ starting_from > 0 ? NEXT_BATCH : FIRST_BATCH
145
165
  end
146
166
 
147
167
  def command?
148
- !documents.empty? && documents.first.key?('ok')
168
+ !documents.empty? && documents.first.key?(Operation::Result::OK)
149
169
  end
150
170
 
151
171
  def find_command
152
- BSON::Document.new(
153
- ok: 1,
154
- cursor: BSON::Document.new(id: cursor_id, batch_field => documents)
155
- )
172
+ document = BSON::Document.new
173
+ cursor_document = BSON::Document.new
174
+ cursor_document.store(ID, cursor_id)
175
+ cursor_document.store(batch_field, documents)
176
+ document.store(Operation::Result::OK, 1)
177
+ document.store(CURSOR, cursor_document)
178
+ document
156
179
  end
157
180
 
158
181
  def op_command
@@ -74,7 +74,7 @@ module Mongo
74
74
  # @param value [String] The string to be serialized.
75
75
  # @return [String] Buffer with serialized value.
76
76
  def self.serialize(buffer, value)
77
- buffer << value
77
+ buffer << value.force_encoding(BSON::BINARY)
78
78
  buffer << NULL
79
79
  end
80
80
  end
@@ -0,0 +1,83 @@
1
+ # Copyright (C) 2015 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
+
17
+ # Defines basic behaviour around retrying operations.
18
+ #
19
+ # @since 2.1.0
20
+ module Retryable
21
+
22
+ # The not master error message.
23
+ #
24
+ # @since 2.1.0
25
+ NOT_MASTER = 'not master'.freeze
26
+
27
+ # Execute a read operation with a retry.
28
+ #
29
+ # @example Execute the read.
30
+ # read_with_retry do
31
+ # ...
32
+ # end
33
+ #
34
+ # @note This only retries read operations on socket errors.
35
+ #
36
+ # @param [ Proc ] block The block to execute.
37
+ #
38
+ # @return [ Result ] The result of the operation.
39
+ #
40
+ # @since 2.1.0
41
+ def read_with_retry(&block)
42
+ begin
43
+ block.call
44
+ rescue Error::SocketError, Error::SocketTimeoutError
45
+ retry_operation(&block)
46
+ end
47
+ end
48
+
49
+ # Execute a write operation with a retry.
50
+ #
51
+ # @example Execute the write.
52
+ # write_with_retry do
53
+ # ...
54
+ # end
55
+ #
56
+ # @note This only retries operations on not master failures, since it is
57
+ # the only case we can be sure a partial write did not already occur.
58
+ #
59
+ # @param [ Proc ] block The block to execute.
60
+ #
61
+ # @return [ Result ] The result of the operation.
62
+ #
63
+ # @since 2.1.0
64
+ def write_with_retry(&block)
65
+ begin
66
+ block.call
67
+ rescue Error::OperationFailure => e
68
+ if e.message.include?(NOT_MASTER)
69
+ retry_operation(&block)
70
+ else
71
+ raise e
72
+ end
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def retry_operation(&block)
79
+ cluster.scan!
80
+ block.call
81
+ end
82
+ end
83
+ end
@@ -92,6 +92,21 @@ module Mongo
92
92
  Context.new(self)
93
93
  end
94
94
 
95
+ # Determine if a connection to the server is able to be established and
96
+ # messages can be sent to it.
97
+ #
98
+ # @example Is the server connectable?
99
+ # server.connectable?
100
+ #
101
+ # @return [ true, false ] If the server is connectable.
102
+ #
103
+ # @since 2.1.0
104
+ def connectable?
105
+ context.with_connection do |connection|
106
+ connection.connectable?
107
+ end
108
+ end
109
+
95
110
  # Disconnect the server from the connection.
96
111
  #
97
112
  # @example Disconnect the server.
@@ -101,9 +116,7 @@ module Mongo
101
116
  #
102
117
  # @since 2.0.0
103
118
  def disconnect!
104
- context.with_connection do |connection|
105
- connection.disconnect!
106
- end
119
+ pool.disconnect!
107
120
  monitor.stop! and true
108
121
  end
109
122
 
@@ -20,6 +20,11 @@ module Mongo
20
20
  # @since 2.0.0
21
21
  module Connectable
22
22
 
23
+ # The ssl option prefix.
24
+ #
25
+ # @since 2.1.0
26
+ SSL = 'ssl'.freeze
27
+
23
28
  # The default time in seconds to timeout a connection attempt.
24
29
  #
25
30
  # @since 2.0.0
@@ -34,6 +39,19 @@ module Mongo
34
39
  # @return [ Integer ] pid The process id when the connection was created.
35
40
  attr_reader :pid
36
41
 
42
+ # Determine if the server is connectable. This will check not only if the
43
+ # connection exists, but if messages can send to it successfully.
44
+ #
45
+ # @example Is the server connectable?
46
+ # connection.connectable?
47
+ #
48
+ # @return [ true, false ] If the connection is connectable.
49
+ #
50
+ # @since 2.1.0
51
+ def connectable?
52
+ begin; ping; rescue; false; end
53
+ end
54
+
37
55
  # Determine if the connection is currently connected.
38
56
  #
39
57
  # @example Is the connection connected?
@@ -41,7 +59,7 @@ module Mongo
41
59
  #
42
60
  # @return [ true, false ] If connected.
43
61
  #
44
- # @since 2.0.0
62
+ # @deprecated Use #connectable? instead
45
63
  def connected?
46
64
  !!@socket && @socket.alive?
47
65
  end
@@ -68,10 +86,10 @@ module Mongo
68
86
 
69
87
  def ensure_connected
70
88
  ensure_same_process!
71
- connect! if socket.nil? || !socket.alive?
89
+ connect!
72
90
  begin
73
91
  yield socket
74
- rescue Error::SocketError, Error::SocketTimeoutError => e
92
+ rescue Exception => e
75
93
  disconnect!
76
94
  raise e
77
95
  end
@@ -23,6 +23,21 @@ module Mongo
23
23
  include Monitoring::Publishable
24
24
  extend Forwardable
25
25
 
26
+ # The ping command.
27
+ #
28
+ # @since 2.1.0
29
+ PING = { :ping => 1 }.freeze
30
+
31
+ # Ping message.
32
+ #
33
+ # @since 2.1.0
34
+ PING_MESSAGE = Protocol::Query.new(Database::ADMIN, Database::COMMAND, PING, :limit => -1)
35
+
36
+ # The ping message as raw bytes.
37
+ #
38
+ # @since 2.1.0
39
+ PING_BYTES = PING_MESSAGE.serialize.freeze
40
+
26
41
  # @return [ Mongo::Auth::CR, Mongo::Auth::X509, Mongo::Auth:LDAP, Mongo::Auth::SCRAM ]
27
42
  # authenticator The authentication strategy.
28
43
  attr_reader :authenticator
@@ -60,7 +75,7 @@ module Mongo
60
75
  def connect!
61
76
  unless socket
62
77
  @socket = address.socket(timeout, ssl_options)
63
- @socket.connect!
78
+ socket.connect!
64
79
  if authenticator
65
80
  authenticator.login(self)
66
81
  @authenticated = true
@@ -133,12 +148,31 @@ module Mongo
133
148
  @monitoring = server.monitoring
134
149
  @options = options.freeze
135
150
  @server = server
136
- @ssl_options = options.reject { |k, v| !k.to_s.start_with?('ssl') }
151
+ @ssl_options = options.reject { |k, v| !k.to_s.start_with?(SSL) }
137
152
  @socket = nil
138
153
  @pid = Process.pid
139
154
  setup_authentication!
140
155
  end
141
156
 
157
+ # Ping the connection to see if the server is responding to commands.
158
+ # This is non-blocking on the server side.
159
+ #
160
+ # @example Ping the connection.
161
+ # connection.ping
162
+ #
163
+ # @note This uses a pre-serialized ping message for optimization.
164
+ #
165
+ # @return [ true, false ] If the server is accepting connections.
166
+ #
167
+ # @since 2.1.0
168
+ def ping
169
+ ensure_connected do |socket|
170
+ socket.write(PING_BYTES)
171
+ reply = Protocol::Reply.deserialize(socket)
172
+ reply.documents[0][Operation::Result::OK] == 1
173
+ end
174
+ end
175
+
142
176
  private
143
177
 
144
178
  def deliver(messages)
@@ -149,12 +183,12 @@ module Mongo
149
183
  def setup_authentication!
150
184
  if options[:user]
151
185
  default_mechanism = @server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr
152
- user = Auth::User.new({ :auth_mech => default_mechanism }.merge(options))
186
+ user = Auth::User.new(Options::Redacted.new(:auth_mech => default_mechanism).merge(options))
153
187
  @authenticator = Auth.get(user)
154
188
  end
155
189
  end
156
190
 
157
- def write(messages, buffer = '')
191
+ def write(messages, buffer = ''.force_encoding(BSON::BINARY))
158
192
  start_size = 0
159
193
  messages.each do |message|
160
194
  message.serialize(buffer, max_bson_object_size)