mongo 1.3.0 → 1.12.5

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 (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
@@ -1,887 +1,19 @@
1
- # encoding: UTF-8
2
-
3
- # --
4
- # Copyright (C) 2008-2011 10gen Inc.
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
5
2
  #
6
3
  # Licensed under the Apache License, Version 2.0 (the "License");
7
4
  # you may not use this file except in compliance with the License.
8
5
  # You may obtain a copy of the License at
9
6
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
11
8
  #
12
9
  # Unless required by applicable law or agreed to in writing, software
13
10
  # distributed under the License is distributed on an "AS IS" BASIS,
14
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
12
  # See the License for the specific language governing permissions and
16
13
  # limitations under the License.
17
- # ++
18
-
19
- require 'set'
20
- require 'socket'
21
- require 'thread'
22
-
23
- module Mongo
24
-
25
- # Instantiates and manages connections to MongoDB.
26
- class Connection
27
- TCPSocket = ::TCPSocket
28
- Mutex = ::Mutex
29
- ConditionVariable = ::ConditionVariable
30
-
31
- # Abort connections if a ConnectionError is raised.
32
- Thread.abort_on_exception = true
33
-
34
- DEFAULT_PORT = 27017
35
- STANDARD_HEADER_SIZE = 16
36
- RESPONSE_HEADER_SIZE = 20
37
-
38
- attr_reader :logger, :size, :auths, :primary, :safe, :primary_pool, :host_to_try, :pool_size
39
-
40
- # Counter for generating unique request ids.
41
- @@current_request_id = 0
42
-
43
- # Create a connection to single MongoDB instance.
44
- #
45
- # You may specify whether connection to slave is permitted.
46
- # In all cases, the default host is "localhost" and the default port is 27017.
47
- #
48
- # If you're connecting to a replica set, you'll need to use ReplSetConnection.new instead.
49
- #
50
- # Once connected to a replica set, you can find out which nodes are primary, secondary, and
51
- # arbiters with the corresponding accessors: Connection#primary, Connection#secondaries, and
52
- # Connection#arbiters. This is useful if your application needs to connect manually to nodes other
53
- # than the primary.
54
- #
55
- # @param [String, Hash] host.
56
- # @param [Integer] port specify a port number here if only one host is being specified.
57
- #
58
- # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
59
- # propogated to DB objects instantiated off of this Connection. This
60
- # default can be overridden upon instantiation of any DB by explicity setting a :safe value
61
- # on initialization.
62
- # @option opts [Boolean] :slave_ok (false) Must be set to +true+ when connecting
63
- # to a single, slave node.
64
- # @option opts [Logger, #debug] :logger (nil) A Logger instance for debugging driver ops. Note that
65
- # logging negatively impacts performance; therefore, it should not be used for high-performance apps.
66
- # @option opts [Integer] :pool_size (1) The maximum number of socket connections allowed per
67
- # connection pool. Note: this setting is relevant only for multi-threaded applications.
68
- # @option opts [Float] :timeout (5.0) When all of the connections a pool are checked out,
69
- # this is the number of seconds to wait for a new connection to be released before throwing an exception.
70
- # Note: this setting is relevant only for multi-threaded applications (which in Ruby are rare).
71
- # @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
72
- # Disabled by default.
73
- #
74
- # @example localhost, 27017
75
- # Connection.new
76
- #
77
- # @example localhost, 27017
78
- # Connection.new("localhost")
79
- #
80
- # @example localhost, 3000, max 5 connections, with max 5 seconds of wait time.
81
- # Connection.new("localhost", 3000, :pool_size => 5, :timeout => 5)
82
- #
83
- # @example localhost, 3000, where this node may be a slave
84
- # Connection.new("localhost", 3000, :slave_ok => true)
85
- #
86
- # @see http://api.mongodb.org/ruby/current/file.REPLICA_SETS.html Replica sets in Ruby
87
- #
88
- # @raise [ReplicaSetConnectionError] This is raised if a replica set name is specified and the
89
- # driver fails to connect to a replica set with that name.
90
- #
91
- # @core connections
92
- def initialize(host=nil, port=nil, opts={})
93
- @host_to_try = format_pair(host, port)
94
-
95
- # Host and port of current master.
96
- @host = @port = nil
97
-
98
- # slave_ok can be true only if one node is specified
99
- @slave_ok = opts[:slave_ok]
100
-
101
- setup(opts)
102
- end
103
-
104
- # DEPRECATED
105
- #
106
- # Initialize a connection to a MongoDB replica set using an array of seed nodes.
107
- #
108
- # The seed nodes specified will be used on the initial connection to the replica set, but note
109
- # that this list of nodes will be replced by the list of canonical nodes returned by running the
110
- # is_master command on the replica set.
111
- #
112
- # @param nodes [Array] An array of arrays, each of which specifies a host and port.
113
- # @param opts [Hash] Any of the available options that can be passed to Connection.new.
114
- #
115
- # @option opts [String] :rs_name (nil) The name of the replica set to connect to. An exception will be
116
- # raised if unable to connect to a replica set with this name.
117
- # @option opts [Boolean] :read_secondary (false) When true, this connection object will pick a random slave
118
- # to send reads to.
119
- #
120
- # @example
121
- # Connection.multi([["db1.example.com", 27017], ["db2.example.com", 27017]])
122
- #
123
- # @example This connection will read from a random secondary node.
124
- # Connection.multi([["db1.example.com", 27017], ["db2.example.com", 27017], ["db3.example.com", 27017]],
125
- # :read_secondary => true)
126
- #
127
- # @return [Mongo::Connection]
128
- #
129
- # @deprecated
130
- def self.multi(nodes, opts={})
131
- warn "Connection.multi is now deprecated. Please use ReplSetConnection.new instead."
132
-
133
- nodes << opts
134
- ReplSetConnection.new(*nodes)
135
- end
136
-
137
- # Initialize a connection to MongoDB using the MongoDB URI spec:
138
- #
139
- # @param uri [String]
140
- # A string of the format mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]
141
- #
142
- # @param opts Any of the options available for Connection.new
143
- #
144
- # @return [Mongo::Connection, Mongo::ReplSetConnection]
145
- def self.from_uri(string, extra_opts={})
146
- uri = URIParser.new(string)
147
- opts = uri.connection_options
148
- opts.merge!(extra_opts)
149
-
150
- if uri.nodes.length == 1
151
- opts.merge!({:auths => uri.auths})
152
- Connection.new(uri.nodes[0][0], uri.nodes[0][1], opts)
153
- elsif uri.nodes.length > 1
154
- nodes = uri.nodes.clone
155
- nodes_with_opts = nodes << opts
156
- ReplSetConnection.new(*nodes_with_opts)
157
- else
158
- raise MongoArgumentError, "No nodes specified. Please ensure that you've provided at least one node."
159
- end
160
- end
161
-
162
- # The host name used for this connection.
163
- #
164
- # @return [String]
165
- def host
166
- @primary_pool.host
167
- end
168
-
169
- # The port used for this connection.
170
- #
171
- # @return [Integer]
172
- def port
173
- @primary_pool.port
174
- end
175
-
176
- # Fsync, then lock the mongod process against writes. Use this to get
177
- # the datafiles in a state safe for snapshotting, backing up, etc.
178
- #
179
- # @return [BSON::OrderedHash] the command response
180
- def lock!
181
- cmd = BSON::OrderedHash.new
182
- cmd[:fsync] = 1
183
- cmd[:lock] = true
184
- self['admin'].command(cmd)
185
- end
186
-
187
- # Is this database locked against writes?
188
- #
189
- # @return [Boolean]
190
- def locked?
191
- self['admin']['$cmd.sys.inprog'].find_one['fsyncLock'] == 1
192
- end
193
-
194
- # Unlock a previously fsync-locked mongod process.
195
- #
196
- # @return [BSON::OrderedHash] command response
197
- def unlock!
198
- self['admin']['$cmd.sys.unlock'].find_one
199
- end
200
-
201
- # Apply each of the saved database authentications.
202
- #
203
- # @return [Boolean] returns true if authentications exist and succeeed, false
204
- # if none exists.
205
- #
206
- # @raise [AuthenticationError] raises an exception if any one
207
- # authentication fails.
208
- def apply_saved_authentication(opts={})
209
- return false if @auths.empty?
210
- @auths.each do |auth|
211
- self[auth['db_name']].issue_authentication(auth['username'], auth['password'], false,
212
- :socket => opts[:socket])
213
- end
214
- true
215
- end
216
-
217
- # Save an authentication to this connection. When connecting,
218
- # the connection will attempt to re-authenticate on every db
219
- # specificed in the list of auths. This method is called automatically
220
- # by DB#authenticate.
221
- #
222
- # Note: this method will not actually issue an authentication command. To do that,
223
- # either run Connection#apply_saved_authentication or DB#authenticate.
224
- #
225
- # @param [String] db_name
226
- # @param [String] username
227
- # @param [String] password
228
- #
229
- # @return [Hash] a hash representing the authentication just added.
230
- def add_auth(db_name, username, password)
231
- remove_auth(db_name)
232
- auth = {}
233
- auth['db_name'] = db_name
234
- auth['username'] = username
235
- auth['password'] = password
236
- @auths << auth
237
- auth
238
- end
239
-
240
- # Remove a saved authentication for this connection.
241
- #
242
- # @param [String] db_name
243
- #
244
- # @return [Boolean]
245
- def remove_auth(db_name)
246
- return unless @auths
247
- if @auths.reject! { |a| a['db_name'] == db_name }
248
- true
249
- else
250
- false
251
- end
252
- end
253
-
254
- # Remove all authenication information stored in this connection.
255
- #
256
- # @return [true] this operation return true because it always succeeds.
257
- def clear_auths
258
- @auths = []
259
- true
260
- end
261
-
262
- def authenticate_pools
263
- @primary_pool.authenticate_existing
264
- end
265
-
266
- def logout_pools(db)
267
- @primary_pool.logout_existing(db)
268
- end
269
-
270
- # Return a hash with all database names
271
- # and their respective sizes on disk.
272
- #
273
- # @return [Hash]
274
- def database_info
275
- doc = self['admin'].command({:listDatabases => 1})
276
- doc['databases'].each_with_object({}) do |db, info|
277
- info[db['name']] = db['sizeOnDisk'].to_i
278
- end
279
- end
280
-
281
- # Return an array of database names.
282
- #
283
- # @return [Array]
284
- def database_names
285
- database_info.keys
286
- end
287
-
288
- # Return a database with the given name.
289
- # See DB#new for valid options hash parameters.
290
- #
291
- # @param [String] db_name a valid database name.
292
- # @param [Hash] opts options to be passed to the DB constructor.
293
- #
294
- # @return [Mongo::DB]
295
- #
296
- # @core databases db-instance_method
297
- def db(db_name, opts={})
298
- DB.new(db_name, self, opts)
299
- end
300
-
301
- # Shortcut for returning a database. Use DB#db to accept options.
302
- #
303
- # @param [String] db_name a valid database name.
304
- #
305
- # @return [Mongo::DB]
306
- #
307
- # @core databases []-instance_method
308
- def [](db_name)
309
- DB.new(db_name, self, :safe => @safe)
310
- end
311
-
312
- # Drop a database.
313
- #
314
- # @param [String] name name of an existing database.
315
- def drop_database(name)
316
- self[name].command(:dropDatabase => 1)
317
- end
318
-
319
- # Copy the database +from+ to +to+ on localhost. The +from+ database is
320
- # assumed to be on localhost, but an alternate host can be specified.
321
- #
322
- # @param [String] from name of the database to copy from.
323
- # @param [String] to name of the database to copy to.
324
- # @param [String] from_host host of the 'from' database.
325
- # @param [String] username username for authentication against from_db (>=1.3.x).
326
- # @param [String] password password for authentication against from_db (>=1.3.x).
327
- def copy_database(from, to, from_host="localhost", username=nil, password=nil)
328
- oh = BSON::OrderedHash.new
329
- oh[:copydb] = 1
330
- oh[:fromhost] = from_host
331
- oh[:fromdb] = from
332
- oh[:todb] = to
333
- if username || password
334
- unless username && password
335
- raise MongoArgumentError, "Both username and password must be supplied for authentication."
336
- end
337
- nonce_cmd = BSON::OrderedHash.new
338
- nonce_cmd[:copydbgetnonce] = 1
339
- nonce_cmd[:fromhost] = from_host
340
- result = self["admin"].command(nonce_cmd)
341
- oh[:nonce] = result["nonce"]
342
- oh[:username] = username
343
- oh[:key] = Mongo::Support.auth_key(username, password, oh[:nonce])
344
- end
345
- self["admin"].command(oh)
346
- end
347
-
348
- # Checks if a server is alive. This command will return immediately
349
- # even if the server is in a lock.
350
- #
351
- # @return [Hash]
352
- def ping
353
- self["admin"].command({:ping => 1})
354
- end
355
-
356
- # Get the build information for the current connection.
357
- #
358
- # @return [Hash]
359
- def server_info
360
- self["admin"].command({:buildinfo => 1})
361
- end
362
-
363
-
364
- # Get the build version of the current server.
365
- #
366
- # @return [Mongo::ServerVersion]
367
- # object allowing easy comparability of version.
368
- def server_version
369
- ServerVersion.new(server_info["version"])
370
- end
371
-
372
- # Is it okay to connect to a slave?
373
- #
374
- # @return [Boolean]
375
- def slave_ok?
376
- @slave_ok
377
- end
378
-
379
- # Send a message to MongoDB, adding the necessary headers.
380
- #
381
- # @param [Integer] operation a MongoDB opcode.
382
- # @param [BSON::ByteBuffer] message a message to send to the database.
383
- #
384
- # @return [Integer] number of bytes sent
385
- def send_message(operation, message, log_message=nil)
386
- begin
387
- add_message_headers(message, operation)
388
- packed_message = message.to_s
389
- socket = checkout_writer
390
- send_message_on_socket(packed_message, socket)
391
- ensure
392
- checkin_writer(socket)
393
- end
394
- end
395
-
396
- # Sends a message to the database, waits for a response, and raises
397
- # an exception if the operation has failed.
398
- #
399
- # @param [Integer] operation a MongoDB opcode.
400
- # @param [BSON::ByteBuffer] message a message to send to the database.
401
- # @param [String] db_name the name of the database. used on call to get_last_error.
402
- # @param [Hash] last_error_params parameters to be sent to getLastError. See DB#error for
403
- # available options.
404
- #
405
- # @see DB#get_last_error for valid last error params.
406
- #
407
- # @return [Hash] The document returned by the call to getlasterror.
408
- def send_message_with_safe_check(operation, message, db_name, log_message=nil, last_error_params=false)
409
- docs = num_received = cursor_id = ''
410
- add_message_headers(message, operation)
411
-
412
- last_error_message = BSON::ByteBuffer.new
413
- build_last_error_message(last_error_message, db_name, last_error_params)
414
- last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY)
415
-
416
- packed_message = message.append!(last_error_message).to_s
417
- begin
418
- sock = checkout_writer
419
- @safe_mutexes[sock].synchronize do
420
- send_message_on_socket(packed_message, sock)
421
- docs, num_received, cursor_id = receive(sock, last_error_id)
422
- end
423
- ensure
424
- checkin_writer(sock)
425
- end
426
-
427
- if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg'])
428
- close if error == "not master"
429
- error = "wtimeout" if error == "timeout"
430
- raise Mongo::OperationFailure, docs[0]['code'].to_s + ': ' + error
431
- end
432
-
433
- docs[0]
434
- end
435
-
436
- # Sends a message to the database and waits for the response.
437
- #
438
- # @param [Integer] operation a MongoDB opcode.
439
- # @param [BSON::ByteBuffer] message a message to send to the database.
440
- # @param [Socket] socket a socket to use in lieu of checking out a new one.
441
- #
442
- # @return [Array]
443
- # An array whose indexes include [0] documents returned, [1] number of document received,
444
- # and [3] a cursor_id.
445
- def receive_message(operation, message, log_message=nil, socket=nil, command=false)
446
- request_id = add_message_headers(message, operation)
447
- packed_message = message.to_s
448
- begin
449
- if socket
450
- sock = socket
451
- checkin = false
452
- else
453
- sock = (command ? checkout_writer : checkout_reader)
454
- checkin = true
455
- end
456
-
457
- result = ''
458
- @safe_mutexes[sock].synchronize do
459
- send_message_on_socket(packed_message, sock)
460
- result = receive(sock, request_id)
461
- end
462
- ensure
463
- if checkin
464
- command ? checkin_writer(sock) : checkin_reader(sock)
465
- end
466
- end
467
- result
468
- end
469
-
470
- # Create a new socket and attempt to connect to master.
471
- # If successful, sets host and port to master and returns the socket.
472
- #
473
- # If connecting to a replica set, this method will replace the
474
- # initially-provided seed list with any nodes known to the set.
475
- #
476
- # @raise [ConnectionFailure] if unable to connect to any host or port.
477
- def connect
478
- close
479
-
480
- config = check_is_master(@host_to_try)
481
- if config
482
- if config['ismaster'] == 1 || config['ismaster'] == true
483
- @read_primary = true
484
- elsif @slave_ok
485
- @read_primary = false
486
- end
487
-
488
- set_primary(@host_to_try)
489
- end
490
-
491
- if connected?
492
- BSON::BSON_CODER.update_max_bson_size(self)
493
- else
494
- raise ConnectionFailure, "Failed to connect to a master node at #{@host_to_try[0]}:#{@host_to_try[1]}"
495
- end
496
- end
497
- alias :reconnect :connect
498
-
499
- def connecting?
500
- @nodes_to_try.length > 0
501
- end
502
-
503
- # It's possible that we defined connected as all nodes being connected???
504
- # NOTE: Do check if this needs to be more stringent.
505
- # Probably not since if any node raises a connection failure, all nodes will be closed.
506
- def connected?
507
- @primary_pool && @primary_pool.host && @primary_pool.port
508
- end
509
-
510
- # Determine if the connection is active. In a normal case the *server_info* operation
511
- # will be performed without issues, but if the connection was dropped by the server or
512
- # for some reason the sockets are unsynchronized, a ConnectionFailure will be raised and
513
- # the return will be false.
514
- #
515
- # @return [Boolean]
516
- def active?
517
- return false unless connected?
518
-
519
- ping
520
- true
521
-
522
- rescue ConnectionFailure
523
- false
524
- end
525
-
526
- # Determine whether we're reading from a primary node. If false,
527
- # this connection connects to a secondary node and @slave_ok is true.
528
- #
529
- # @return [Boolean]
530
- def read_primary?
531
- @read_primary
532
- end
533
- alias :primary? :read_primary?
534
-
535
- # Close the connection to the database.
536
- def close
537
- @primary_pool.close if @primary_pool
538
- @primary_pool = nil
539
- @primary = nil
540
- end
541
-
542
- # Returns the maximum BSON object size as returned by the core server.
543
- # Use the 4MB default when the server doesn't report this.
544
- #
545
- # @return [Integer]
546
- def max_bson_size
547
- config = self['admin'].command({:ismaster => 1})
548
- config['maxBsonObjectSize'] || Mongo::DEFAULT_MAX_BSON_SIZE
549
- end
550
-
551
- # Checkout a socket for reading (i.e., a secondary node).
552
- # Note: this is overridden in ReplSetConnection.
553
- def checkout_reader
554
- connect unless connected?
555
- @primary_pool.checkout
556
- end
557
-
558
- # Checkout a socket for writing (i.e., a primary node).
559
- # Note: this is overridden in ReplSetConnection.
560
- def checkout_writer
561
- connect unless connected?
562
- @primary_pool.checkout
563
- end
564
-
565
- # Checkin a socket used for reading.
566
- # Note: this is overridden in ReplSetConnection.
567
- def checkin_reader(socket)
568
- if @primary_pool
569
- @primary_pool.checkin(socket)
570
- end
571
- end
572
-
573
- # Checkin a socket used for writing.
574
- # Note: this is overridden in ReplSetConnection.
575
- def checkin_writer(socket)
576
- if @primary_pool
577
- @primary_pool.checkin(socket)
578
- end
579
- end
580
-
581
- # Execute the block and log the operation described by name
582
- # and payload.
583
- # TODO: Not sure if this should take a block.
584
- def instrument(name, payload = {}, &blk)
585
- res = yield
586
- log_operation(name, payload)
587
- res
588
- end
589
-
590
- protected
591
-
592
- # Generic initialization code.
593
- def setup(opts)
594
- # Authentication objects
595
- @auths = opts.fetch(:auths, [])
596
-
597
- # Lock for request ids.
598
- @id_lock = Mutex.new
599
-
600
- # Pool size and timeout.
601
- @pool_size = opts[:pool_size] || 1
602
- @timeout = opts[:timeout] || 5.0
603
-
604
- # Timeout on socket read operation.
605
- @op_timeout = opts[:op_timeout] || nil
606
-
607
- # Mutex for synchronizing pool access
608
- @connection_mutex = Mutex.new
609
-
610
- # Global safe option. This is false by default.
611
- @safe = opts[:safe] || false
612
-
613
- # Create a mutex when a new key, in this case a socket,
614
- # is added to the hash.
615
- @safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
616
-
617
- # Condition variable for signal and wait
618
- @queue = ConditionVariable.new
619
-
620
- # Connection pool for primay node
621
- @primary = nil
622
- @primary_pool = nil
623
-
624
- @logger = opts[:logger] || nil
625
-
626
- if @logger
627
- @logger.debug("MongoDB logging. Please note that logging negatively impacts performance " +
628
- "and should be disabled for high-performance production apps.")
629
- end
630
-
631
- should_connect = opts.fetch(:connect, true)
632
- connect if should_connect
633
- end
634
-
635
- ## Configuration helper methods
636
-
637
- # Returns a host-port pair.
638
- #
639
- # @return [Array]
640
- #
641
- # @private
642
- def format_pair(host, port)
643
- case host
644
- when String
645
- [host, port ? port.to_i : DEFAULT_PORT]
646
- when nil
647
- ['localhost', DEFAULT_PORT]
648
- end
649
- end
650
-
651
- ## Logging methods
652
-
653
- def log_operation(name, payload)
654
- return unless @logger
655
- msg = "#{payload[:database]}['#{payload[:collection]}'].#{name}("
656
- msg += payload.values_at(:selector, :document, :documents, :fields ).compact.map(&:inspect).join(', ') + ")"
657
- msg += ".skip(#{payload[:skip]})" if payload[:skip]
658
- msg += ".limit(#{payload[:limit]})" if payload[:limit]
659
- msg += ".sort(#{payload[:order]})" if payload[:order]
660
- @logger.debug "MONGODB #{msg}"
661
- end
662
-
663
- private
664
-
665
- ## Methods for establishing a connection:
666
-
667
- # If a ConnectionFailure is raised, this method will be called
668
- # to close the connection and reset connection values.
669
- # TODO: evaluate whether this method is actually necessary
670
- def reset_connection
671
- close
672
- end
673
-
674
- def check_is_master(node)
675
- begin
676
- host, port = *node
677
- socket = TCPSocket.new(host, port)
678
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
679
-
680
- config = self['admin'].command({:ismaster => 1}, :socket => socket)
681
- rescue OperationFailure, SocketError, SystemCallError, IOError => ex
682
- close
683
- ensure
684
- socket.close if socket
685
- end
686
-
687
- config
688
- end
689
-
690
- # Set the specified node as primary.
691
- def set_primary(node)
692
- host, port = *node
693
- @primary = [host, port]
694
- @primary_pool = Pool.new(self, host, port, :size => @pool_size, :timeout => @timeout)
695
- end
696
-
697
- ## Low-level connection methods.
698
-
699
- def receive(sock, expected_response)
700
- begin
701
- receive_header(sock, expected_response)
702
- number_received, cursor_id = receive_response_header(sock)
703
- read_documents(number_received, cursor_id, sock)
704
- rescue Mongo::ConnectionFailure => ex
705
- close
706
- raise ex
707
- end
708
- end
709
-
710
- def receive_header(sock, expected_response)
711
- header = receive_message_on_socket(16, sock)
712
- size, request_id, response_to = header.unpack('VVV')
713
- if expected_response != response_to
714
- raise Mongo::ConnectionFailure, "Expected response #{expected_response} but got #{response_to}"
715
- end
716
-
717
- unless header.size == STANDARD_HEADER_SIZE
718
- raise "Short read for DB response header: " +
719
- "expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
720
- end
721
- nil
722
- end
723
-
724
- def receive_response_header(sock)
725
- header_buf = receive_message_on_socket(RESPONSE_HEADER_SIZE, sock)
726
- if header_buf.length != RESPONSE_HEADER_SIZE
727
- raise "Short read for DB response header; " +
728
- "expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
729
- end
730
- flags, cursor_id_a, cursor_id_b, starting_from, number_remaining = header_buf.unpack('VVVVV')
731
- check_response_flags(flags)
732
- cursor_id = (cursor_id_b << 32) + cursor_id_a
733
- [number_remaining, cursor_id]
734
- end
735
-
736
- def check_response_flags(flags)
737
- if flags & Mongo::Constants::REPLY_CURSOR_NOT_FOUND != 0
738
- raise Mongo::OperationFailure, "Query response returned CURSOR_NOT_FOUND. " +
739
- "Either an invalid cursor was specified, or the cursor may have timed out on the server."
740
- elsif flags & Mongo::Constants::REPLY_QUERY_FAILURE != 0
741
- # Getting odd failures when a exception is raised here.
742
- end
743
- end
744
-
745
- def read_documents(number_received, cursor_id, sock)
746
- docs = []
747
- number_remaining = number_received
748
- while number_remaining > 0 do
749
- buf = receive_message_on_socket(4, sock)
750
- size = buf.unpack('V')[0]
751
- buf << receive_message_on_socket(size - 4, sock)
752
- number_remaining -= 1
753
- docs << BSON::BSON_CODER.deserialize(buf)
754
- end
755
- [docs, number_received, cursor_id]
756
- end
757
-
758
- # Constructs a getlasterror message. This method is used exclusively by
759
- # Connection#send_message_with_safe_check.
760
- #
761
- # Because it modifies message by reference, we don't need to return it.
762
- def build_last_error_message(message, db_name, opts)
763
- message.put_int(0)
764
- BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd")
765
- message.put_int(0)
766
- message.put_int(-1)
767
- cmd = BSON::OrderedHash.new
768
- cmd[:getlasterror] = 1
769
- if opts.is_a?(Hash)
770
- opts.assert_valid_keys(:w, :wtimeout, :fsync)
771
- cmd.merge!(opts)
772
- end
773
- message.put_binary(BSON::BSON_CODER.serialize(cmd, false).to_s)
774
- nil
775
- end
776
-
777
- # Prepares a message for transmission to MongoDB by
778
- # constructing a valid message header.
779
- #
780
- # Note: this method modifies message by reference.
781
- #
782
- # @return [Integer] the request id used in the header
783
- def add_message_headers(message, operation)
784
- headers = [
785
- # Message size.
786
- 16 + message.size,
787
-
788
- # Unique request id.
789
- request_id = get_request_id,
790
-
791
- # Response id.
792
- 0,
793
-
794
- # Opcode.
795
- operation
796
- ].pack('VVVV')
797
-
798
- message.prepend!(headers)
799
-
800
- request_id
801
- end
802
-
803
- # Increment and return the next available request id.
804
- #
805
- # return [Integer]
806
- def get_request_id
807
- request_id = ''
808
- @id_lock.synchronize do
809
- request_id = @@current_request_id += 1
810
- end
811
- request_id
812
- end
813
-
814
- # Low-level method for sending a message on a socket.
815
- # Requires a packed message and an available socket,
816
- #
817
- # @return [Integer] number of bytes sent
818
- def send_message_on_socket(packed_message, socket)
819
- begin
820
- total_bytes_sent = socket.send(packed_message, 0)
821
- if total_bytes_sent != packed_message.size
822
- packed_message.slice!(0, total_bytes_sent)
823
- while packed_message.size > 0
824
- byte_sent = socket.send(packed_message, 0)
825
- total_bytes_sent += byte_sent
826
- packed_message.slice!(0, byte_sent)
827
- end
828
- end
829
- total_bytes_sent
830
- rescue => ex
831
- close
832
- raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
833
- end
834
- end
835
-
836
- # Low-level method for receiving data from socket.
837
- # Requires length and an available socket.
838
- def receive_message_on_socket(length, socket)
839
- begin
840
- if @op_timeout
841
- message = nil
842
- Mongo::TimeoutHandler.timeout(@op_timeout, OperationTimeout) do
843
- message = receive_data(length, socket)
844
- end
845
- else
846
- message = receive_data(length, socket)
847
- end
848
- rescue => ex
849
- close
850
-
851
- if ex.class == OperationTimeout
852
- raise OperationTimeout, "Timed out waiting on socket read."
853
- else
854
- raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
855
- end
856
- end
857
- message
858
- end
859
-
860
- def receive_data(length, socket)
861
- message = new_binary_string
862
- socket.read(length, message)
863
- raise ConnectionFailure, "connection closed" unless message && message.length > 0
864
- if message.length < length
865
- chunk = new_binary_string
866
- while message.length < length
867
- socket.read(length - message.length, chunk)
868
- raise ConnectionFailure, "connection closed" unless chunk.length > 0
869
- message << chunk
870
- end
871
- end
872
- message
873
- end
874
-
875
- if defined?(Encoding)
876
- BINARY_ENCODING = Encoding.find("binary")
877
14
 
878
- def new_binary_string
879
- "".force_encoding(BINARY_ENCODING)
880
- end
881
- else
882
- def new_binary_string
883
- ""
884
- end
885
- end
886
- end
887
- end
15
+ require 'mongo/connection/socket'
16
+ require 'mongo/connection/node'
17
+ require 'mongo/connection/pool'
18
+ require 'mongo/connection/pool_manager'
19
+ require 'mongo/connection/sharding_pool_manager'