mongo-lyon 1.2.4

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 (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