jonbell-mongo 1.3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/LICENSE.txt +190 -0
  2. data/README.md +333 -0
  3. data/Rakefile +215 -0
  4. data/bin/mongo_console +21 -0
  5. data/docs/CREDITS.md +123 -0
  6. data/docs/FAQ.md +116 -0
  7. data/docs/GridFS.md +158 -0
  8. data/docs/HISTORY.md +263 -0
  9. data/docs/RELEASES.md +33 -0
  10. data/docs/REPLICA_SETS.md +72 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo.rb +97 -0
  14. data/lib/mongo/collection.rb +895 -0
  15. data/lib/mongo/connection.rb +926 -0
  16. data/lib/mongo/cursor.rb +474 -0
  17. data/lib/mongo/db.rb +617 -0
  18. data/lib/mongo/exceptions.rb +71 -0
  19. data/lib/mongo/gridfs/grid.rb +107 -0
  20. data/lib/mongo/gridfs/grid_ext.rb +57 -0
  21. data/lib/mongo/gridfs/grid_file_system.rb +146 -0
  22. data/lib/mongo/gridfs/grid_io.rb +485 -0
  23. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  24. data/lib/mongo/repl_set_connection.rb +356 -0
  25. data/lib/mongo/util/conversions.rb +89 -0
  26. data/lib/mongo/util/core_ext.rb +60 -0
  27. data/lib/mongo/util/pool.rb +177 -0
  28. data/lib/mongo/util/server_version.rb +71 -0
  29. data/lib/mongo/util/support.rb +82 -0
  30. data/lib/mongo/util/uri_parser.rb +185 -0
  31. data/mongo.gemspec +34 -0
  32. data/test/auxillary/1.4_features.rb +166 -0
  33. data/test/auxillary/authentication_test.rb +68 -0
  34. data/test/auxillary/autoreconnect_test.rb +41 -0
  35. data/test/auxillary/fork_test.rb +30 -0
  36. data/test/auxillary/repl_set_auth_test.rb +58 -0
  37. data/test/auxillary/slave_connection_test.rb +36 -0
  38. data/test/auxillary/threaded_authentication_test.rb +101 -0
  39. data/test/bson/binary_test.rb +15 -0
  40. data/test/bson/bson_test.rb +654 -0
  41. data/test/bson/byte_buffer_test.rb +208 -0
  42. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  43. data/test/bson/json_test.rb +17 -0
  44. data/test/bson/object_id_test.rb +154 -0
  45. data/test/bson/ordered_hash_test.rb +210 -0
  46. data/test/bson/timestamp_test.rb +24 -0
  47. data/test/collection_test.rb +910 -0
  48. data/test/connection_test.rb +324 -0
  49. data/test/conversions_test.rb +119 -0
  50. data/test/cursor_fail_test.rb +75 -0
  51. data/test/cursor_message_test.rb +43 -0
  52. data/test/cursor_test.rb +483 -0
  53. data/test/db_api_test.rb +738 -0
  54. data/test/db_connection_test.rb +15 -0
  55. data/test/db_test.rb +315 -0
  56. data/test/grid_file_system_test.rb +259 -0
  57. data/test/grid_io_test.rb +209 -0
  58. data/test/grid_test.rb +258 -0
  59. data/test/load/thin/load.rb +24 -0
  60. data/test/load/unicorn/load.rb +23 -0
  61. data/test/replica_sets/connect_test.rb +112 -0
  62. data/test/replica_sets/connection_string_test.rb +32 -0
  63. data/test/replica_sets/count_test.rb +35 -0
  64. data/test/replica_sets/insert_test.rb +53 -0
  65. data/test/replica_sets/pooled_insert_test.rb +55 -0
  66. data/test/replica_sets/query_secondaries.rb +108 -0
  67. data/test/replica_sets/query_test.rb +51 -0
  68. data/test/replica_sets/replication_ack_test.rb +66 -0
  69. data/test/replica_sets/rs_test_helper.rb +27 -0
  70. data/test/safe_test.rb +68 -0
  71. data/test/support/hash_with_indifferent_access.rb +186 -0
  72. data/test/support/keys.rb +45 -0
  73. data/test/support_test.rb +18 -0
  74. data/test/test_helper.rb +102 -0
  75. data/test/threading/threading_with_large_pool_test.rb +90 -0
  76. data/test/threading_test.rb +87 -0
  77. data/test/tools/auth_repl_set_manager.rb +14 -0
  78. data/test/tools/repl_set_manager.rb +266 -0
  79. data/test/unit/collection_test.rb +130 -0
  80. data/test/unit/connection_test.rb +85 -0
  81. data/test/unit/cursor_test.rb +109 -0
  82. data/test/unit/db_test.rb +94 -0
  83. data/test/unit/grid_test.rb +49 -0
  84. data/test/unit/pool_test.rb +9 -0
  85. data/test/unit/repl_set_connection_test.rb +59 -0
  86. data/test/unit/safe_test.rb +125 -0
  87. data/test/uri_test.rb +91 -0
  88. metadata +224 -0
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+
19
+ module Mongo
20
+ class GridIO
21
+
22
+ # This fixes a comparson issue in JRuby 1.9
23
+ def get_md5
24
+ md5_command = BSON::OrderedHash.new
25
+ md5_command['filemd5'] = @files_id
26
+ md5_command['root'] = @fs_name
27
+ @server_md5 = @files.db.command(md5_command)['md5']
28
+ if @safe
29
+ @client_md5 = @local_md5.hexdigest
30
+ if @local_md5.to_s != @server_md5.to_s
31
+ raise GridMD5Failure, "File on server failed MD5 check"
32
+ end
33
+ else
34
+ @server_md5
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,356 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+
19
+ module Mongo
20
+
21
+ # Instantiates and manages connections to a MongoDB replica set.
22
+ class ReplSetConnection < Connection
23
+ attr_reader :nodes, :secondaries, :arbiters, :read_pool, :secondary_pools
24
+
25
+ # Create a connection to a MongoDB replica set.
26
+ #
27
+ # Once connected to a replica set, you can find out which nodes are primary, secondary, and
28
+ # arbiters with the corresponding accessors: Connection#primary, Connection#secondaries, and
29
+ # Connection#arbiters. This is useful if your application needs to connect manually to nodes other
30
+ # than the primary.
31
+ #
32
+ # @param [Array] args A list of host-port pairs to be used as seed nodes followed by a
33
+ # hash containing any options. See the examples below for exactly how to use the constructor.
34
+ #
35
+ # @option options [String] :rs_name (nil) The name of the replica set to connect to. You
36
+ # can use this option to verify that you're connecting to the right replica set.
37
+ # @option options [Boolean, Hash] :safe (false) Set the default safe-mode options
38
+ # propogated to DB objects instantiated off of this Connection. This
39
+ # default can be overridden upon instantiation of any DB by explicity setting a :safe value
40
+ # on initialization.
41
+ # @option options [Boolean] :read_secondary(false) If true, a random secondary node will be chosen,
42
+ # and all reads will be directed to that node.
43
+ # @option options [Logger, #debug] :logger (nil) Logger instance to receive driver operation log.
44
+ # @option options [Integer] :pool_size (1) The maximum number of socket connections allowed per
45
+ # connection pool. Note: this setting is relevant only for multi-threaded applications.
46
+ # @option options [Float] :timeout (5.0) When all of the connections a pool are checked out,
47
+ # this is the number of seconds to wait for a new connection to be released before throwing an exception.
48
+ # Note: this setting is relevant only for multi-threaded applications.
49
+ # @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
50
+ # Disabled by default.
51
+ # @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
52
+ # connection attempt.
53
+ #
54
+ # @example Connect to a replica set and provide two seed nodes. Note that the number of seed nodes does
55
+ # not have to be equal to the number of replica set members. The purpose of seed nodes is to permit
56
+ # the driver to find at least one replica set member even if a member is down.
57
+ # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001])
58
+ #
59
+ # @example Connect to a replica set providing two seed nodes and ensuring a connection to the replica set named 'prod':
60
+ # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001], :rs_name => 'prod')
61
+ #
62
+ # @example Connect to a replica set providing two seed nodes and allowing reads from a secondary node:
63
+ # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001], :read_secondary => true)
64
+ #
65
+ # @see http://api.mongodb.org/ruby/current/file.REPLICA_SETS.html Replica sets in Ruby
66
+ #
67
+ # @raise [ReplicaSetConnectionError] This is raised if a replica set name is specified and the
68
+ # driver fails to connect to a replica set with that name.
69
+ def initialize(*args)
70
+ if args.last.is_a?(Hash)
71
+ opts = args.pop
72
+ else
73
+ opts = {}
74
+ end
75
+
76
+ unless args.length > 0
77
+ raise MongoArgumentError, "A ReplSetConnection requires at least one node."
78
+ end
79
+
80
+ # Get seed nodes
81
+ @nodes = args
82
+
83
+ # Replica set name
84
+ @replica_set = opts[:rs_name]
85
+
86
+ # Cache the various node types when connecting to a replica set.
87
+ @secondaries = []
88
+ @arbiters = []
89
+
90
+ # Connection pools for each secondary node
91
+ @secondary_pools = []
92
+ @read_pool = nil
93
+
94
+ # Are we allowing reads from secondaries?
95
+ @read_secondary = opts.fetch(:read_secondary, false)
96
+ @slave_okay = false
97
+
98
+ setup(opts)
99
+ end
100
+
101
+ # Create a new socket and attempt to connect to master.
102
+ # If successful, sets host and port to master and returns the socket.
103
+ #
104
+ # If connecting to a replica set, this method will replace the
105
+ # initially-provided seed list with any nodes known to the set.
106
+ #
107
+ # @raise [ConnectionFailure] if unable to connect to any host or port.
108
+ def connect
109
+ close
110
+ @nodes_to_try = @nodes.clone
111
+
112
+ while connecting?
113
+ node = @nodes_to_try.shift
114
+ config = check_is_master(node)
115
+
116
+ if is_primary?(config)
117
+ set_primary(node)
118
+ else
119
+ set_auxillary(node, config)
120
+ end
121
+ end
122
+
123
+ pick_secondary_for_read if @read_secondary
124
+
125
+ if connected?
126
+ BSON::BSON_CODER.update_max_bson_size(self)
127
+ else
128
+ if @secondary_pools.empty?
129
+ close # close any existing pools and sockets
130
+ raise ConnectionFailure, "Failed to connect any given host:port"
131
+ else
132
+ close # close any existing pools and sockets
133
+ raise ConnectionFailure, "Failed to connect to primary node."
134
+ end
135
+ end
136
+ end
137
+ alias :reconnect :connect
138
+
139
+ def connecting?
140
+ @nodes_to_try.length > 0
141
+ end
142
+
143
+ # The replica set primary's host name.
144
+ #
145
+ # @return [String]
146
+ def host
147
+ super
148
+ end
149
+
150
+ # The replica set primary's port.
151
+ #
152
+ # @return [Integer]
153
+ def port
154
+ super
155
+ end
156
+
157
+ # Determine whether we're reading from a primary node. If false,
158
+ # this connection connects to a secondary node and @read_secondaries is true.
159
+ #
160
+ # @return [Boolean]
161
+ def read_primary?
162
+ !@read_pool
163
+ end
164
+ alias :primary? :read_primary?
165
+
166
+ # Close the connection to the database.
167
+ def close
168
+ super
169
+ @read_pool = nil
170
+ @secondary_pools.each do |pool|
171
+ pool.close
172
+ end
173
+ @secondaries = []
174
+ @secondary_pools = []
175
+ @arbiters = []
176
+ @nodes_tried = []
177
+ @nodes_to_try = []
178
+ end
179
+
180
+ # If a ConnectionFailure is raised, this method will be called
181
+ # to close the connection and reset connection values.
182
+ # @deprecated
183
+ def reset_connection
184
+ close
185
+ warn "ReplSetConnection#reset_connection is now deprecated. " +
186
+ "Use ReplSetConnection#close instead."
187
+ end
188
+
189
+ # Is it okay to connect to a slave?
190
+ #
191
+ # @return [Boolean]
192
+ def slave_ok?
193
+ @read_secondary || @slave_ok
194
+ end
195
+
196
+ def authenticate_pools
197
+ super
198
+ @secondary_pools.each do |pool|
199
+ pool.authenticate_existing
200
+ end
201
+ end
202
+
203
+ def logout_pools(db)
204
+ super
205
+ @secondary_pools.each do |pool|
206
+ pool.logout_existing(db)
207
+ end
208
+ end
209
+
210
+ private
211
+
212
+ def check_is_master(node)
213
+ begin
214
+ host, port = *node
215
+ socket = nil
216
+
217
+ if @connect_timeout
218
+ Mongo::TimeoutHandler.timeout(@connect_timeout, OperationTimeout) do
219
+ socket = TCPSocket.new(host, port)
220
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
221
+ end
222
+ else
223
+ socket = TCPSocket.new(host, port)
224
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
225
+ end
226
+
227
+ config = self['admin'].command({:ismaster => 1}, :socket => socket)
228
+
229
+ check_set_name(config, socket)
230
+ rescue OperationFailure, SocketError, SystemCallError, IOError, OperationTimeout => ex
231
+ # It's necessary to rescue here. The #connect method will keep trying
232
+ # until it has no more nodes to try and raise a ConnectionFailure if
233
+ # it can't connect to a primary.
234
+ ensure
235
+ socket.close if socket
236
+ @nodes_tried << node
237
+
238
+ if config
239
+ nodes = []
240
+ nodes += config['hosts'] if config['hosts']
241
+ nodes += config['arbiters'] if config['arbiters']
242
+ nodes += config['passives'] if config['passives']
243
+ update_node_list(nodes)
244
+
245
+ if config['msg'] && @logger
246
+ @logger.warn("MONGODB #{config['msg']}")
247
+ end
248
+ end
249
+ end
250
+
251
+ config
252
+ end
253
+
254
+ # Primary, when connecting to a replica can, can only be a true primary node.
255
+ # (And not a slave, which is possible when connecting with the standard
256
+ # Connection class.
257
+ def is_primary?(config)
258
+ config && (config['ismaster'] == 1 || config['ismaster'] == true)
259
+ end
260
+
261
+ # Pick a node randomly from the set of possible secondaries.
262
+ def pick_secondary_for_read
263
+ if (size = @secondary_pools.size) > 0
264
+ @read_pool = @secondary_pools[rand(size)]
265
+ end
266
+ end
267
+
268
+ # Make sure that we're connected to the expected replica set.
269
+ def check_set_name(config, socket)
270
+ if @replica_set
271
+ config = self['admin'].command({:replSetGetStatus => 1},
272
+ :socket => socket, :check_response => false)
273
+
274
+ if !Mongo::Support.ok?(config)
275
+ raise ReplicaSetConnectionError, config['errmsg']
276
+ elsif config['set'] != @replica_set
277
+ raise ReplicaSetConnectionError,
278
+ "Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set}'"
279
+ end
280
+ end
281
+ end
282
+
283
+ # Determines what kind of node we have and caches its host
284
+ # and port so that users can easily connect manually.
285
+ def set_auxillary(node, config)
286
+ if config
287
+ if config['secondary']
288
+ host, port = *node
289
+ @secondaries << node unless @secondaries.include?(node)
290
+ @secondary_pools << Pool.new(self, host, port, :size => @pool_size, :timeout => @timeout)
291
+ elsif config['arbiterOnly']
292
+ @arbiters << node unless @arbiters.include?(node)
293
+ end
294
+ end
295
+ end
296
+
297
+ # Update the list of known nodes. Only applies to replica sets,
298
+ # where the response to the ismaster command will return a list
299
+ # of known hosts.
300
+ #
301
+ # @param hosts [Array] a list of hosts, specified as string-encoded
302
+ # host-port values. Example: ["myserver-1.org:27017", "myserver-1.org:27017"]
303
+ #
304
+ # @return [Array] the updated list of nodes
305
+ def update_node_list(hosts)
306
+ new_nodes = hosts.map do |host|
307
+ if !host.respond_to?(:split)
308
+ warn "Could not parse host #{host.inspect}."
309
+ next
310
+ end
311
+
312
+ host, port = host.split(':')
313
+ [host, port ? port.to_i : Connection::DEFAULT_PORT]
314
+ end
315
+
316
+ # Replace the list of seed nodes with the canonical list.
317
+ @nodes = new_nodes.clone
318
+
319
+ @nodes_to_try = new_nodes - @nodes_tried
320
+ end
321
+
322
+ # Checkout a socket for reading (i.e., a secondary node).
323
+ def checkout_reader
324
+ connect unless connected?
325
+
326
+ if @read_pool
327
+ @read_pool.checkout
328
+ else
329
+ checkout_writer
330
+ end
331
+ end
332
+
333
+ # Checkout a socket for writing (i.e., a primary node).
334
+ def checkout_writer
335
+ connect unless connected?
336
+
337
+ @primary_pool.checkout
338
+ end
339
+
340
+ # Checkin a socket used for reading.
341
+ def checkin_reader(socket)
342
+ if @read_pool
343
+ @read_pool.checkin(socket)
344
+ else
345
+ checkin_writer(socket)
346
+ end
347
+ end
348
+
349
+ # Checkin a socket used for writing.
350
+ def checkin_writer(socket)
351
+ if @primary_pool
352
+ @primary_pool.checkin(socket)
353
+ end
354
+ end
355
+ end
356
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+ module Mongo #:nodoc:
19
+
20
+ # Utility module to include when needing to convert certain types of
21
+ # objects to mongo-friendly parameters.
22
+ module Conversions
23
+
24
+ ASCENDING_CONVERSION = ["ascending", "asc", "1"]
25
+ DESCENDING_CONVERSION = ["descending", "desc", "-1"]
26
+
27
+ # Converts the supplied +Array+ to a +Hash+ to pass to mongo as
28
+ # sorting parameters. The returned +Hash+ will vary depending
29
+ # on whether the passed +Array+ is one or two dimensional.
30
+ #
31
+ # Example:
32
+ #
33
+ # <tt>array_as_sort_parameters([["field1", :asc], ["field2", :desc]])</tt> =>
34
+ # <tt>{ "field1" => 1, "field2" => -1}</tt>
35
+ def array_as_sort_parameters(value)
36
+ order_by = BSON::OrderedHash.new
37
+ if value.first.is_a? Array
38
+ value.each do |param|
39
+ if (param.class.name == "String")
40
+ order_by[param] = 1
41
+ else
42
+ order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
43
+ end
44
+ end
45
+ elsif !value.empty?
46
+ if order_by.size == 1
47
+ order_by[value.first] = 1
48
+ else
49
+ order_by[value.first] = sort_value(value[1])
50
+ end
51
+ end
52
+ order_by
53
+ end
54
+
55
+ # Converts the supplied +String+ or +Symbol+ to a +Hash+ to pass to mongo as
56
+ # a sorting parameter with ascending order. If the +String+
57
+ # is empty then an empty +Hash+ will be returned.
58
+ #
59
+ # Example:
60
+ #
61
+ # *DEPRECATED
62
+ #
63
+ # <tt>string_as_sort_parameters("field")</tt> => <tt>{ "field" => 1 }</tt>
64
+ # <tt>string_as_sort_parameters("")</tt> => <tt>{}</tt>
65
+ def string_as_sort_parameters(value)
66
+ return {} if (str = value.to_s).empty?
67
+ { str => 1 }
68
+ end
69
+
70
+ # Converts the +String+, +Symbol+, or +Integer+ to the
71
+ # corresponding sort value in MongoDB.
72
+ #
73
+ # Valid conversions (case-insensitive):
74
+ #
75
+ # <tt>ascending, asc, :ascending, :asc, 1</tt> => <tt>1</tt>
76
+ # <tt>descending, desc, :descending, :desc, -1</tt> => <tt>-1</tt>
77
+ #
78
+ # If the value is invalid then an error will be raised.
79
+ def sort_value(value)
80
+ val = value.to_s.downcase
81
+ return 1 if ASCENDING_CONVERSION.include?(val)
82
+ return -1 if DESCENDING_CONVERSION.include?(val)
83
+ raise InvalidSortValueError.new(
84
+ "#{self} was supplied as a sort direction when acceptable values are: " +
85
+ "Mongo::ASCENDING, 'ascending', 'asc', :ascending, :asc, 1, Mongo::DESCENDING, " +
86
+ "'descending', 'desc', :descending, :desc, -1.")
87
+ end
88
+ end
89
+ end