mongo 1.10.0-java
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.tar.gz.sig +0 -0
- data/LICENSE +190 -0
- data/README.md +149 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/bin/mongo_console +43 -0
- data/ext/jsasl/target/jsasl.jar +0 -0
- data/lib/mongo.rb +90 -0
- data/lib/mongo/bulk_write_collection_view.rb +380 -0
- data/lib/mongo/collection.rb +1164 -0
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection.rb +19 -0
- data/lib/mongo/connection/node.rb +239 -0
- data/lib/mongo/connection/pool.rb +347 -0
- data/lib/mongo/connection/pool_manager.rb +325 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
- data/lib/mongo/connection/socket.rb +18 -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 +86 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/cursor.rb +719 -0
- data/lib/mongo/db.rb +735 -0
- data/lib/mongo/exception.rb +88 -0
- data/lib/mongo/functional.rb +21 -0
- data/lib/mongo/functional/authentication.rb +318 -0
- data/lib/mongo/functional/logging.rb +85 -0
- data/lib/mongo/functional/read_preference.rb +174 -0
- data/lib/mongo/functional/sasl_java.rb +48 -0
- data/lib/mongo/functional/uri_parser.rb +374 -0
- data/lib/mongo/functional/write_concern.rb +66 -0
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/gridfs/grid.rb +112 -0
- data/lib/mongo/gridfs/grid_ext.rb +53 -0
- data/lib/mongo/gridfs/grid_file_system.rb +163 -0
- data/lib/mongo/gridfs/grid_io.rb +484 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +702 -0
- data/lib/mongo/mongo_replica_set_client.rb +523 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +370 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo/utils/conversions.rb +110 -0
- data/lib/mongo/utils/core_ext.rb +70 -0
- data/lib/mongo/utils/server_version.rb +69 -0
- data/lib/mongo/utils/support.rb +80 -0
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/mongo.gemspec +36 -0
- data/test/functional/authentication_test.rb +35 -0
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1129 -0
- data/test/functional/client_test.rb +565 -0
- data/test/functional/collection_test.rb +2073 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/functional/conversions_test.rb +163 -0
- data/test/functional/cursor_fail_test.rb +63 -0
- data/test/functional/cursor_message_test.rb +57 -0
- data/test/functional/cursor_test.rb +625 -0
- data/test/functional/db_api_test.rb +819 -0
- data/test/functional/db_connection_test.rb +27 -0
- data/test/functional/db_test.rb +344 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/functional/grid_io_test.rb +252 -0
- data/test/functional/grid_test.rb +273 -0
- data/test/functional/pool_test.rb +62 -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 +58 -0
- data/test/functional/uri_test.rb +330 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +317 -0
- data/test/replica_set/authentication_test.rb +35 -0
- data/test/replica_set/basic_test.rb +174 -0
- data/test/replica_set/client_test.rb +341 -0
- data/test/replica_set/complex_connect_test.rb +77 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +64 -0
- data/test/replica_set/cursor_test.rb +212 -0
- data/test/replica_set/insert_test.rb +140 -0
- data/test/replica_set/max_values_test.rb +145 -0
- data/test/replica_set/pinning_test.rb +55 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +214 -0
- data/test/replica_set/refresh_test.rb +175 -0
- data/test/replica_set/replication_ack_test.rb +94 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +197 -0
- data/test/shared/authentication/basic_auth_shared.rb +286 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +259 -0
- data/test/shared/authentication/gssapi_shared.rb +164 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +56 -0
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +608 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +347 -0
- data/test/unit/collection_test.rb +166 -0
- data/test/unit/connection_test.rb +325 -0
- data/test/unit/cursor_test.rb +299 -0
- data/test/unit/db_test.rb +136 -0
- data/test/unit/grid_test.rb +76 -0
- 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 +142 -0
- data/test/unit/read_pref_test.rb +115 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +158 -0
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- metadata +260 -0
- metadata.gz.sig +0 -0
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
# NOTE: on ruby <1.9 you need to run individual tests with 'bundle exec'
|
|
16
|
+
|
|
17
|
+
unless RUBY_VERSION < '1.9' || ENV.key?('JENKINS_CI')
|
|
18
|
+
require 'simplecov'
|
|
19
|
+
require 'coveralls'
|
|
20
|
+
|
|
21
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
|
22
|
+
SimpleCov::Formatter::HTMLFormatter,
|
|
23
|
+
Coveralls::SimpleCov::Formatter
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
SimpleCov.start do
|
|
27
|
+
add_group 'Driver', 'lib/mongo'
|
|
28
|
+
add_group 'BSON', 'lib/bson'
|
|
29
|
+
|
|
30
|
+
add_filter 'tasks'
|
|
31
|
+
add_filter 'test'
|
|
32
|
+
add_filter 'bin'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# required for at_exit, at_start hooks
|
|
37
|
+
require 'test-unit'
|
|
38
|
+
|
|
39
|
+
require 'test/unit'
|
|
40
|
+
require 'shoulda'
|
|
41
|
+
require 'mocha/setup'
|
|
42
|
+
|
|
43
|
+
# cluster manager
|
|
44
|
+
require 'tools/mongo_config'
|
|
45
|
+
|
|
46
|
+
# test helpers
|
|
47
|
+
require 'helpers/general'
|
|
48
|
+
require 'helpers/test_unit'
|
|
49
|
+
|
|
50
|
+
# optional development and debug utilities
|
|
51
|
+
begin
|
|
52
|
+
require 'pry-rescue'
|
|
53
|
+
require 'pry-nav'
|
|
54
|
+
rescue LoadError
|
|
55
|
+
# failed to load, skipping pry
|
|
56
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
require 'test_helper'
|
|
16
|
+
|
|
17
|
+
class ThreadingTest < Test::Unit::TestCase
|
|
18
|
+
|
|
19
|
+
include Mongo
|
|
20
|
+
|
|
21
|
+
def setup
|
|
22
|
+
@client = standard_connection(:pool_size => 10, :pool_timeout => 30)
|
|
23
|
+
@db = @client.db(TEST_DB)
|
|
24
|
+
@coll = @db.collection('thread-test-collection')
|
|
25
|
+
@coll.drop
|
|
26
|
+
|
|
27
|
+
collections = ['duplicate', 'unique']
|
|
28
|
+
|
|
29
|
+
collections.each do |coll_name|
|
|
30
|
+
coll = @db.collection(coll_name)
|
|
31
|
+
coll.drop
|
|
32
|
+
coll.insert("test" => "insert")
|
|
33
|
+
coll.insert("test" => "update")
|
|
34
|
+
instance_variable_set("@#{coll_name}", coll)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@unique.create_index("test", :unique => true)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_safe_update
|
|
41
|
+
threads = []
|
|
42
|
+
300.times do |i|
|
|
43
|
+
threads << Thread.new do
|
|
44
|
+
if i % 2 == 0
|
|
45
|
+
assert_raise Mongo::OperationFailure do
|
|
46
|
+
@unique.update({"test" => "insert"}, {"$set" => {"test" => "update"}})
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
@duplicate.update({"test" => "insert"}, {"$set" => {"test" => "update"}})
|
|
50
|
+
@duplicate.update({"test" => "update"}, {"$set" => {"test" => "insert"}})
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
threads.each {|thread| thread.join}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_safe_insert
|
|
59
|
+
threads = []
|
|
60
|
+
300.times do |i|
|
|
61
|
+
threads << Thread.new do
|
|
62
|
+
if i % 2 == 0
|
|
63
|
+
assert_raise Mongo::OperationFailure do
|
|
64
|
+
@unique.insert({"test" => "insert"})
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
@duplicate.insert({"test" => "insert"})
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
threads.each {|thread| thread.join}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_concurrent_find
|
|
76
|
+
n_threads = 50
|
|
77
|
+
|
|
78
|
+
1000.times do |i|
|
|
79
|
+
@coll.insert({ "x" => "a" })
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
threads = []
|
|
83
|
+
n_threads.times do |i|
|
|
84
|
+
threads << Thread.new do
|
|
85
|
+
sum = 0
|
|
86
|
+
@coll.find.to_a.size
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
thread_values = threads.map(&:value)
|
|
91
|
+
assert thread_values.all?{|v| v == 1000}
|
|
92
|
+
assert_equal thread_values.size, n_threads
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_threading
|
|
96
|
+
@coll.drop
|
|
97
|
+
@coll = @db.collection('thread-test-collection')
|
|
98
|
+
|
|
99
|
+
docs = []
|
|
100
|
+
1000.times {|i| docs << {:x => i}}
|
|
101
|
+
@coll.insert(docs)
|
|
102
|
+
|
|
103
|
+
threads = []
|
|
104
|
+
|
|
105
|
+
10.times do |i|
|
|
106
|
+
threads[i] = Thread.new do
|
|
107
|
+
sum = 0
|
|
108
|
+
@coll.find().each do |document|
|
|
109
|
+
sum += document["x"]
|
|
110
|
+
end
|
|
111
|
+
assert_equal 499500, sum
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
10.times do |i|
|
|
116
|
+
threads[i].join
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require 'socket'
|
|
18
|
+
require 'fileutils'
|
|
19
|
+
require 'mongo'
|
|
20
|
+
require 'sfl'
|
|
21
|
+
|
|
22
|
+
$debug_level = 2
|
|
23
|
+
STDOUT.sync = true
|
|
24
|
+
|
|
25
|
+
def debug(level, arg)
|
|
26
|
+
if level <= $debug_level
|
|
27
|
+
file_line = caller[0][/(.*:\d+):/, 1]
|
|
28
|
+
calling_method = caller[0][/`([^']*)'/, 1]
|
|
29
|
+
puts "#{file_line}:#{calling_method}:#{arg.class == String ? arg : arg.inspect}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
# Design Notes
|
|
35
|
+
# Configuration and Cluster Management are modularized with the concept that the Cluster Manager
|
|
36
|
+
# can be supplied with any configuration to run.
|
|
37
|
+
# A configuration can be edited, modified, copied into a test file, and supplied to a cluster manager
|
|
38
|
+
# as a parameter.
|
|
39
|
+
#
|
|
40
|
+
module Mongo
|
|
41
|
+
class Config
|
|
42
|
+
DEFAULT_BASE_OPTS = { :host => 'localhost', :dbpath => 'data', :logpath => 'data/log' }
|
|
43
|
+
DEFAULT_REPLICA_SET = DEFAULT_BASE_OPTS.merge( :replicas => 3, :arbiters => 0 )
|
|
44
|
+
DEFAULT_SHARDED_SIMPLE = DEFAULT_BASE_OPTS.merge( :shards => 2, :configs => 1, :routers => 2 )
|
|
45
|
+
DEFAULT_SHARDED_REPLICA = DEFAULT_SHARDED_SIMPLE.merge( :replicas => 3, :arbiters => 0)
|
|
46
|
+
|
|
47
|
+
IGNORE_KEYS = [:host, :command, :_id]
|
|
48
|
+
SHARDING_OPT_KEYS = [:shards, :configs, :routers]
|
|
49
|
+
REPLICA_OPT_KEYS = [:replicas, :arbiters]
|
|
50
|
+
MONGODS_OPT_KEYS = [:mongods]
|
|
51
|
+
CLUSTER_OPT_KEYS = SHARDING_OPT_KEYS + REPLICA_OPT_KEYS + MONGODS_OPT_KEYS
|
|
52
|
+
|
|
53
|
+
FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet, :fastsync, :auth, :ipv6]
|
|
54
|
+
|
|
55
|
+
DEFAULT_VERIFIES = 60
|
|
56
|
+
BASE_PORT = 3000
|
|
57
|
+
@@port = BASE_PORT
|
|
58
|
+
|
|
59
|
+
def self.configdb(config)
|
|
60
|
+
config[:configs].collect{|c|"#{c[:host]}:#{c[:port]}"}.join(' ')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.cluster(opts = DEFAULT_SHARDED_SIMPLE)
|
|
64
|
+
raise "missing required option" if [:host, :dbpath].any?{|k| !opts[k]}
|
|
65
|
+
|
|
66
|
+
config = opts.reject {|k,v| CLUSTER_OPT_KEYS.include?(k)}
|
|
67
|
+
|
|
68
|
+
kinds = CLUSTER_OPT_KEYS.select{|key| opts.has_key?(key)} # order is significant
|
|
69
|
+
|
|
70
|
+
replica_count = 0
|
|
71
|
+
|
|
72
|
+
kinds.each do |kind|
|
|
73
|
+
config[kind] = opts.fetch(kind,1).times.collect do |i| #default to 1 of whatever
|
|
74
|
+
if kind == :shards && opts[:replicas]
|
|
75
|
+
self.cluster(opts.reject{|k,v| SHARDING_OPT_KEYS.include?(k)}.merge(:dbpath => path))
|
|
76
|
+
else
|
|
77
|
+
node = case kind
|
|
78
|
+
when :replicas
|
|
79
|
+
make_replica(opts, replica_count)
|
|
80
|
+
when :arbiters
|
|
81
|
+
make_replica(opts, replica_count)
|
|
82
|
+
when :configs
|
|
83
|
+
make_config(opts)
|
|
84
|
+
when :routers
|
|
85
|
+
make_router(config, opts)
|
|
86
|
+
else
|
|
87
|
+
make_mongod(kind, opts)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
replica_count += 1 if [:replicas, :arbiters].member?(kind)
|
|
91
|
+
node
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
config
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.make_mongo(kind, opts)
|
|
99
|
+
dbpath = opts[:dbpath]
|
|
100
|
+
port = self.get_available_port
|
|
101
|
+
path = "#{dbpath}/#{kind}-#{port}"
|
|
102
|
+
logpath = "#{path}/#{kind}.log"
|
|
103
|
+
|
|
104
|
+
{ :host => opts[:host],
|
|
105
|
+
:port => port,
|
|
106
|
+
:logpath => logpath,
|
|
107
|
+
:logappend => true }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.make_mongod(kind, opts)
|
|
111
|
+
params = make_mongo('mongods', opts)
|
|
112
|
+
|
|
113
|
+
mongod = ENV['MONGOD'] || 'mongod'
|
|
114
|
+
path = File.dirname(params[:logpath])
|
|
115
|
+
|
|
116
|
+
noprealloc = opts[:noprealloc] || true
|
|
117
|
+
smallfiles = opts[:smallfiles] || true
|
|
118
|
+
quiet = opts[:quiet] || true
|
|
119
|
+
fast_sync = opts[:fastsync] || false
|
|
120
|
+
auth = opts[:auth] || true
|
|
121
|
+
ipv6 = opts[:ipv6].nil? ? true : opts[:ipv6]
|
|
122
|
+
|
|
123
|
+
params.merge(:command => mongod,
|
|
124
|
+
:dbpath => path,
|
|
125
|
+
:smallfiles => smallfiles,
|
|
126
|
+
:noprealloc => noprealloc,
|
|
127
|
+
:quiet => quiet,
|
|
128
|
+
:fastsync => fast_sync,
|
|
129
|
+
:auth => auth,
|
|
130
|
+
:ipv6 => ipv6)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.make_replica(opts, id)
|
|
134
|
+
params = make_mongod('replicas', opts)
|
|
135
|
+
|
|
136
|
+
replSet = opts[:replSet] || 'ruby-driver-test'
|
|
137
|
+
oplogSize = opts[:oplog_size] || 5
|
|
138
|
+
keyFile = opts[:key_file] || '/test/fixtures/auth/keyfile'
|
|
139
|
+
|
|
140
|
+
keyFile = Dir.pwd << keyFile
|
|
141
|
+
system "chmod 600 #{keyFile}"
|
|
142
|
+
|
|
143
|
+
params.merge(:_id => id,
|
|
144
|
+
:replSet => replSet,
|
|
145
|
+
:oplogSize => oplogSize,
|
|
146
|
+
:keyFile => keyFile)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def self.make_config(opts)
|
|
150
|
+
params = make_mongod('configs', opts)
|
|
151
|
+
params.merge(:configsvr => nil)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.make_router(config, opts)
|
|
155
|
+
params = make_mongo('routers', opts)
|
|
156
|
+
mongos = ENV['MONGOS'] || 'mongos'
|
|
157
|
+
|
|
158
|
+
params.merge(
|
|
159
|
+
:command => mongos,
|
|
160
|
+
:configdb => self.configdb(config)
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def self.port_available?(port)
|
|
165
|
+
ret = false
|
|
166
|
+
socket = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
|
|
167
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
|
168
|
+
sockaddr = Socket.sockaddr_in(port, '0.0.0.0')
|
|
169
|
+
begin
|
|
170
|
+
socket.bind(sockaddr)
|
|
171
|
+
ret = true
|
|
172
|
+
rescue Exception
|
|
173
|
+
end
|
|
174
|
+
socket.close
|
|
175
|
+
ret
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def self.get_available_port
|
|
179
|
+
while true
|
|
180
|
+
port = @@port
|
|
181
|
+
@@port += 1
|
|
182
|
+
break if port_available?(port)
|
|
183
|
+
end
|
|
184
|
+
port
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class SysProc
|
|
188
|
+
attr_reader :pid, :cmd
|
|
189
|
+
|
|
190
|
+
def initialize(cmd = nil)
|
|
191
|
+
@pid = nil
|
|
192
|
+
@cmd = cmd
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def clear_zombie
|
|
196
|
+
if @pid
|
|
197
|
+
begin
|
|
198
|
+
pid = Process.waitpid(@pid, Process::WNOHANG)
|
|
199
|
+
rescue Errno::ECHILD
|
|
200
|
+
# JVM might have already reaped the exit status
|
|
201
|
+
end
|
|
202
|
+
@pid = nil if pid && pid > 0
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def start(verifies = 0)
|
|
207
|
+
clear_zombie
|
|
208
|
+
return @pid if running?
|
|
209
|
+
begin
|
|
210
|
+
# redirection not supported in jruby
|
|
211
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
212
|
+
@pid = Process.spawn(*@cmd)
|
|
213
|
+
else
|
|
214
|
+
cmd_and_opts = [@cmd, {:out => '/dev/null'}].flatten
|
|
215
|
+
@pid = Process.spawn(*cmd_and_opts)
|
|
216
|
+
end
|
|
217
|
+
verify(verifies) if verifies > 0
|
|
218
|
+
@pid
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def stop
|
|
223
|
+
kill
|
|
224
|
+
wait
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def kill(signal_no = 2)
|
|
228
|
+
begin
|
|
229
|
+
@pid && Process.kill(signal_no, @pid) && true
|
|
230
|
+
rescue Errno::ESRCH
|
|
231
|
+
false
|
|
232
|
+
end
|
|
233
|
+
# cleanup lock if unclean shutdown
|
|
234
|
+
begin
|
|
235
|
+
File.delete(File.join(@config[:dbpath], 'mongod.lock')) if @config[:dbpath]
|
|
236
|
+
rescue Errno::ENOENT
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def wait
|
|
241
|
+
begin
|
|
242
|
+
Process.waitpid(@pid) if @pid
|
|
243
|
+
rescue Errno::ECHILD
|
|
244
|
+
# JVM might have already reaped the exit status
|
|
245
|
+
end
|
|
246
|
+
@pid = nil
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def running?
|
|
250
|
+
begin
|
|
251
|
+
@pid && Process.kill(0, @pid) && true
|
|
252
|
+
rescue Errno::ESRCH
|
|
253
|
+
false
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def verify(verifies = DEFAULT_VERIFIES)
|
|
258
|
+
verifies.times do |i|
|
|
259
|
+
return @pid if running?
|
|
260
|
+
sleep 1
|
|
261
|
+
end
|
|
262
|
+
nil
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
class Server < SysProc
|
|
267
|
+
attr_reader :host, :port
|
|
268
|
+
|
|
269
|
+
def initialize(cmd = nil, host = nil, port = nil)
|
|
270
|
+
super(cmd)
|
|
271
|
+
@host = host
|
|
272
|
+
@port = port
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def host_port
|
|
276
|
+
[@host, @port].join(':')
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def host_port_a # for old format
|
|
280
|
+
[@host, @port]
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
class DbServer < Server
|
|
285
|
+
attr_accessor :config
|
|
286
|
+
|
|
287
|
+
def initialize(config)
|
|
288
|
+
@config = config
|
|
289
|
+
dbpath = @config[:dbpath]
|
|
290
|
+
[dbpath, File.dirname(@config[:logpath])].compact.each{|dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
|
|
291
|
+
command = @config[:command] || 'mongod'
|
|
292
|
+
params = @config.reject{|k,v| IGNORE_KEYS.include?(k)}
|
|
293
|
+
arguments = params.sort{|a, b| a[0].to_s <=> b[0].to_s}.collect do |arg, value| # sort block is needed for 1.8.7 which lacks Symbol#<=>
|
|
294
|
+
argument = '--' + arg.to_s
|
|
295
|
+
if FLAGS.member?(arg) && value == true
|
|
296
|
+
[argument]
|
|
297
|
+
elsif !FLAGS.member?(arg)
|
|
298
|
+
[argument, value.to_s]
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
cmd = [command, arguments].flatten.compact
|
|
302
|
+
super(cmd, @config[:host], @config[:port])
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def start(verifies = DEFAULT_VERIFIES)
|
|
306
|
+
super(verifies)
|
|
307
|
+
verify(verifies)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def verify(verifies = 600)
|
|
311
|
+
verifies.times do |i|
|
|
312
|
+
#puts "DbServer.verify via connection probe - port:#{@port.inspect} iteration:#{i} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
|
|
313
|
+
begin
|
|
314
|
+
raise Mongo::ConnectionFailure unless running?
|
|
315
|
+
Mongo::MongoClient.new(@host, @port).close
|
|
316
|
+
#puts "DbServer.verified via connection - port: #{@port} iteration: #{i}"
|
|
317
|
+
return @pid
|
|
318
|
+
rescue Mongo::ConnectionFailure
|
|
319
|
+
sleep 1
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
system "ps -fp #{@pid}; cat #{@config[:logpath]}"
|
|
323
|
+
raise Mongo::ConnectionFailure, "DbServer.start verify via connection probe failed - port:#{@port.inspect} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
class ClusterManager
|
|
329
|
+
attr_reader :config
|
|
330
|
+
def initialize(config)
|
|
331
|
+
@config = config
|
|
332
|
+
@servers = {}
|
|
333
|
+
Mongo::Config::CLUSTER_OPT_KEYS.each do |key|
|
|
334
|
+
@servers[key] = @config[key].collect{|conf| DbServer.new(conf)} if @config[key]
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def servers(key = nil)
|
|
339
|
+
@servers.collect{|k,v| (!key || key == k) ? v : nil}.flatten.compact
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def command( cmd_servers, db_name, cmd, opts = {} )
|
|
343
|
+
ret = []
|
|
344
|
+
cmd = cmd.class == Array ? cmd : [ cmd ]
|
|
345
|
+
debug 3, "ClusterManager.command cmd:#{cmd.inspect}"
|
|
346
|
+
cmd_servers = cmd_servers.class == Array ? cmd_servers : [cmd_servers]
|
|
347
|
+
cmd_servers.each do |cmd_server|
|
|
348
|
+
debug 3, cmd_server.inspect
|
|
349
|
+
cmd_server = cmd_server.config if cmd_server.is_a?(DbServer)
|
|
350
|
+
client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
|
|
351
|
+
cmd.each do |c|
|
|
352
|
+
debug 3, "ClusterManager.command c:#{c.inspect}"
|
|
353
|
+
response = client[db_name].command( c, opts )
|
|
354
|
+
debug 3, "ClusterManager.command response:#{response.inspect}"
|
|
355
|
+
raise Mongo::OperationFailure, "c:#{c.inspect} opts:#{opts.inspect} failed" unless response["ok"] == 1.0 || opts.fetch(:check_response, true) == false
|
|
356
|
+
ret << response
|
|
357
|
+
end
|
|
358
|
+
client.close
|
|
359
|
+
end
|
|
360
|
+
debug 3, "command ret:#{ret.inspect}"
|
|
361
|
+
ret.size == 1 ? ret.first : ret
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def repl_set_get_status
|
|
365
|
+
command( @config[:replicas], 'admin', { :replSetGetStatus => 1 }, {:check_response => false } )
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def repl_set_get_config
|
|
369
|
+
host, port = primary_name.split(":")
|
|
370
|
+
client = Mongo::MongoClient.new(host, port)
|
|
371
|
+
client['local']['system.replset'].find_one
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def repl_set_config
|
|
375
|
+
members = []
|
|
376
|
+
@config[:replicas].each{|s| members << { :_id => s[:_id], :host => "#{s[:host]}:#{s[:port]}", :tags => { :node => s[:_id].to_s } } }
|
|
377
|
+
@config[:arbiters].each{|s| members << { :_id => s[:_id], :host => "#{s[:host]}:#{s[:port]}", :arbiterOnly => true } }
|
|
378
|
+
{
|
|
379
|
+
:_id => @config[:replicas].first[:replSet],
|
|
380
|
+
:members => members
|
|
381
|
+
}
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def repl_set_initiate( cfg = nil )
|
|
385
|
+
command( @config[:replicas].first, 'admin', { :replSetInitiate => cfg || repl_set_config } )
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def repl_set_startup
|
|
389
|
+
states = nil
|
|
390
|
+
healthy = false
|
|
391
|
+
|
|
392
|
+
60.times do
|
|
393
|
+
# enter the thunderdome...
|
|
394
|
+
states = repl_set_get_status.zip(repl_set_is_master)
|
|
395
|
+
healthy = states.all? do |status, is_master|
|
|
396
|
+
# check replica set status for member list
|
|
397
|
+
next unless status['ok'] == 1.0 && (members = status['members'])
|
|
398
|
+
|
|
399
|
+
# ensure all replica set members are in a valid state
|
|
400
|
+
next unless members.all? { |m| [1,2,7].include?(m['state']) }
|
|
401
|
+
|
|
402
|
+
# check for primary replica set member
|
|
403
|
+
next unless (primary = members.find { |m| m['state'] == 1 })
|
|
404
|
+
|
|
405
|
+
# check replica set member optimes
|
|
406
|
+
primary_optime = primary['optime'].seconds
|
|
407
|
+
next unless primary_optime && members.all? do |m|
|
|
408
|
+
m['state'] == 7 || primary_optime - m['optime'].seconds < 5
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# check replica set state
|
|
412
|
+
case status['myState']
|
|
413
|
+
when 1
|
|
414
|
+
is_master['ismaster'] == true &&
|
|
415
|
+
is_master['secondary'] == false
|
|
416
|
+
when 2
|
|
417
|
+
is_master['ismaster'] == false &&
|
|
418
|
+
is_master['secondary'] == true
|
|
419
|
+
when 7
|
|
420
|
+
is_master['ismaster'] == false &&
|
|
421
|
+
is_master['secondary'] == false
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
return healthy if healthy
|
|
426
|
+
sleep(1)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
raise Mongo::OperationFailure,
|
|
430
|
+
"replSet startup failed - status: #{states.inspect}"
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def repl_set_seeds
|
|
434
|
+
@config[:replicas].collect{|node| "#{node[:host]}:#{node[:port]}"}
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def repl_set_seeds_old
|
|
438
|
+
@config[:replicas].collect{|node| [node[:host], node[:port]]}
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def repl_set_seeds_uri
|
|
442
|
+
repl_set_seeds.join(',')
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def repl_set_name
|
|
446
|
+
@config[:replicas].first[:replSet]
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def member_names_by_state(state)
|
|
450
|
+
states = Array(state)
|
|
451
|
+
# Any status with a REMOVED node won't have the full cluster state
|
|
452
|
+
status = repl_set_get_status.find {|status| status['members'].find {|m| m['state'] == 'REMOVED'}.nil?}
|
|
453
|
+
status['members'].find_all{|member| states.index(member['state']) }.collect{|member| member['name']}
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def primary_name
|
|
457
|
+
member_names_by_state(1).first
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def secondary_names
|
|
461
|
+
member_names_by_state(2)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def replica_names
|
|
465
|
+
member_names_by_state([1,2])
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def arbiter_names
|
|
469
|
+
member_names_by_state(7)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def members_by_name(names)
|
|
473
|
+
names.collect do |name|
|
|
474
|
+
member_by_name(name)
|
|
475
|
+
end.compact
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def member_by_name(name)
|
|
479
|
+
servers.find{|server| server.host_port == name}
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def primary
|
|
483
|
+
members_by_name([primary_name]).first
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def secondaries
|
|
487
|
+
members_by_name(secondary_names)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def stop_primary
|
|
491
|
+
primary.stop
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def stop_secondary
|
|
495
|
+
secondaries[rand(secondaries.length)].stop
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def replicas
|
|
499
|
+
members_by_name(replica_names)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def arbiters
|
|
503
|
+
members_by_name(arbiter_names)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def config_names_by_kind(kind)
|
|
507
|
+
@config[kind].collect{|conf| "#{conf[:host]}:#{conf[:port]}"}
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def shards
|
|
511
|
+
members_by_name(config_names_by_kind(:shards))
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def repl_set_reconfig(new_config)
|
|
515
|
+
new_config['version'] = repl_set_get_config['version'] + 1
|
|
516
|
+
command( primary, 'admin', { :replSetReconfig => new_config } )
|
|
517
|
+
repl_set_startup
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def repl_set_remove_node(state = [1,2])
|
|
521
|
+
names = member_names_by_state(state)
|
|
522
|
+
name = names[rand(names.length)]
|
|
523
|
+
|
|
524
|
+
@config[:replicas].delete_if{|node| "#{node[:host]}:#{node[:port]}" == name}
|
|
525
|
+
repl_set_reconfig(repl_set_config)
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def repl_set_add_node
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def configs
|
|
532
|
+
members_by_name(config_names_by_kind(:configs))
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def routers
|
|
536
|
+
members_by_name(config_names_by_kind(:routers))
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def mongos_seeds
|
|
540
|
+
config_names_by_kind(:routers)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def ismaster(servers)
|
|
544
|
+
command( servers, 'admin', { :ismaster => 1 } )
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def sharded_cluster_is_master
|
|
548
|
+
ismaster(@config[:routers])
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def repl_set_is_master
|
|
552
|
+
ismaster(@config[:replicas])
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def addshards(shards = @config[:shards])
|
|
556
|
+
command( @config[:routers].first, 'admin', Array(shards).collect{|s| { :addshard => "#{s[:host]}:#{s[:port]}" } } )
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def listshards
|
|
560
|
+
command( @config[:routers].first, 'admin', { :listshards => 1 } )
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def enablesharding( dbname )
|
|
564
|
+
command( @config[:routers].first, 'admin', { :enablesharding => dbname } )
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def shardcollection( namespace, key, unique = false )
|
|
568
|
+
command( @config[:routers].first, 'admin', { :shardcollection => namespace, :key => key, :unique => unique } )
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def mongos_discover # can also do @config[:routers] find but only want mongos for connections
|
|
572
|
+
(@config[:configs]).collect do |cmd_server|
|
|
573
|
+
client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
|
|
574
|
+
result = client['config']['mongos'].find.to_a
|
|
575
|
+
client.close
|
|
576
|
+
result
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def start
|
|
581
|
+
# Must start configs before mongos -- hash order not guaranteed on 1.8.X
|
|
582
|
+
servers(:configs).each{|server| server.start}
|
|
583
|
+
servers.each{|server| server.start}
|
|
584
|
+
# TODO - sharded replica sets - pending
|
|
585
|
+
if @config[:replicas]
|
|
586
|
+
repl_set_initiate if repl_set_get_status.first['startupStatus'] == 3
|
|
587
|
+
repl_set_startup
|
|
588
|
+
end
|
|
589
|
+
if @config[:routers]
|
|
590
|
+
addshards if listshards['shards'].size == 0
|
|
591
|
+
end
|
|
592
|
+
self
|
|
593
|
+
end
|
|
594
|
+
alias :restart :start
|
|
595
|
+
|
|
596
|
+
def stop
|
|
597
|
+
servers.each{|server| server.stop}
|
|
598
|
+
self
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def clobber
|
|
602
|
+
FileUtils.rm_rf @config[:dbpath]
|
|
603
|
+
self
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
end
|
|
608
|
+
end
|