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
@@ -0,0 +1,535 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+
17
+ # Instantiates and manages connections to a MongoDB replica set.
18
+ class MongoReplicaSetClient < MongoClient
19
+ include ReadPreference
20
+ include ThreadLocalVariableManager
21
+
22
+ REPL_SET_OPTS = [
23
+ :refresh_mode,
24
+ :refresh_interval,
25
+ :read_secondary,
26
+ :rs_name,
27
+ :name
28
+ ]
29
+
30
+ attr_reader :replica_set_name,
31
+ :seeds,
32
+ :refresh_interval,
33
+ :refresh_mode,
34
+ :refresh_version,
35
+ :manager
36
+
37
+ # Create a connection to a MongoDB replica set.
38
+ #
39
+ # If no args are provided, it will check <code>ENV["MONGODB_URI"]</code>.
40
+ #
41
+ # Once connected to a replica set, you can find out which nodes are primary, secondary, and
42
+ # arbiters with the corresponding accessors: MongoClient#primary, MongoClient#secondaries, and
43
+ # MongoClient#arbiters. This is useful if your application needs to connect manually to nodes other
44
+ # than the primary.
45
+ #
46
+ # @overload initialize(seeds=ENV["MONGODB_URI"], opts={})
47
+ # @param [Array<String>, Array<Array(String, Integer)>] seeds
48
+ #
49
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
50
+ # should be acknowledged.
51
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout.
52
+ # @option opts [Boolean] :j (false) If true, block until write operations have been committed
53
+ # to the journal. Cannot be used in combination with 'fsync'. Prior to MongoDB 2.6 this option was
54
+ # ignored if the server was running without journaling. Starting with MongoDB 2.6, write operations will
55
+ # fail with an exception if this option is used when the server is running without journaling.
56
+ # @option opts [Boolean] :fsync (false) If true, and the server is running without journaling, blocks until
57
+ # the server has synced all data files to disk. If the server is running with journaling, this acts the same as
58
+ # the 'j' option, blocking until write operations have been committed to the journal.
59
+ # Cannot be used in combination with 'j'.
60
+ #
61
+ # Notes about write concern options:
62
+ # Write concern options are propagated to objects instantiated from this MongoReplicaSetClient.
63
+ # These defaults can be overridden upon instantiation of any object by explicitly setting an options hash
64
+ # on initialization.
65
+ # @option opts [:primary, :primary_preferred, :secondary, :secondary_preferred, :nearest] :read (:primary)
66
+ # A "read preference" determines the candidate replica set members to which a query or command can be sent.
67
+ # [:primary]
68
+ # * Read from primary only.
69
+ # * Cannot be combined with tags.
70
+ # [:primary_preferred]
71
+ # * Read from primary if available, otherwise read from a secondary.
72
+ # [:secondary]
73
+ # * Read from secondary if available.
74
+ # [:secondary_preferred]
75
+ # * Read from a secondary if available, otherwise read from the primary.
76
+ # [:nearest]
77
+ # * Read from any member.
78
+ # @option opts [Array<Hash{ String, Symbol => Tag Value }>] :tag_sets ([])
79
+ # Read from replica-set members with these tags.
80
+ # @option opts [Integer] :secondary_acceptable_latency_ms (15) The acceptable
81
+ # nearest available member for a member to be considered "near".
82
+ # @option opts [Logger] :logger (nil) Logger instance to receive driver operation log.
83
+ # @option opts [Integer] :pool_size (1) The maximum number of socket connections allowed per
84
+ # connection pool. Note: this setting is relevant only for multi-threaded applications.
85
+ # @option opts [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
86
+ # this is the number of seconds to wait for a new connection to be released before throwing an exception.
87
+ # Note: this setting is relevant only for multi-threaded applications.
88
+ # @option opts [Float] :op_timeout (DEFAULT_OP_TIMEOUT) The number of seconds to wait for a read operation to time out.
89
+ # Set to DEFAULT_OP_TIMEOUT (20) by default. A value of nil may be specified explicitly.
90
+ # @option opts [Float] :connect_timeout (30) The number of seconds to wait before timing out a
91
+ # connection attempt.
92
+ # @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
93
+ # @option opts [String] :ssl_cert (nil) The certificate file used to identify the local connection against MongoDB.
94
+ # @option opts [String] :ssl_key (nil) The private keyfile used to identify the local connection against MongoDB.
95
+ # Note that even if the key is stored in the same file as the certificate, both need to be explicitly specified.
96
+ # @option opts [String] :ssl_key_pass_phrase (nil) A passphrase for the private key.
97
+ # @option opts [Boolean] :ssl_verify (nil) Specifies whether or not peer certification validation should occur.
98
+ # @option opts [String] :ssl_ca_cert (nil) The ca_certs file contains a set of concatenated "certification authority"
99
+ # certificates, which are used to validate certificates passed from the other end of the connection.
100
+ # Required for :ssl_verify.
101
+ # @option opts [Boolean] :refresh_mode (false) Set this to :sync to periodically update the
102
+ # state of the connection every :refresh_interval seconds. Replica set connection failures
103
+ # will always trigger a complete refresh. This option is useful when you want to add new nodes
104
+ # or remove replica set nodes not currently in use by the driver.
105
+ # @option opts [Integer] :refresh_interval (90) If :refresh_mode is enabled, this is the number of seconds
106
+ # between calls to check the replica set's state.
107
+ # @note the number of seed nodes does not have to be equal to the number of replica set members.
108
+ # The purpose of seed nodes is to permit the driver to find at least one replica set member even if a member is down.
109
+ #
110
+ # @example Connect to a replica set and provide two seed nodes.
111
+ # MongoReplicaSetClient.new(['localhost:30000', 'localhost:30001'])
112
+ #
113
+ # @example Connect to a replica set providing two seed nodes and ensuring a connection to the replica set named 'prod':
114
+ # MongoReplicaSetClient.new(['localhost:30000', 'localhost:30001'], :name => 'prod')
115
+ #
116
+ # @example Connect to a replica set providing two seed nodes and allowing reads from a secondary node:
117
+ # MongoReplicaSetClient.new(['localhost:30000', 'localhost:30001'], :read => :secondary)
118
+ #
119
+ # @see http://api.mongodb.org/ruby/current/file.REPLICA_SETS.html Replica sets in Ruby
120
+ #
121
+ # @raise [MongoArgumentError] This is raised for usage errors.
122
+ #
123
+ # @raise [ConnectionFailure] This is raised for the various connection failures.
124
+ def initialize(*args)
125
+ opts = args.last.is_a?(Hash) ? args.pop : {}
126
+ nodes = args.shift || []
127
+
128
+ raise MongoArgumentError, "Too many arguments" unless args.empty?
129
+
130
+ # This is temporary until support for the old format is dropped
131
+ @seeds = nodes.collect do |node|
132
+ if node.is_a?(Array)
133
+ warn "Initiating a MongoReplicaSetClient with seeds passed as individual [host, port] array arguments is deprecated."
134
+ warn "Please specify hosts as an array of 'host:port' strings; the old format will be removed in v2.0"
135
+ node
136
+ elsif node.is_a?(String)
137
+ Support.normalize_seeds(node)
138
+ else
139
+ raise MongoArgumentError "Bad seed format!"
140
+ end
141
+ end
142
+
143
+ if @seeds.empty? && ENV.has_key?('MONGODB_URI')
144
+ parser = URIParser.new ENV['MONGODB_URI']
145
+ if parser.direct?
146
+ raise MongoArgumentError,
147
+ "ENV['MONGODB_URI'] implies a direct connection."
148
+ end
149
+ opts = parser.connection_options.merge! opts
150
+ @seeds = parser.nodes
151
+ end
152
+
153
+ if @seeds.length.zero?
154
+ raise MongoArgumentError, "A MongoReplicaSetClient requires at least one seed node."
155
+ end
156
+
157
+ @seeds.freeze
158
+
159
+ # Refresh
160
+ @last_refresh = Time.now
161
+ @refresh_version = 0
162
+
163
+ # No connection manager by default.
164
+ @manager = nil
165
+
166
+ # Lock for request ids.
167
+ @id_lock = Mutex.new
168
+
169
+ @connected = false
170
+
171
+ @connect_mutex = Mutex.new
172
+
173
+ @mongos = false
174
+
175
+ check_opts(opts)
176
+ setup(opts.dup)
177
+ end
178
+
179
+ def valid_opts
180
+ super + REPL_SET_OPTS - CLIENT_ONLY_OPTS
181
+ end
182
+
183
+ def inspect
184
+ "<Mongo::MongoReplicaSetClient:0x#{self.object_id.to_s(16)} @seeds=#{@seeds.inspect} " +
185
+ "@connected=#{@connected}>"
186
+ end
187
+
188
+ # Initiate a connection to the replica set.
189
+ def connect(force = !connected?)
190
+ return unless force
191
+ log(:info, "Connecting...")
192
+
193
+ # Prevent recursive connection attempts from the same thread.
194
+ # This is done rather than using a Monitor to prevent potentially recursing
195
+ # infinitely while attempting to connect and continually failing. Instead, fail fast.
196
+ raise ConnectionFailure, "Failed to get node data." if thread_local[:locks][:connecting] == true
197
+
198
+ current_version = @refresh_version
199
+ @connect_mutex.synchronize do
200
+ # don't try to connect if another thread has done so while we were waiting for the lock
201
+ return unless current_version == @refresh_version
202
+ begin
203
+ thread_local[:locks][:connecting] = true
204
+ if @manager
205
+ ensure_manager
206
+ @manager.refresh!(@seeds)
207
+ else
208
+ @manager = PoolManager.new(self, @seeds)
209
+ ensure_manager
210
+ @manager.connect
211
+ end
212
+ ensure
213
+ thread_local[:locks][:connecting] = false
214
+ end
215
+ @refresh_version += 1
216
+
217
+ if @manager.pools.empty?
218
+ close
219
+ raise ConnectionFailure, "Failed to connect to any node."
220
+ end
221
+ check_wire_version_in_range
222
+ @connected = true
223
+ end
224
+ end
225
+
226
+ # Reconnect the replica set client.
227
+ #
228
+ # @return [Boolean] +true+ unless the refresh lock can't be acquired.
229
+ #
230
+ # @since 1.12.4
231
+ def reconnect
232
+ close
233
+ refresh
234
+ end
235
+
236
+ # Determine whether a replica set refresh is
237
+ # required. If so, run a hard refresh. You can
238
+ # force a hard refresh by running
239
+ # MongoReplicaSetClient#hard_refresh!
240
+ #
241
+ # @return [Boolean] +true+ unless a hard refresh
242
+ # is run and the refresh lock can't be acquired.
243
+ def refresh(opts={})
244
+ if !connected?
245
+ log(:info, "Trying to check replica set health but not " +
246
+ "connected...")
247
+ return hard_refresh!
248
+ end
249
+
250
+ log(:debug, "Checking replica set connection health...")
251
+ ensure_manager
252
+ @manager.check_connection_health
253
+
254
+ if @manager.refresh_required?
255
+ return hard_refresh!
256
+ end
257
+
258
+ return true
259
+ end
260
+
261
+ # Force a hard refresh of this connection's view
262
+ # of the replica set.
263
+ #
264
+ # @return [Boolean] +true+ if hard refresh
265
+ # occurred. +false+ is returned when unable
266
+ # to get the refresh lock.
267
+ def hard_refresh!
268
+ log(:info, "Initiating hard refresh...")
269
+ connect(true)
270
+ return true
271
+ end
272
+
273
+ def connected?
274
+ @connected && !@manager.pools.empty?
275
+ end
276
+
277
+ # @deprecated
278
+ def connecting?
279
+ warn "MongoReplicaSetClient#connecting? is deprecated and will be removed in v2.0."
280
+ false
281
+ end
282
+
283
+ # The replica set primary's host name.
284
+ #
285
+ # @return [String]
286
+ def host
287
+ @manager.primary_pool.host
288
+ end
289
+
290
+ # The replica set primary's port.
291
+ #
292
+ # @return [Integer]
293
+ def port
294
+ @manager.primary_pool.port
295
+ end
296
+
297
+ def nodes
298
+ warn "MongoReplicaSetClient#nodes is DEPRECATED and will be removed in v2.0. " +
299
+ "Please use MongoReplicaSetClient#seeds instead."
300
+ @seeds
301
+ end
302
+
303
+ # Determine whether we're reading from a primary node. If false,
304
+ # this connection connects to a secondary node and @read_secondaries is true.
305
+ #
306
+ # @return [Boolean]
307
+ def read_primary?
308
+ read_pool == primary_pool
309
+ end
310
+ alias :primary? :read_primary?
311
+
312
+ # Close the connection to the database.
313
+ def close(opts={})
314
+ if opts[:soft]
315
+ @manager.close(:soft => true) if @manager
316
+ else
317
+ @manager.close if @manager
318
+ end
319
+
320
+ # Clear the reference to this object.
321
+ thread_local[:managers].delete(self)
322
+ unpin_pool
323
+
324
+ @connected = false
325
+ end
326
+
327
+ # If a ConnectionFailure is raised, this method will be called
328
+ # to close the connection and reset connection values.
329
+ # @deprecated
330
+ def reset_connection
331
+ close
332
+ warn "MongoReplicaSetClient#reset_connection is now deprecated and will be removed in v2.0. " +
333
+ "Use MongoReplicaSetClient#close instead."
334
+ end
335
+
336
+ # Returns +true+ if it's okay to read from a secondary node.
337
+ #
338
+ # This method exist primarily so that Cursor objects will
339
+ # generate query messages with a slaveOkay value of +true+.
340
+ #
341
+ # @return [Boolean] +true+
342
+ def slave_ok?
343
+ @read != :primary
344
+ end
345
+
346
+ # Generic socket checkout
347
+ # Takes a block that returns a socket from pool
348
+ def checkout
349
+ ensure_manager
350
+
351
+ connected? ? sync_refresh : connect
352
+
353
+ begin
354
+ socket = yield
355
+ rescue => ex
356
+ checkin(socket) if socket
357
+ raise ex
358
+ end
359
+
360
+ if socket
361
+ return socket
362
+ else
363
+ @connected = false
364
+ raise ConnectionFailure.new("Could not checkout a socket.")
365
+ end
366
+ end
367
+
368
+ def checkout_reader(read_pref={})
369
+ checkout do
370
+ pool = read_pool(read_pref)
371
+ get_socket_from_pool(pool)
372
+ end
373
+ end
374
+
375
+ # Checkout a socket for writing (i.e., a primary node).
376
+ def checkout_writer
377
+ checkout do
378
+ get_socket_from_pool(primary_pool)
379
+ end
380
+ end
381
+
382
+ # Checkin a socket used for reading.
383
+ def checkin(socket)
384
+ if socket && socket.pool
385
+ socket.checkin
386
+ end
387
+ sync_refresh
388
+ end
389
+
390
+ def ensure_manager
391
+ thread_local[:managers][self] = @manager
392
+ end
393
+
394
+ def pinned_pool
395
+ thread_local[:pinned_pools][@manager.object_id] if @manager
396
+ end
397
+
398
+ def pin_pool(pool, read_preference)
399
+ if @manager
400
+ thread_local[:pinned_pools][@manager.object_id] = {
401
+ :pool => pool,
402
+ :read_preference => read_preference
403
+ }
404
+ end
405
+ end
406
+
407
+ def unpin_pool
408
+ thread_local[:pinned_pools].delete @manager.object_id if @manager
409
+ end
410
+
411
+ def get_socket_from_pool(pool)
412
+ begin
413
+ pool.checkout if pool
414
+ rescue ConnectionFailure
415
+ nil
416
+ end
417
+ end
418
+
419
+ def local_manager
420
+ thread_local[:managers][self]
421
+ end
422
+
423
+ def arbiters
424
+ local_manager.arbiters.nil? ? [] : local_manager.arbiters
425
+ end
426
+
427
+ def primary
428
+ local_manager ? local_manager.primary : nil
429
+ end
430
+
431
+ # Note: might want to freeze these after connecting.
432
+ def secondaries
433
+ local_manager ? local_manager.secondaries : []
434
+ end
435
+
436
+ def hosts
437
+ local_manager ? local_manager.hosts : []
438
+ end
439
+
440
+ def primary_pool
441
+ local_manager ? local_manager.primary_pool : nil
442
+ end
443
+
444
+ def secondary_pool
445
+ local_manager ? local_manager.secondary_pool : nil
446
+ end
447
+
448
+ def secondary_pools
449
+ local_manager ? local_manager.secondary_pools : []
450
+ end
451
+
452
+ def pools
453
+ local_manager ? local_manager.pools : []
454
+ end
455
+
456
+ def tag_map
457
+ local_manager ? local_manager.tag_map : {}
458
+ end
459
+
460
+ def max_bson_size
461
+ return local_manager.max_bson_size if local_manager
462
+ DEFAULT_MAX_BSON_SIZE
463
+ end
464
+
465
+ def max_message_size
466
+ return local_manager.max_message_size if local_manager
467
+ max_bson_size * MESSAGE_SIZE_FACTOR
468
+ end
469
+
470
+ def max_wire_version
471
+ return local_manager.max_wire_version if local_manager
472
+ 0
473
+ end
474
+
475
+ def min_wire_version
476
+ return local_manager.min_wire_version if local_manager
477
+ 0
478
+ end
479
+
480
+ def primary_wire_version_feature?(feature)
481
+ local_manager && local_manager.primary_pool && local_manager.primary_pool.node.wire_version_feature?(feature)
482
+ end
483
+
484
+ def max_write_batch_size
485
+ local_manager && local_manager.primary_pool && local_manager.primary_pool.node.max_write_batch_size ||
486
+ DEFAULT_MAX_WRITE_BATCH_SIZE
487
+ end
488
+
489
+ private
490
+
491
+ # Parse option hash
492
+ def setup(opts)
493
+ # Refresh
494
+ @refresh_mode = opts.delete(:refresh_mode) || false
495
+ @refresh_interval = opts.delete(:refresh_interval) || 90
496
+
497
+ if @refresh_mode && @refresh_interval < 60
498
+ @refresh_interval = 60 unless ENV['TEST_MODE'] = 'TRUE'
499
+ end
500
+
501
+ if @refresh_mode == :async
502
+ warn ":async refresh mode has been deprecated. Refresh
503
+ mode will be disabled."
504
+ elsif ![:sync, false].include?(@refresh_mode)
505
+ raise MongoArgumentError,
506
+ "Refresh mode must be either :sync or false."
507
+ end
508
+
509
+ if opts[:read_secondary]
510
+ warn ":read_secondary options has now been deprecated and will " +
511
+ "be removed in driver v2.0. Use the :read option instead."
512
+ @read_secondary = opts.delete(:read_secondary) || false
513
+ end
514
+
515
+ # Replica set name
516
+ if opts[:rs_name]
517
+ warn ":rs_name option has been deprecated and will be removed in v2.0. " +
518
+ "Please use :name instead."
519
+ @replica_set_name = opts.delete(:rs_name)
520
+ else
521
+ @replica_set_name = opts.delete(:name)
522
+ end
523
+
524
+ super opts
525
+ end
526
+
527
+ def sync_refresh
528
+ if @refresh_mode == :sync &&
529
+ ((Time.now - @last_refresh) > @refresh_interval)
530
+ @last_refresh = Time.now
531
+ refresh
532
+ end
533
+ end
534
+ end
535
+ end
@@ -0,0 +1,159 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+
17
+ # Instantiates and manages connections to a MongoDB sharded cluster for high availability.
18
+ class MongoShardedClient < MongoReplicaSetClient
19
+ include ThreadLocalVariableManager
20
+
21
+ SHARDED_CLUSTER_OPTS = [:refresh_mode, :refresh_interval, :tag_sets, :read]
22
+
23
+ attr_reader :seeds, :refresh_interval, :refresh_mode,
24
+ :refresh_version, :manager
25
+
26
+ def initialize(*args)
27
+ opts = args.last.is_a?(Hash) ? args.pop : {}
28
+
29
+ nodes = args.flatten
30
+
31
+ if nodes.empty? and ENV.has_key?('MONGODB_URI')
32
+ parser = URIParser.new ENV['MONGODB_URI']
33
+ opts = parser.connection_options.merge! opts
34
+ nodes = parser.node_strings
35
+ end
36
+
37
+ unless nodes.length > 0
38
+ raise MongoArgumentError, "A MongoShardedClient requires at least one seed node."
39
+ end
40
+
41
+ @seeds = nodes.map do |host_port|
42
+ Support.normalize_seeds(host_port)
43
+ end
44
+
45
+ # TODO: add a method for replacing this list of node.
46
+ @seeds.freeze
47
+
48
+ # Refresh
49
+ @last_refresh = Time.now
50
+ @refresh_version = 0
51
+
52
+ # No connection manager by default.
53
+ @manager = nil
54
+
55
+ # Lock for request ids.
56
+ @id_lock = Mutex.new
57
+
58
+ @connected = false
59
+
60
+ @connect_mutex = Mutex.new
61
+
62
+ @mongos = true
63
+
64
+ check_opts(opts)
65
+ setup(opts)
66
+ end
67
+
68
+ def valid_opts
69
+ super + SHARDED_CLUSTER_OPTS
70
+ end
71
+
72
+ def inspect
73
+ "<Mongo::MongoShardedClient:0x#{self.object_id.to_s(16)} @seeds=#{@seeds.inspect} " +
74
+ "@connected=#{@connected}>"
75
+ end
76
+
77
+ # Initiate a connection to the sharded cluster.
78
+ def connect(force = !connected?)
79
+ return unless force
80
+ log(:info, "Connecting...")
81
+
82
+ # Prevent recursive connection attempts from the same thread.
83
+ # This is done rather than using a Monitor to prevent potentially recursing
84
+ # infinitely while attempting to connect and continually failing. Instead, fail fast.
85
+ raise ConnectionFailure, "Failed to get node data." if thread_local[:locks][:connecting]
86
+
87
+ @connect_mutex.synchronize do
88
+ begin
89
+ thread_local[:locks][:connecting] = true
90
+ if @manager
91
+ thread_local[:managers][self] = @manager
92
+ @manager.refresh! @seeds
93
+ else
94
+ @manager = ShardingPoolManager.new(self, @seeds)
95
+ ensure_manager
96
+ @manager.connect
97
+ check_wire_version_in_range
98
+ end
99
+ ensure
100
+ thread_local[:locks][:connecting] = false
101
+ end
102
+
103
+ @refresh_version += 1
104
+ @last_refresh = Time.now
105
+ @connected = true
106
+ end
107
+ end
108
+
109
+ # Force a hard refresh of this connection's view
110
+ # of the sharded cluster.
111
+ #
112
+ # @return [Boolean] +true+ if hard refresh
113
+ # occurred. +false+ is returned when unable
114
+ # to get the refresh lock.
115
+ def hard_refresh!
116
+ log(:info, "Initiating hard refresh...")
117
+ connect(true)
118
+ return true
119
+ end
120
+
121
+ def connected?
122
+ !!(@connected && @manager.primary_pool)
123
+ end
124
+
125
+ # Returns +true+ if it's okay to read from a secondary node.
126
+ # Since this is a sharded cluster, this must always be false.
127
+ #
128
+ # This method exist primarily so that Cursor objects will
129
+ # generate query messages with a slaveOkay value of +true+.
130
+ #
131
+ # @return [Boolean] +true+
132
+ def slave_ok?
133
+ false
134
+ end
135
+
136
+ def checkout(&block)
137
+ tries = 0
138
+ begin
139
+ super(&block)
140
+ rescue ConnectionFailure
141
+ tries +=1
142
+ tries < 2 ? retry : raise
143
+ end
144
+ end
145
+
146
+ # Initialize a connection to MongoDB using the MongoDB URI spec.
147
+ #
148
+ # @param uri [ String ] string of the format:
149
+ # mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]
150
+ #
151
+ # @param options [ Hash ] Any of the options available for MongoShardedClient.new
152
+ #
153
+ # @return [ Mongo::MongoShardedClient ] The sharded client.
154
+ def self.from_uri(uri, options={})
155
+ uri ||= ENV['MONGODB_URI']
156
+ URIParser.new(uri).connection(options, false, true)
157
+ end
158
+ end
159
+ end