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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -271
- data/Rakefile +25 -209
- data/VERSION +1 -0
- data/bin/mongo_console +31 -9
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +576 -269
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection/node.rb +249 -0
- data/lib/mongo/connection/pool.rb +340 -0
- data/lib/mongo/connection/pool_manager.rb +320 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
- data/lib/mongo/connection/socket/socket_util.rb +37 -0
- data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
- data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/connection.rb +7 -875
- data/lib/mongo/cursor.rb +403 -117
- data/lib/mongo/db.rb +444 -243
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/functional/logging.rb +85 -0
- data/lib/mongo/functional/read_preference.rb +183 -0
- data/lib/mongo/functional/scram.rb +556 -0
- data/lib/mongo/functional/uri_parser.rb +409 -0
- data/lib/mongo/functional/write_concern.rb +66 -0
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +30 -24
- data/lib/mongo/gridfs/grid_ext.rb +6 -10
- data/lib/mongo/gridfs/grid_file_system.rb +38 -20
- data/lib/mongo/gridfs/grid_io.rb +84 -75
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +697 -0
- data/lib/mongo/mongo_replica_set_client.rb +535 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +372 -0
- data/lib/mongo/{util → utils}/conversions.rb +29 -8
- data/lib/mongo/{util → utils}/core_ext.rb +28 -18
- data/lib/mongo/{util → utils}/server_version.rb +4 -6
- data/lib/mongo/{util → utils}/support.rb +29 -31
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +51 -50
- data/mongo.gemspec +29 -32
- data/test/functional/authentication_test.rb +39 -0
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1198 -0
- data/test/functional/client_test.rb +627 -0
- data/test/functional/collection_test.rb +2175 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
- data/test/functional/cursor_fail_test.rb +57 -0
- data/test/functional/cursor_message_test.rb +56 -0
- data/test/functional/cursor_test.rb +683 -0
- data/test/functional/db_api_test.rb +835 -0
- data/test/functional/db_connection_test.rb +25 -0
- data/test/functional/db_test.rb +348 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
- data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
- data/test/functional/pool_test.rb +136 -0
- data/test/functional/safe_test.rb +98 -0
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +62 -0
- data/test/functional/timeout_test.rb +60 -0
- data/test/functional/uri_test.rb +446 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +37 -0
- data/test/replica_set/basic_test.rb +189 -0
- data/test/replica_set/client_test.rb +393 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +66 -0
- data/test/replica_set/cursor_test.rb +220 -0
- data/test/replica_set/insert_test.rb +157 -0
- data/test/replica_set/max_values_test.rb +151 -0
- data/test/replica_set/pinning_test.rb +105 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +219 -0
- data/test/replica_set/refresh_test.rb +211 -0
- data/test/replica_set/replication_ack_test.rb +95 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +203 -0
- data/test/shared/authentication/basic_auth_shared.rb +260 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
- data/test/shared/authentication/gssapi_shared.rb +176 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +53 -94
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +708 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +381 -0
- data/test/unit/collection_test.rb +89 -53
- data/test/unit/connection_test.rb +282 -32
- data/test/unit/cursor_test.rb +206 -8
- data/test/unit/db_test.rb +55 -13
- data/test/unit/grid_test.rb +43 -16
- data/test/unit/mongo_sharded_client_test.rb +48 -0
- data/test/unit/node_test.rb +93 -0
- data/test/unit/pool_manager_test.rb +111 -0
- data/test/unit/read_pref_test.rb +406 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +69 -36
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- data.tar.gz.sig +3 -0
- metadata +227 -216
- metadata.gz.sig +0 -0
- data/docs/CREDITS.md +0 -123
- data/docs/FAQ.md +0 -116
- data/docs/GridFS.md +0 -158
- data/docs/HISTORY.md +0 -244
- data/docs/RELEASES.md +0 -33
- data/docs/REPLICA_SETS.md +0 -72
- data/docs/TUTORIAL.md +0 -247
- data/docs/WRITE_CONCERN.md +0 -28
- data/lib/mongo/exceptions.rb +0 -71
- data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
- data/lib/mongo/repl_set_connection.rb +0 -342
- data/lib/mongo/test.rb +0 -20
- data/lib/mongo/util/pool.rb +0 -177
- data/lib/mongo/util/uri_parser.rb +0 -185
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/auxillary/1.4_features.rb +0 -166
- data/test/auxillary/authentication_test.rb +0 -68
- data/test/auxillary/autoreconnect_test.rb +0 -41
- data/test/auxillary/fork_test.rb +0 -30
- data/test/auxillary/repl_set_auth_test.rb +0 -58
- data/test/auxillary/slave_connection_test.rb +0 -36
- data/test/auxillary/threaded_authentication_test.rb +0 -101
- data/test/bson/binary_test.rb +0 -15
- data/test/bson/bson_test.rb +0 -649
- data/test/bson/byte_buffer_test.rb +0 -208
- data/test/bson/hash_with_indifferent_access_test.rb +0 -38
- data/test/bson/json_test.rb +0 -17
- data/test/bson/object_id_test.rb +0 -154
- data/test/bson/ordered_hash_test.rb +0 -204
- data/test/bson/timestamp_test.rb +0 -24
- data/test/collection_test.rb +0 -910
- data/test/connection_test.rb +0 -309
- data/test/cursor_fail_test.rb +0 -75
- data/test/cursor_message_test.rb +0 -43
- data/test/cursor_test.rb +0 -483
- data/test/db_api_test.rb +0 -726
- data/test/db_connection_test.rb +0 -15
- data/test/db_test.rb +0 -287
- data/test/grid_file_system_test.rb +0 -243
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/thin/load.rb +0 -24
- data/test/load/unicorn/load.rb +0 -23
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/replica_sets/connect_test.rb +0 -94
- data/test/replica_sets/connection_string_test.rb +0 -32
- data/test/replica_sets/count_test.rb +0 -35
- data/test/replica_sets/insert_test.rb +0 -53
- data/test/replica_sets/pooled_insert_test.rb +0 -55
- data/test/replica_sets/query_secondaries.rb +0 -96
- data/test/replica_sets/query_test.rb +0 -51
- data/test/replica_sets/replication_ack_test.rb +0 -66
- data/test/replica_sets/rs_test_helper.rb +0 -27
- data/test/safe_test.rb +0 -68
- data/test/support/hash_with_indifferent_access.rb +0 -186
- data/test/support/keys.rb +0 -45
- data/test/support_test.rb +0 -18
- data/test/threading/threading_with_large_pool_test.rb +0 -90
- data/test/threading_test.rb +0 -87
- data/test/tools/auth_repl_set_manager.rb +0 -14
- data/test/tools/load.rb +0 -58
- data/test/tools/repl_set_manager.rb +0 -266
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/repl_set_connection_test.rb +0 -59
- 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
|