mongo 1.6.4 → 1.7.0.rc0
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.
- data/README.md +13 -13
- data/Rakefile +7 -10
- data/docs/{GridFS.md → GRID_FS.md} +0 -0
- data/docs/HISTORY.md +16 -0
- data/docs/READ_PREFERENCE.md +70 -10
- data/docs/TUTORIAL.md +2 -2
- data/lib/mongo.rb +2 -0
- data/lib/mongo/collection.rb +62 -11
- data/lib/mongo/connection.rb +31 -41
- data/lib/mongo/cursor.rb +42 -86
- data/lib/mongo/db.rb +10 -8
- data/lib/mongo/networking.rb +30 -65
- data/lib/mongo/repl_set_connection.rb +91 -170
- data/lib/mongo/sharded_connection.rb +221 -0
- data/lib/mongo/util/node.rb +29 -36
- data/lib/mongo/util/pool.rb +10 -3
- data/lib/mongo/util/pool_manager.rb +77 -90
- data/lib/mongo/util/sharding_pool_manager.rb +143 -0
- data/lib/mongo/util/support.rb +22 -2
- data/lib/mongo/util/tcp_socket.rb +10 -15
- data/lib/mongo/util/uri_parser.rb +17 -10
- data/lib/mongo/version.rb +1 -1
- data/test/collection_test.rb +133 -1
- data/test/connection_test.rb +50 -4
- data/test/db_api_test.rb +3 -3
- data/test/db_test.rb +6 -1
- data/test/replica_sets/basic_test.rb +3 -6
- data/test/replica_sets/complex_connect_test.rb +14 -2
- data/test/replica_sets/complex_read_preference_test.rb +237 -0
- data/test/replica_sets/connect_test.rb +47 -67
- data/test/replica_sets/count_test.rb +1 -1
- data/test/replica_sets/cursor_test.rb +70 -0
- data/test/replica_sets/read_preference_test.rb +171 -118
- data/test/replica_sets/refresh_test.rb +3 -3
- data/test/replica_sets/refresh_with_threads_test.rb +2 -2
- data/test/replica_sets/rs_test_helper.rb +2 -2
- data/test/sharded_cluster/basic_test.rb +112 -0
- data/test/sharded_cluster/mongo_config_test.rb +126 -0
- data/test/sharded_cluster/sc_test_helper.rb +39 -0
- data/test/test_helper.rb +3 -3
- data/test/threading/threading_with_large_pool_test.rb +1 -1
- data/test/tools/mongo_config.rb +307 -0
- data/test/tools/repl_set_manager.rb +12 -12
- data/test/unit/collection_test.rb +1 -1
- data/test/unit/cursor_test.rb +11 -6
- data/test/unit/db_test.rb +4 -0
- data/test/unit/grid_test.rb +2 -0
- data/test/unit/read_test.rb +39 -8
- data/test/uri_test.rb +4 -8
- metadata +144 -127
@@ -5,10 +5,10 @@ require './test/tools/repl_set_manager'
|
|
5
5
|
class Test::Unit::TestCase
|
6
6
|
# Ensure replica set is available as an instance variable and that
|
7
7
|
# a new set is spun up for each TestCase class
|
8
|
-
def ensure_rs
|
8
|
+
def ensure_rs(opts={})
|
9
9
|
unless defined?(@@current_class) and @@current_class == self.class
|
10
10
|
@@current_class = self.class
|
11
|
-
@@rs = ReplSetManager.new
|
11
|
+
@@rs = ReplSetManager.new(opts)
|
12
12
|
@@rs.start_set
|
13
13
|
end
|
14
14
|
@rs = @@rs
|
@@ -0,0 +1,112 @@
|
|
1
|
+
$:.unshift(File.expand_path('../..', File.dirname(__FILE__)))
|
2
|
+
require 'test/sharded_cluster/sc_test_helper'
|
3
|
+
require 'test/tools/mongo_config'
|
4
|
+
|
5
|
+
class BasicTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def self.suite
|
8
|
+
s = super
|
9
|
+
def s.setup
|
10
|
+
|
11
|
+
end
|
12
|
+
def s.teardown
|
13
|
+
@@sc.stop
|
14
|
+
@@sc.clobber
|
15
|
+
end
|
16
|
+
def s.run(*args)
|
17
|
+
setup
|
18
|
+
super
|
19
|
+
teardown
|
20
|
+
end
|
21
|
+
s
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup
|
25
|
+
ensure_sc
|
26
|
+
end
|
27
|
+
|
28
|
+
def teardown
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO member.primary? ==> true
|
33
|
+
def test_connect
|
34
|
+
seeds = @sc.mongos_seeds
|
35
|
+
@con = Mongo::ShardedConnection.new(seeds)
|
36
|
+
assert @con.connected?
|
37
|
+
assert_equal(seeds.size, @con.seeds.size)
|
38
|
+
probe(seeds.size)
|
39
|
+
@con.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_hard_refresh
|
43
|
+
seeds = @sc.mongos_seeds
|
44
|
+
@con = Mongo::ShardedConnection.new(seeds)
|
45
|
+
assert @con.connected?
|
46
|
+
@con.hard_refresh!
|
47
|
+
assert @con.connected?
|
48
|
+
@con.close
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_reconnect
|
52
|
+
seeds = @sc.mongos_seeds
|
53
|
+
@con = Mongo::ShardedConnection.new(seeds)
|
54
|
+
assert @con.connected?
|
55
|
+
router = @sc.servers(:routers).first
|
56
|
+
router.stop
|
57
|
+
probe(seeds.size)
|
58
|
+
assert @con.connected?
|
59
|
+
@con.close
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_all_down
|
63
|
+
seeds = @sc.mongos_seeds
|
64
|
+
@con = Mongo::ShardedConnection.new(seeds)
|
65
|
+
assert @con.connected?
|
66
|
+
@sc.servers(:routers).each{|router| router.stop}
|
67
|
+
assert_raises Mongo::ConnectionFailure do
|
68
|
+
probe(seeds.size)
|
69
|
+
end
|
70
|
+
assert_false @con.connected?
|
71
|
+
@con.close
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_cycle
|
75
|
+
seeds = @sc.mongos_seeds
|
76
|
+
@con = Mongo::ShardedConnection.new(seeds)
|
77
|
+
assert @con.connected?
|
78
|
+
routers = @sc.servers(:routers)
|
79
|
+
while routers.size > 0 do
|
80
|
+
rescue_connection_failure do
|
81
|
+
probe(seeds.size)
|
82
|
+
end
|
83
|
+
probe(seeds.size)
|
84
|
+
#p @con.manager.primary
|
85
|
+
router = routers.detect{|r| r.port == @con.manager.primary.last}
|
86
|
+
routers.delete(router)
|
87
|
+
router.stop
|
88
|
+
end
|
89
|
+
assert_raises Mongo::ConnectionFailure do
|
90
|
+
probe(seeds.size)
|
91
|
+
end
|
92
|
+
assert_false @con.connected?
|
93
|
+
routers = @sc.servers(:routers).reverse
|
94
|
+
routers.each do |r|
|
95
|
+
r.start
|
96
|
+
@con.hard_refresh!
|
97
|
+
#p @con.manager.primary
|
98
|
+
rescue_connection_failure do
|
99
|
+
probe(seeds.size)
|
100
|
+
end
|
101
|
+
probe(seeds.size)
|
102
|
+
end
|
103
|
+
@con.close
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def probe(size)
|
109
|
+
assert_equal(size, @con['config']['mongos'].find.to_a.size)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', File.dirname(__FILE__))).unshift(File.expand_path('../..', File.dirname(__FILE__)))
|
2
|
+
require 'test-unit'
|
3
|
+
require 'test/tools/mongo_config'
|
4
|
+
|
5
|
+
class MongoConfig < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def self.suite
|
8
|
+
s = super
|
9
|
+
def s.setup
|
10
|
+
|
11
|
+
end
|
12
|
+
def s.teardown
|
13
|
+
|
14
|
+
end
|
15
|
+
def s.run(*args)
|
16
|
+
setup
|
17
|
+
super
|
18
|
+
teardown
|
19
|
+
end
|
20
|
+
s
|
21
|
+
end
|
22
|
+
|
23
|
+
test "config defaults" do
|
24
|
+
[ Mongo::Config::DEFAULT_BASE_OPTS,
|
25
|
+
Mongo::Config::DEFAULT_REPLICA_SET,
|
26
|
+
Mongo::Config::DEFAULT_SHARDED_SIMPLE,
|
27
|
+
Mongo::Config::DEFAULT_SHARDED_REPLICA
|
28
|
+
].each do |params|
|
29
|
+
config = Mongo::Config.cluster(params)
|
30
|
+
assert(config.size > 0)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
test "get available port" do
|
35
|
+
assert_not_nil(Mongo::Config.get_available_port)
|
36
|
+
end
|
37
|
+
|
38
|
+
test "SysProc start" do
|
39
|
+
cmd = "true"
|
40
|
+
sys_proc = Mongo::Config::SysProc.new(cmd)
|
41
|
+
assert_equal(cmd, sys_proc.cmd)
|
42
|
+
assert_nil(sys_proc.pid)
|
43
|
+
assert_not_nil(sys_proc.start(0))
|
44
|
+
assert_not_nil(sys_proc.pid)
|
45
|
+
end
|
46
|
+
|
47
|
+
test "SysProc wait" do
|
48
|
+
sys_proc = Mongo::Config::SysProc.new("true")
|
49
|
+
assert_not_nil(sys_proc.start(0))
|
50
|
+
assert(sys_proc.running?)
|
51
|
+
sys_proc.wait
|
52
|
+
assert(!sys_proc.running?)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "SysProc kill" do
|
56
|
+
sys_proc = Mongo::Config::SysProc.new("true")
|
57
|
+
assert_not_nil(sys_proc.start(0))
|
58
|
+
sys_proc.kill
|
59
|
+
sys_proc.wait
|
60
|
+
assert(!sys_proc.running?)
|
61
|
+
end
|
62
|
+
|
63
|
+
test "SysProc stop" do
|
64
|
+
sys_proc = Mongo::Config::SysProc.new("true")
|
65
|
+
assert_not_nil(sys_proc.start(0))
|
66
|
+
sys_proc.stop
|
67
|
+
assert(!sys_proc.running?)
|
68
|
+
end
|
69
|
+
|
70
|
+
test "Server" do
|
71
|
+
server = Mongo::Config::Server.new('a cmd', 'host', 1234)
|
72
|
+
assert_equal('a cmd', server.cmd)
|
73
|
+
assert_equal('host', server.host)
|
74
|
+
assert_equal(1234, server.port)
|
75
|
+
end
|
76
|
+
|
77
|
+
test "DbServer" do
|
78
|
+
config = Mongo::Config::DEFAULT_BASE_OPTS
|
79
|
+
server = Mongo::Config::DbServer.new(config)
|
80
|
+
assert_equal(config, server.config)
|
81
|
+
assert_equal("mongod --logpath data/log --dbpath data", server.cmd)
|
82
|
+
assert_equal(config[:host], server.host)
|
83
|
+
assert_equal(config[:port], server.port)
|
84
|
+
end
|
85
|
+
|
86
|
+
def cluster_test(opts)
|
87
|
+
#debug 1, opts.inspect
|
88
|
+
config = Mongo::Config.cluster(opts)
|
89
|
+
#debug 1, config.inspect
|
90
|
+
manager = Mongo::Config::ClusterManager.new(config)
|
91
|
+
assert_equal(config, manager.config)
|
92
|
+
manager.start
|
93
|
+
manager.servers.each{|s| p s}
|
94
|
+
manager.stop
|
95
|
+
manager.servers.each{|s| assert_equal(false, s.running?)}
|
96
|
+
manager.clobber
|
97
|
+
end
|
98
|
+
|
99
|
+
test "cluster manager base" do
|
100
|
+
#cluster_test(Mongo::Config::DEFAULT_BASE_OPTS)
|
101
|
+
end
|
102
|
+
|
103
|
+
test "cluster manager replica set" do
|
104
|
+
#cluster_test(Mongo::Config::DEFAULT_REPLICA_SET)
|
105
|
+
end
|
106
|
+
|
107
|
+
test "cluster manager sharded simple" do
|
108
|
+
#manager = Mongo::Config::ClusterManager.new(Mongo::Config.cluster(Mongo::Config::DEFAULT_SHARDED_SIMPLE)).start
|
109
|
+
opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE
|
110
|
+
#debug 1, opts.inspect
|
111
|
+
config = Mongo::Config.cluster(opts)
|
112
|
+
#debug 1, config.inspect
|
113
|
+
manager = Mongo::Config::ClusterManager.new(config)
|
114
|
+
assert_equal(config, manager.config)
|
115
|
+
manager.start
|
116
|
+
#debug 1, manager.ismaster
|
117
|
+
#debug 1, manager.mongos_discover
|
118
|
+
manager.stop.clobber
|
119
|
+
end
|
120
|
+
|
121
|
+
test "cluster manager sharded replica" do
|
122
|
+
#cluster_test(Mongo::Config::DEFAULT_SHARDED_REPLICA)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', File.dirname(__FILE__)))
|
2
|
+
require File.expand_path("../../test_helper", __FILE__)
|
3
|
+
require 'test/tools/mongo_config'
|
4
|
+
|
5
|
+
class Test::Unit::TestCase
|
6
|
+
# Ensure sharded cluster is available as an instance variable and that
|
7
|
+
# a new set is spun up for each TestCase class
|
8
|
+
def ensure_sc
|
9
|
+
if defined?(@@current_class) and @@current_class == self.class
|
10
|
+
@@sc.start
|
11
|
+
else
|
12
|
+
@@current_class = self.class
|
13
|
+
dbpath = 'sc'
|
14
|
+
opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE.merge(:dbpath => dbpath).merge(:routers => 4)
|
15
|
+
#debug 1, opts
|
16
|
+
config = Mongo::Config.cluster(opts)
|
17
|
+
#debug 1, config
|
18
|
+
@@sc = Mongo::Config::ClusterManager.new(config)
|
19
|
+
@@sc.start
|
20
|
+
end
|
21
|
+
@sc = @@sc
|
22
|
+
end
|
23
|
+
|
24
|
+
# Generic code for rescuing connection failures and retrying operations.
|
25
|
+
# This could be combined with some timeout functionality.
|
26
|
+
def rescue_connection_failure(max_retries=30)
|
27
|
+
retries = 0
|
28
|
+
begin
|
29
|
+
yield
|
30
|
+
rescue Mongo::ConnectionFailure => ex
|
31
|
+
#puts "Rescue attempt #{retries}: from #{ex}"
|
32
|
+
retries += 1
|
33
|
+
raise ex if retries > max_retries
|
34
|
+
sleep(2)
|
35
|
+
retry
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
-
require 'rubygems'
|
2
|
+
require 'rubygems'
|
3
3
|
require 'mongo'
|
4
4
|
gem 'test-unit'
|
5
5
|
require 'test/unit'
|
@@ -93,7 +93,7 @@ class Test::Unit::TestCase
|
|
93
93
|
Object.new
|
94
94
|
end
|
95
95
|
|
96
|
-
def assert_raise_error(klass, message)
|
96
|
+
def assert_raise_error(klass, message=nil)
|
97
97
|
begin
|
98
98
|
yield
|
99
99
|
rescue => e
|
@@ -101,7 +101,7 @@ class Test::Unit::TestCase
|
|
101
101
|
flunk "Expected exception class #{klass} but got #{e.class}.\n #{e.backtrace}"
|
102
102
|
end
|
103
103
|
|
104
|
-
if !e.message.include?(message)
|
104
|
+
if message && !e.message.include?(message)
|
105
105
|
p e.backtrace
|
106
106
|
flunk "#{e.message} does not include #{message}.\n#{e.backtrace}"
|
107
107
|
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
$debug_level = 2
|
7
|
+
STDOUT.sync = true
|
8
|
+
|
9
|
+
unless defined? Mongo
|
10
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'mongo')
|
11
|
+
end
|
12
|
+
|
13
|
+
def debug(level, arg)
|
14
|
+
if level <= $debug_level
|
15
|
+
file_line = caller[0][/(.*:\d+):/, 1]
|
16
|
+
calling_method = caller[0][/`([^']*)'/, 1]
|
17
|
+
puts "#{file_line}:#{calling_method}:#{arg.class == String ? arg : arg.inspect}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Mongo
|
22
|
+
class Config
|
23
|
+
DEFAULT_BASE_OPTS = { :host => 'localhost', :logpath => 'data/log', :dbpath => 'data' }
|
24
|
+
DEFAULT_REPLICA_SET = DEFAULT_BASE_OPTS.merge( :replicas => 3 )
|
25
|
+
DEFAULT_SHARDED_SIMPLE = DEFAULT_BASE_OPTS.merge( :shards => 2, :configs => 1, :routers => 2 )
|
26
|
+
DEFAULT_SHARDED_REPLICA = DEFAULT_SHARDED_SIMPLE.merge( :replicas => 3 )
|
27
|
+
|
28
|
+
SERVER_PRELUDE_KEYS = [:host, :command]
|
29
|
+
SHARDING_OPT_KEYS = [:shards, :configs, :routers]
|
30
|
+
REPLICA_OPT_KEYS = [:replicas]
|
31
|
+
MONGODS_OPT_KEYS = [:mongods]
|
32
|
+
CLUSTER_OPT_KEYS = SHARDING_OPT_KEYS + REPLICA_OPT_KEYS + MONGODS_OPT_KEYS
|
33
|
+
|
34
|
+
DEFAULT_VERIFIES = 60
|
35
|
+
BASE_PORT = 3000
|
36
|
+
@@port = BASE_PORT
|
37
|
+
|
38
|
+
def self.configdb(config)
|
39
|
+
config[:configs].collect{|c|"#{c[:host]}:#{c[:port]}"}.join(' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.cluster(opts = DEFAULT_SHARDED_SIMPLE)
|
43
|
+
raise "missing required option" if [:host, :dbpath].any?{|k| !opts[k]}
|
44
|
+
config = opts.reject{|k,v| CLUSTER_OPT_KEYS.include?(k)}
|
45
|
+
keys = SHARDING_OPT_KEYS.any?{|k| opts[k]} ? SHARDING_OPT_KEYS : nil
|
46
|
+
keys ||= REPLICA_OPT_KEYS.any?{|k| opts[k]} ? REPLICA_OPT_KEYS : nil
|
47
|
+
keys ||= MONGODS_OPT_KEYS
|
48
|
+
keys.each do |key|
|
49
|
+
config[key] = opts.fetch(key,1).times.collect do |i| #default to 1 of whatever
|
50
|
+
server_base = key.to_s.chop
|
51
|
+
dbpath = "#{opts[:dbpath]}/#{server_base}#{i}"
|
52
|
+
logpath = "#{dbpath}/#{server_base}.log"
|
53
|
+
if key == :shards && opts[:replicas]
|
54
|
+
self.cluster(opts.reject{|k,v| SHARDING_OPT_KEYS.include?(k)}.merge(:dbpath => dbpath))
|
55
|
+
else
|
56
|
+
server_params = { :host => opts[:host], :port => self.get_available_port, :logpath => logpath }
|
57
|
+
case key
|
58
|
+
when :replicas; server_params.merge!( :command => 'mongod', :dbpath => dbpath, :replSet => File.basename(opts[:dbpath]) )
|
59
|
+
when :configs; server_params.merge!( :command => 'mongod', :dbpath => dbpath, :configsvr => nil )
|
60
|
+
when :routers; server_params.merge!( :command => 'mongos', :configdb => self.configdb(config) ) # mongos, NO dbpath
|
61
|
+
else server_params.merge!( :command => 'mongod', :dbpath => dbpath ) # :mongods, :shards
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
config
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.port_available?(port)
|
70
|
+
ret = false
|
71
|
+
socket = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
|
72
|
+
sockaddr = Socket.sockaddr_in(port, '0.0.0.0')
|
73
|
+
begin
|
74
|
+
socket.bind(sockaddr)
|
75
|
+
ret = true
|
76
|
+
rescue Exception
|
77
|
+
end
|
78
|
+
socket.close
|
79
|
+
ret
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.get_available_port
|
83
|
+
while true
|
84
|
+
port = @@port
|
85
|
+
@@port += 1
|
86
|
+
break if port_available?(port)
|
87
|
+
end
|
88
|
+
port
|
89
|
+
end
|
90
|
+
|
91
|
+
class SysProc
|
92
|
+
attr_reader :pid, :cmd
|
93
|
+
|
94
|
+
def initialize(cmd = nil)
|
95
|
+
@pid = nil
|
96
|
+
@cmd = cmd
|
97
|
+
end
|
98
|
+
|
99
|
+
def start(verifies = 0)
|
100
|
+
return @pid if running?
|
101
|
+
begin
|
102
|
+
@pid = fork do
|
103
|
+
STDIN.reopen '/dev/null'
|
104
|
+
STDOUT.reopen '/dev/null', 'a'
|
105
|
+
STDERR.reopen STDOUT
|
106
|
+
exec cmd # spawn(@cmd, [:in, :out, :err] => :close) #
|
107
|
+
end
|
108
|
+
verify(verifies) if verifies > 0
|
109
|
+
@pid
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def stop
|
114
|
+
kill
|
115
|
+
wait
|
116
|
+
end
|
117
|
+
|
118
|
+
def kill
|
119
|
+
begin
|
120
|
+
@pid && Process.kill(3, @pid) && true
|
121
|
+
rescue Errno::ESRCH
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def wait
|
127
|
+
Process.wait(@pid) if @pid
|
128
|
+
@pid = nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def running?
|
132
|
+
begin
|
133
|
+
@pid && Process.kill(0, @pid) && true
|
134
|
+
rescue Errno::ESRCH
|
135
|
+
false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def verify(verifies = DEFAULT_VERIFIES)
|
140
|
+
verifies.times do |i|
|
141
|
+
return @pid if running?
|
142
|
+
sleep 1
|
143
|
+
end
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class Server < SysProc
|
149
|
+
attr_reader :host, :port
|
150
|
+
|
151
|
+
def initialize(cmd = nil, host = nil, port = nil)
|
152
|
+
super(cmd)
|
153
|
+
@host = host
|
154
|
+
@port = port
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class DbServer < Server
|
159
|
+
attr_accessor :config
|
160
|
+
def initialize(config)
|
161
|
+
@config = config
|
162
|
+
dbpath = @config[:dbpath]
|
163
|
+
[dbpath, File.dirname(@config[:logpath])].compact.each{|dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
|
164
|
+
command = @config[:command] || 'mongod'
|
165
|
+
arguments = @config.reject{|k,v| SERVER_PRELUDE_KEYS.include?(k)}
|
166
|
+
cmd = [command, arguments.collect{|k,v| ['--' + k.to_s, v ]}].flatten.join(' ')
|
167
|
+
super(cmd, @config[:host], @config[:port])
|
168
|
+
end
|
169
|
+
|
170
|
+
def start(verifies = DEFAULT_VERIFIES)
|
171
|
+
super(verifies)
|
172
|
+
verify(verifies)
|
173
|
+
end
|
174
|
+
|
175
|
+
def verify(verifies = 10)
|
176
|
+
verifies.times do |i|
|
177
|
+
#puts "DbServer.verify - port: #{@port} iteration: #{i}"
|
178
|
+
begin
|
179
|
+
raise Mongo::ConnectionFailure unless running?
|
180
|
+
Mongo::Connection.new(@host, @port).close
|
181
|
+
#puts "DbServer.verified via connection - port: #{@port} iteration: #{i}"
|
182
|
+
return @pid
|
183
|
+
rescue Mongo::ConnectionFailure
|
184
|
+
sleep 1
|
185
|
+
end
|
186
|
+
end
|
187
|
+
raise Mongo::ConnectionFailure, "DbServer.start verification via connection failed - port: #{@port}"
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
class ClusterManager
|
193
|
+
attr_reader :config
|
194
|
+
def initialize(config)
|
195
|
+
@config = config
|
196
|
+
@servers = {}
|
197
|
+
Mongo::Config::CLUSTER_OPT_KEYS.each do |key|
|
198
|
+
@servers[key] = @config[key].collect{|conf| DbServer.new(conf)} if @config[key]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def servers(key = nil)
|
203
|
+
@servers.collect{|k,v| (!key || key == k) ? v : nil}.flatten.compact
|
204
|
+
end
|
205
|
+
|
206
|
+
def command( cmd_servers, db_name, cmd, opts = {} )
|
207
|
+
ret = []
|
208
|
+
cmd = cmd.class == Array ? cmd : [ cmd ]
|
209
|
+
debug 3, "ClusterManager.command cmd:#{cmd.inspect}"
|
210
|
+
cmd_servers = cmd_servers.class == Array ? cmd_servers : [cmd_servers]
|
211
|
+
cmd_servers.each do |cmd_server|
|
212
|
+
debug 3, cmd_server.inspect
|
213
|
+
conn = Mongo::Connection.new(cmd_server[:host], cmd_server[:port])
|
214
|
+
cmd.each do |c|
|
215
|
+
debug 3, "ClusterManager.command c:#{c.inspect}"
|
216
|
+
response = conn[db_name].command( c, opts )
|
217
|
+
debug 3, "ClusterManager.command response:#{response.inspect}"
|
218
|
+
raise Mongo::OperationFailure, "c:#{c.inspect} opts:#{opts.inspect} failed" unless response["ok"] == 1.0 || opts.fetch(:check_response, true) == false
|
219
|
+
ret << response
|
220
|
+
end
|
221
|
+
conn.close
|
222
|
+
end
|
223
|
+
debug 3, "command ret:#{ret.inspect}"
|
224
|
+
ret.size == 1 ? ret.first : ret
|
225
|
+
end
|
226
|
+
|
227
|
+
def repl_set_get_status
|
228
|
+
command( @config[:replicas].first, 'admin', { :replSetGetStatus => 1 }, {:check_response => false } )
|
229
|
+
end
|
230
|
+
|
231
|
+
def repl_set_initiate( cfg = nil )
|
232
|
+
cfg ||= {
|
233
|
+
:_id => @config[:replicas].first[:replSet],
|
234
|
+
:members => @config[:replicas].each_with_index.collect{|s, i| { :_id => i, :host => "#{s[:host]}:#{s[:port]}" } },
|
235
|
+
}
|
236
|
+
command( @config[:replicas].first, 'admin', { :replSetInitiate => cfg } )
|
237
|
+
end
|
238
|
+
|
239
|
+
def repl_set_startup
|
240
|
+
response = nil
|
241
|
+
60.times do |i|
|
242
|
+
break if (response = repl_set_get_status)['ok'] == 1.0
|
243
|
+
sleep 1
|
244
|
+
end
|
245
|
+
raise Mongo::OperationFailure, "replSet startup failed - status: #{repsonse.inspect}" unless response && response['ok'] == 1.0
|
246
|
+
response
|
247
|
+
end
|
248
|
+
|
249
|
+
def mongos_seeds
|
250
|
+
@config[:routers].collect{|router| "#{router[:host]}:#{router[:port]}"}
|
251
|
+
end
|
252
|
+
|
253
|
+
def ismaster
|
254
|
+
command( @config[:routers], 'admin', { :ismaster => 1 } )
|
255
|
+
end
|
256
|
+
|
257
|
+
def addshards(shards = @config[:shards])
|
258
|
+
command( @config[:routers].first, 'admin', Array(shards).collect{|s| { :addshard => "#{s[:host]}:#{s[:port]}" } } )
|
259
|
+
end
|
260
|
+
|
261
|
+
def listshards
|
262
|
+
command( @config[:routers].first, 'admin', { :listshards => 1 } )
|
263
|
+
end
|
264
|
+
|
265
|
+
def enablesharding( dbname )
|
266
|
+
command( @config[:routers].first, 'admin', { :enablesharding => dbname } )
|
267
|
+
end
|
268
|
+
|
269
|
+
def shardcollection( namespace, key, unique = false )
|
270
|
+
command( @config[:routers].first, 'admin', { :shardcollection => namespace, :key => key, :unique => unique } )
|
271
|
+
end
|
272
|
+
|
273
|
+
def mongos_discover # can also do @config[:routers] find but only want mongos for connections
|
274
|
+
(@config[:configs]).collect do |cmd_server|
|
275
|
+
conn = Mongo::Connection.new(cmd_server[:host], cmd_server[:port])
|
276
|
+
result = conn['config']['mongos'].find.to_a
|
277
|
+
conn.close
|
278
|
+
result
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def start
|
283
|
+
servers.each{|server| server.start}
|
284
|
+
# TODO - sharded replica sets - pending
|
285
|
+
if @config[:replicas]
|
286
|
+
repl_set_initiate if repl_set_get_status['startupStatus'] == 3
|
287
|
+
repl_set_startup
|
288
|
+
end
|
289
|
+
if @config[:routers]
|
290
|
+
addshards if listshards['shards'].size == 0
|
291
|
+
end
|
292
|
+
self
|
293
|
+
end
|
294
|
+
|
295
|
+
def stop
|
296
|
+
servers.each{|server| server.stop}
|
297
|
+
self
|
298
|
+
end
|
299
|
+
|
300
|
+
def clobber
|
301
|
+
system "rm -fr #{@config[:dbpath]}"
|
302
|
+
self
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
end
|