mongo 2.1.0.rc0 → 2.1.0

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