mongo-lyon 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/LICENSE.txt +190 -0
  2. data/README.md +344 -0
  3. data/Rakefile +202 -0
  4. data/bin/mongo_console +34 -0
  5. data/docs/1.0_UPGRADE.md +21 -0
  6. data/docs/CREDITS.md +123 -0
  7. data/docs/FAQ.md +116 -0
  8. data/docs/GridFS.md +158 -0
  9. data/docs/HISTORY.md +225 -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 +77 -0
  14. data/lib/mongo/collection.rb +872 -0
  15. data/lib/mongo/connection.rb +875 -0
  16. data/lib/mongo/cursor.rb +449 -0
  17. data/lib/mongo/db.rb +607 -0
  18. data/lib/mongo/exceptions.rb +68 -0
  19. data/lib/mongo/gridfs/grid.rb +106 -0
  20. data/lib/mongo/gridfs/grid_ext.rb +57 -0
  21. data/lib/mongo/gridfs/grid_file_system.rb +145 -0
  22. data/lib/mongo/gridfs/grid_io.rb +394 -0
  23. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  24. data/lib/mongo/repl_set_connection.rb +342 -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 +185 -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 +181 -0
  31. data/lib/mongo/version.rb +3 -0
  32. data/mongo.gemspec +34 -0
  33. data/test/auxillary/1.4_features.rb +166 -0
  34. data/test/auxillary/authentication_test.rb +68 -0
  35. data/test/auxillary/autoreconnect_test.rb +41 -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 +614 -0
  41. data/test/bson/byte_buffer_test.rb +190 -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 +197 -0
  46. data/test/collection_test.rb +893 -0
  47. data/test/connection_test.rb +303 -0
  48. data/test/conversions_test.rb +120 -0
  49. data/test/cursor_fail_test.rb +75 -0
  50. data/test/cursor_message_test.rb +43 -0
  51. data/test/cursor_test.rb +457 -0
  52. data/test/db_api_test.rb +715 -0
  53. data/test/db_connection_test.rb +15 -0
  54. data/test/db_test.rb +287 -0
  55. data/test/grid_file_system_test.rb +244 -0
  56. data/test/grid_io_test.rb +120 -0
  57. data/test/grid_test.rb +200 -0
  58. data/test/load/thin/load.rb +24 -0
  59. data/test/load/unicorn/load.rb +23 -0
  60. data/test/replica_sets/connect_test.rb +86 -0
  61. data/test/replica_sets/connection_string_test.rb +32 -0
  62. data/test/replica_sets/count_test.rb +35 -0
  63. data/test/replica_sets/insert_test.rb +53 -0
  64. data/test/replica_sets/pooled_insert_test.rb +55 -0
  65. data/test/replica_sets/query_secondaries.rb +96 -0
  66. data/test/replica_sets/query_test.rb +51 -0
  67. data/test/replica_sets/replication_ack_test.rb +66 -0
  68. data/test/replica_sets/rs_test_helper.rb +27 -0
  69. data/test/safe_test.rb +68 -0
  70. data/test/support/hash_with_indifferent_access.rb +199 -0
  71. data/test/support/keys.rb +45 -0
  72. data/test/support_test.rb +19 -0
  73. data/test/test_helper.rb +83 -0
  74. data/test/threading/threading_with_large_pool_test.rb +90 -0
  75. data/test/threading_test.rb +87 -0
  76. data/test/tools/auth_repl_set_manager.rb +14 -0
  77. data/test/tools/repl_set_manager.rb +266 -0
  78. data/test/unit/collection_test.rb +130 -0
  79. data/test/unit/connection_test.rb +98 -0
  80. data/test/unit/cursor_test.rb +99 -0
  81. data/test/unit/db_test.rb +96 -0
  82. data/test/unit/grid_test.rb +49 -0
  83. data/test/unit/pool_test.rb +9 -0
  84. data/test/unit/repl_set_connection_test.rb +72 -0
  85. data/test/unit/safe_test.rb +125 -0
  86. data/test/uri_test.rb +91 -0
  87. metadata +202 -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,342 @@
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
+ #
50
+ # @example Connect to a replica set and provide two seed nodes. Note that the number of seed nodes does
51
+ # not have to be equal to the number of replica set members. The purpose of seed nodes is to permit
52
+ # the driver to find at least one replica set member even if a member is down.
53
+ # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001])
54
+ #
55
+ # @example Connect to a replica set providing two seed nodes and ensuring a connection to the replica set named 'prod':
56
+ # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001], :rs_name => 'prod')
57
+ #
58
+ # @example Connect to a replica set providing two seed nodes and allowing reads from a secondary node:
59
+ # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001], :read_secondary => true)
60
+ #
61
+ # @see http://api.mongodb.org/ruby/current/file.REPLICA_SETS.html Replica sets in Ruby
62
+ #
63
+ # @raise [ReplicaSetConnectionError] This is raised if a replica set name is specified and the
64
+ # driver fails to connect to a replica set with that name.
65
+ def initialize(*args)
66
+ if args.last.is_a?(Hash)
67
+ opts = args.pop
68
+ else
69
+ opts = {}
70
+ end
71
+
72
+ unless args.length > 0
73
+ raise MongoArgumentError, "A ReplSetConnection requires at least one node."
74
+ end
75
+
76
+ # Get seed nodes
77
+ @nodes = args
78
+
79
+ # Replica set name
80
+ @replica_set = opts[:rs_name]
81
+
82
+ # Cache the various node types when connecting to a replica set.
83
+ @secondaries = []
84
+ @arbiters = []
85
+
86
+ # Connection pools for each secondary node
87
+ @secondary_pools = []
88
+ @read_pool = nil
89
+
90
+ # Are we allowing reads from secondaries?
91
+ @read_secondary = opts.fetch(:read_secondary, false)
92
+
93
+ setup(opts)
94
+ end
95
+
96
+ # Create a new socket and attempt to connect to master.
97
+ # If successful, sets host and port to master and returns the socket.
98
+ #
99
+ # If connecting to a replica set, this method will replace the
100
+ # initially-provided seed list with any nodes known to the set.
101
+ #
102
+ # @raise [ConnectionFailure] if unable to connect to any host or port.
103
+ def connect
104
+ reset_connection
105
+ @nodes_to_try = @nodes.clone
106
+
107
+ while connecting?
108
+ node = @nodes_to_try.shift
109
+ config = check_is_master(node)
110
+
111
+ if is_primary?(config)
112
+ set_primary(node)
113
+ else
114
+ set_auxillary(node, config)
115
+ end
116
+ end
117
+
118
+ pick_secondary_for_read if @read_secondary
119
+
120
+ if connected?
121
+ BSON::BSON_CODER.update_max_bson_size(self)
122
+ else
123
+ if @secondary_pools.empty?
124
+ close # close any existing pools and sockets
125
+ raise ConnectionFailure, "Failed to connect any given host:port"
126
+ else
127
+ close # close any existing pools and sockets
128
+ raise ConnectionFailure, "Failed to connect to primary node."
129
+ end
130
+ end
131
+ end
132
+ alias :reconnect :connect
133
+
134
+ def connecting?
135
+ @nodes_to_try.length > 0
136
+ end
137
+
138
+ # Determine whether we're reading from a primary node. If false,
139
+ # this connection connects to a secondary node and @read_secondaries is true.
140
+ #
141
+ # @return [Boolean]
142
+ def read_primary?
143
+ !@read_pool
144
+ end
145
+ alias :primary? :read_primary?
146
+
147
+ # Close the connection to the database.
148
+ def close
149
+ super
150
+ @read_pool = nil
151
+ @secondary_pools.each do |pool|
152
+ pool.close
153
+ end
154
+ end
155
+
156
+ # If a ConnectionFailure is raised, this method will be called
157
+ # to close the connection and reset connection values.
158
+ # TODO: what's the point of this method?
159
+ def reset_connection
160
+ super
161
+ @secondaries = []
162
+ @secondary_pools = []
163
+ @arbiters = []
164
+ @nodes_tried = []
165
+ @nodes_to_try = []
166
+ end
167
+
168
+ # Is it okay to connect to a slave?
169
+ #
170
+ # @return [Boolean]
171
+ def slave_ok?
172
+ @read_secondary || @slave_ok
173
+ end
174
+
175
+ def authenticate_pools
176
+ super
177
+ @secondary_pools.each do |pool|
178
+ pool.authenticate_existing
179
+ end
180
+ end
181
+
182
+ def logout_pools(db)
183
+ super
184
+ @secondary_pools.each do |pool|
185
+ pool.logout_existing(db)
186
+ end
187
+ end
188
+
189
+ private
190
+
191
+ def check_is_master(node)
192
+ begin
193
+ if node[1]
194
+ host, port = *node
195
+ socket = TCPSocket.new(host, port)
196
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
197
+ else
198
+ socket = UNIXSocket.new(node[0])
199
+ end
200
+
201
+ config = self['admin'].command({:ismaster => 1}, :socket => socket)
202
+
203
+ check_set_name(config, socket)
204
+ rescue OperationFailure, SocketError, SystemCallError, IOError => ex
205
+ # It's necessary to rescue here. The #connect method will keep trying
206
+ # until it has no more nodes to try and raise a ConnectionFailure if
207
+ # it can't connect to a primary.
208
+ ensure
209
+ socket.close if socket
210
+ @nodes_tried << node
211
+
212
+ if config
213
+ nodes = []
214
+ nodes += config['hosts'] if config['hosts']
215
+ nodes += config['arbiters'] if config['arbiters']
216
+ nodes += config['passives'] if config['passives']
217
+ update_node_list(nodes)
218
+
219
+ if config['msg'] && @logger
220
+ @logger.warn("MONGODB #{config['msg']}")
221
+ end
222
+ end
223
+ end
224
+
225
+ config
226
+ end
227
+
228
+ # Primary, when connecting to a replica can, can only be a true primary node.
229
+ # (And not a slave, which is possible when connecting with the standard
230
+ # Connection class.
231
+ def is_primary?(config)
232
+ config && (config['ismaster'] == 1 || config['ismaster'] == true)
233
+ end
234
+
235
+ # Pick a node randomly from the set of possible secondaries.
236
+ def pick_secondary_for_read
237
+ if (size = @secondary_pools.size) > 0
238
+ @read_pool = @secondary_pools[rand(size)]
239
+ end
240
+ end
241
+
242
+ # Make sure that we're connected to the expected replica set.
243
+ def check_set_name(config, socket)
244
+ if @replica_set
245
+ config = self['admin'].command({:replSetGetStatus => 1},
246
+ :socket => socket, :check_response => false)
247
+
248
+ if !Mongo::Support.ok?(config)
249
+ raise ReplicaSetConnectionError, config['errmsg']
250
+ elsif config['set'] != @replica_set
251
+ raise ReplicaSetConnectionError,
252
+ "Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set}'"
253
+ end
254
+ end
255
+ end
256
+
257
+ # Determines what kind of node we have and caches its host
258
+ # and port so that users can easily connect manually.
259
+ def set_auxillary(node, config)
260
+ if config
261
+ if config['secondary']
262
+ @secondaries << node unless @secondaries.include?(node)
263
+ if node[1]
264
+ host, port = *node
265
+ @secondary_pools << Pool.new(self, host, port, :size => @pool_size, :timeout => @timeout)
266
+ else
267
+ unix_socket_path = *node
268
+ @secondary_pools << Pool.new(self, unix_socket_path, nil, :size => @pool_size, :timeout => @timeout)
269
+ end
270
+ elsif config['arbiterOnly']
271
+ @arbiters << node unless @arbiters.include?(node)
272
+ end
273
+ end
274
+ end
275
+
276
+ # Update the list of known nodes. Only applies to replica sets,
277
+ # where the response to the ismaster command will return a list
278
+ # of known hosts.
279
+ #
280
+ # @param hosts [Array] a list of hosts, specified as string-encoded
281
+ # host-port values. Example: ["myserver-1.org:27017", "myserver-1.org:27017"]
282
+ #
283
+ # @return [Array] the updated list of nodes
284
+ def update_node_list(hosts)
285
+ new_nodes = hosts.map do |host|
286
+ if !host.respond_to?(:split)
287
+ warn "Could not parse host #{host.inspect}."
288
+ next
289
+ end
290
+
291
+ host, port = host.split(':')
292
+ unless port || File.exists?(host)
293
+ port = Connection::DEFAULT_PORT
294
+ end
295
+ if port
296
+ [host, port.to_i]
297
+ else
298
+ [host]
299
+ end
300
+ end
301
+
302
+ # Replace the list of seed nodes with the canonical list.
303
+ @nodes = new_nodes.clone
304
+
305
+ @nodes_to_try = new_nodes - @nodes_tried
306
+ end
307
+
308
+ # Checkout a socket for reading (i.e., a secondary node).
309
+ def checkout_reader
310
+ connect unless connected?
311
+
312
+ if @read_pool
313
+ @read_pool.checkout
314
+ else
315
+ checkout_writer
316
+ end
317
+ end
318
+
319
+ # Checkout a socket for writing (i.e., a primary node).
320
+ def checkout_writer
321
+ connect unless connected?
322
+
323
+ @primary_pool.checkout
324
+ end
325
+
326
+ # Checkin a socket used for reading.
327
+ def checkin_reader(socket)
328
+ if @read_pool
329
+ @read_pool.checkin(socket)
330
+ else
331
+ checkin_writer(socket)
332
+ end
333
+ end
334
+
335
+ # Checkin a socket used for writing.
336
+ def checkin_writer(socket)
337
+ if @primary_pool
338
+ @primary_pool.checkin(socket)
339
+ end
340
+ end
341
+ end
342
+ 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