jonbell-mongo 1.3.1.2

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