mongo 1.1.5 → 1.3.0
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 +15 -15
- data/Rakefile +38 -17
- data/docs/FAQ.md +4 -0
- data/docs/HISTORY.md +59 -0
- data/docs/RELEASES.md +33 -0
- data/docs/REPLICA_SETS.md +13 -16
- data/lib/mongo/collection.rb +157 -69
- data/lib/mongo/connection.rb +189 -65
- data/lib/mongo/cursor.rb +43 -29
- data/lib/mongo/db.rb +63 -43
- data/lib/mongo/exceptions.rb +4 -1
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/gridfs/grid_ext.rb +1 -1
- data/lib/mongo/gridfs/grid_file_system.rb +1 -1
- data/lib/mongo/gridfs/grid_io.rb +89 -8
- data/lib/mongo/gridfs/grid_io_fix.rb +1 -1
- data/lib/mongo/repl_set_connection.rb +72 -20
- data/lib/mongo/test.rb +20 -0
- data/lib/mongo/util/conversions.rb +1 -1
- data/lib/mongo/util/core_ext.rb +11 -1
- data/lib/mongo/util/pool.rb +67 -15
- data/lib/mongo/util/server_version.rb +1 -1
- data/lib/mongo/util/support.rb +1 -1
- data/lib/mongo/util/uri_parser.rb +127 -13
- data/lib/mongo.rb +38 -2
- data/test/async/collection_test.rb +224 -0
- data/test/async/connection_test.rb +24 -0
- data/test/async/cursor_test.rb +162 -0
- data/test/async/worker_pool_test.rb +99 -0
- data/test/auxillary/fork_test.rb +30 -0
- data/test/auxillary/repl_set_auth_test.rb +58 -0
- data/test/auxillary/threaded_authentication_test.rb +101 -0
- data/test/bson/bson_test.rb +140 -28
- data/test/bson/byte_buffer_test.rb +18 -0
- data/test/bson/object_id_test.rb +14 -1
- data/test/bson/ordered_hash_test.rb +7 -0
- data/test/bson/timestamp_test.rb +24 -0
- data/test/collection_test.rb +104 -15
- data/test/connection_test.rb +78 -2
- data/test/conversions_test.rb +10 -11
- data/test/cursor_fail_test.rb +1 -1
- data/test/cursor_message_test.rb +1 -1
- data/test/cursor_test.rb +33 -4
- data/test/db_api_test.rb +30 -52
- data/test/db_test.rb +3 -3
- data/test/grid_file_system_test.rb +0 -1
- data/test/grid_io_test.rb +72 -1
- data/test/grid_test.rb +16 -16
- data/test/load/resque/load.rb +21 -0
- data/test/load/resque/processor.rb +26 -0
- data/test/load/thin/load.rb +24 -0
- data/test/load/unicorn/load.rb +23 -0
- data/test/load/unicorn/unicorn.rb +29 -0
- data/test/replica_sets/connect_test.rb +11 -1
- data/test/replica_sets/connection_string_test.rb +32 -0
- data/test/replica_sets/query_secondaries.rb +16 -0
- data/test/replica_sets/query_test.rb +10 -0
- data/test/replica_sets/replication_ack_test.rb +2 -0
- data/test/replica_sets/rs_test_helper.rb +9 -11
- data/test/support/hash_with_indifferent_access.rb +0 -13
- data/test/support_test.rb +0 -1
- data/test/test_helper.rb +27 -8
- data/test/tools/auth_repl_set_manager.rb +14 -0
- data/test/tools/load.rb +58 -0
- data/test/tools/repl_set_manager.rb +34 -9
- data/test/tools/sharding_manager.rb +202 -0
- data/test/tools/test.rb +3 -12
- data/test/unit/collection_test.rb +20 -24
- data/test/unit/connection_test.rb +4 -18
- data/test/unit/cursor_test.rb +16 -6
- data/test/unit/db_test.rb +10 -11
- data/test/unit/repl_set_connection_test.rb +0 -23
- data/test/unit/safe_test.rb +3 -3
- data/test/uri_test.rb +91 -0
- metadata +49 -12
- data/docs/1.0_UPGRADE.md +0 -21
|
@@ -16,8 +16,8 @@ class ConnectTest < Test::Unit::TestCase
|
|
|
16
16
|
|
|
17
17
|
def test_connect_with_deprecated_multi
|
|
18
18
|
@conn = Connection.multi([[RS.host, RS.ports[0]], [RS.host, RS.ports[1]]], :name => RS.name)
|
|
19
|
-
assert @conn.connected?
|
|
20
19
|
assert @conn.is_a?(ReplSetConnection)
|
|
20
|
+
assert @conn.connected?
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def test_connect_bad_name
|
|
@@ -31,12 +31,22 @@ class ConnectTest < Test::Unit::TestCase
|
|
|
31
31
|
@conn = ReplSetConnection.new([RS.host, RS.ports[0]], [RS.host, RS.ports[1]],
|
|
32
32
|
[RS.host, RS.ports[2]], :name => RS.name)
|
|
33
33
|
assert @conn.connected?
|
|
34
|
+
assert @conn.read_primary?
|
|
35
|
+
assert @conn.primary?
|
|
34
36
|
|
|
35
37
|
assert_equal RS.primary, @conn.primary
|
|
36
38
|
assert_equal RS.secondaries.sort, @conn.secondaries.sort
|
|
37
39
|
assert_equal RS.arbiters.sort, @conn.arbiters.sort
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
def test_host_port_accessors
|
|
43
|
+
@conn = ReplSetConnection.new([RS.host, RS.ports[0]], [RS.host, RS.ports[1]],
|
|
44
|
+
[RS.host, RS.ports[2]], :name => RS.name)
|
|
45
|
+
|
|
46
|
+
assert_equal @conn.host, RS.primary[0]
|
|
47
|
+
assert_equal @conn.port, RS.primary[1]
|
|
48
|
+
end
|
|
49
|
+
|
|
40
50
|
def test_connect_with_primary_node_killed
|
|
41
51
|
node = RS.kill_primary
|
|
42
52
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
+
require './test/replica_sets/rs_test_helper'
|
|
3
|
+
|
|
4
|
+
# NOTE: This test expects a replica set of three nodes to be running on RS.host,
|
|
5
|
+
# on ports TEST_PORT, RS.ports[1], and TEST + 2.
|
|
6
|
+
class ConnectionStringTest < Test::Unit::TestCase
|
|
7
|
+
include Mongo
|
|
8
|
+
|
|
9
|
+
def setup
|
|
10
|
+
RS.restart_killed_nodes
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def teardown
|
|
14
|
+
RS.restart_killed_nodes
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_connect_with_connection_string
|
|
18
|
+
@conn = Connection.from_uri("mongodb://#{RS.host}:#{RS.ports[0]},#{RS.host}:#{RS.ports[1]}?replicaset=#{RS.name}")
|
|
19
|
+
assert @conn.is_a?(ReplSetConnection)
|
|
20
|
+
assert @conn.connected?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_connect_with_full_connection_string
|
|
24
|
+
@conn = Connection.from_uri("mongodb://#{RS.host}:#{RS.ports[0]},#{RS.host}:#{RS.ports[1]}?replicaset=#{RS.name};safe=true;w=2;fsync=true;slaveok=true")
|
|
25
|
+
assert @conn.is_a?(ReplSetConnection)
|
|
26
|
+
assert @conn.connected?
|
|
27
|
+
assert_equal 2, @conn.safe[:w]
|
|
28
|
+
assert @conn.safe[:fsync]
|
|
29
|
+
assert @conn.read_pool
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -16,6 +16,13 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
|
|
|
16
16
|
RS.restart_killed_nodes
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
def test_read_primary
|
|
20
|
+
rescue_connection_failure do
|
|
21
|
+
assert !@conn.read_primary?
|
|
22
|
+
assert !@conn.primary?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
19
26
|
def test_con
|
|
20
27
|
assert @conn.primary_pool, "No primary pool!"
|
|
21
28
|
assert @conn.read_pool, "No read pool!"
|
|
@@ -54,6 +61,12 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
|
|
|
54
61
|
# Should still be able to read immediately after killing master node
|
|
55
62
|
RS.kill_primary
|
|
56
63
|
assert_equal 2, @coll.find.to_a.length
|
|
64
|
+
rescue_connection_failure do
|
|
65
|
+
@coll.save({:a => 50}, :safe => {:w => 2, :wtimeout => 10000})
|
|
66
|
+
end
|
|
67
|
+
RS.restart_killed_nodes
|
|
68
|
+
@coll.save({:a => 50}, :safe => {:w => 2, :wtimeout => 10000})
|
|
69
|
+
assert_equal 4, @coll.find.to_a.length
|
|
57
70
|
end
|
|
58
71
|
|
|
59
72
|
def test_kill_secondary
|
|
@@ -66,6 +79,7 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
|
|
|
66
79
|
RS.kill(read_node)
|
|
67
80
|
|
|
68
81
|
# Should fail immediately on next read
|
|
82
|
+
old_read_pool_port = @conn.read_pool.port
|
|
69
83
|
assert_raise ConnectionFailure do
|
|
70
84
|
@coll.find.to_a.length
|
|
71
85
|
end
|
|
@@ -75,6 +89,8 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
|
|
|
75
89
|
length = @coll.find.to_a.length
|
|
76
90
|
assert_equal 2, length
|
|
77
91
|
end
|
|
92
|
+
new_read_pool_port = @conn.read_pool.port
|
|
93
|
+
assert old_read_pool != new_read_pool
|
|
78
94
|
end
|
|
79
95
|
|
|
80
96
|
end
|
|
@@ -27,6 +27,8 @@ class ReplicaSetQueryTest < Test::Unit::TestCase
|
|
|
27
27
|
assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
puts "Benchmark before failover: #{benchmark_queries}"
|
|
31
|
+
|
|
30
32
|
RS.kill_primary
|
|
31
33
|
|
|
32
34
|
results = []
|
|
@@ -35,7 +37,15 @@ class ReplicaSetQueryTest < Test::Unit::TestCase
|
|
|
35
37
|
[20, 30, 40].each do |a|
|
|
36
38
|
assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
|
|
37
39
|
end
|
|
40
|
+
|
|
41
|
+
puts "Benchmark after failover: #{benchmark_queries}"
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
|
|
45
|
+
def benchmark_queries
|
|
46
|
+
t1 = Time.now
|
|
47
|
+
10000.times { @coll.find_one }
|
|
48
|
+
Time.now - t1
|
|
49
|
+
end
|
|
50
|
+
|
|
41
51
|
end
|
|
@@ -13,6 +13,8 @@ class ReplicaSetAckTest < Test::Unit::TestCase
|
|
|
13
13
|
@slave1 = Connection.new(@conn.secondary_pools[0].host,
|
|
14
14
|
@conn.secondary_pools[0].port, :slave_ok => true)
|
|
15
15
|
|
|
16
|
+
assert !@slave1.read_primary?
|
|
17
|
+
|
|
16
18
|
@db = @conn.db(MONGO_TEST_DB)
|
|
17
19
|
@db.drop_collection("test-sets")
|
|
18
20
|
@col = @db.collection("test-sets")
|
|
@@ -12,17 +12,15 @@ class Test::Unit::TestCase
|
|
|
12
12
|
# Generic code for rescuing connection failures and retrying operations.
|
|
13
13
|
# This could be combined with some timeout functionality.
|
|
14
14
|
def rescue_connection_failure(max_retries=60)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
sleep(1)
|
|
25
|
-
end
|
|
15
|
+
retries = 0
|
|
16
|
+
begin
|
|
17
|
+
yield
|
|
18
|
+
rescue Mongo::ConnectionFailure => ex
|
|
19
|
+
puts "Rescue attempt #{retries}: from #{ex}"
|
|
20
|
+
retries += 1
|
|
21
|
+
raise ex if retries > max_retries
|
|
22
|
+
sleep(1)
|
|
23
|
+
retry
|
|
26
24
|
end
|
|
27
25
|
end
|
|
28
26
|
|
|
@@ -38,19 +38,6 @@ class Hash
|
|
|
38
38
|
|
|
39
39
|
alias_method :to_options, :symbolize_keys
|
|
40
40
|
#alias_method :to_options!, :symbolize_keys!
|
|
41
|
-
|
|
42
|
-
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
|
|
43
|
-
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
|
|
44
|
-
# as keys, this will fail.
|
|
45
|
-
#
|
|
46
|
-
# ==== Examples
|
|
47
|
-
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
|
|
48
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
|
|
49
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
|
50
|
-
def assert_valid_keys(*valid_keys)
|
|
51
|
-
unknown_keys = keys - [valid_keys].flatten
|
|
52
|
-
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
|
53
|
-
end
|
|
54
41
|
end
|
|
55
42
|
|
|
56
43
|
module ActiveSupport
|
data/test/support_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
-
require 'rubygems' if ENV['C_EXT']
|
|
2
|
+
require 'rubygems' if RUBY_VERSION < '1.9.0' && ENV['C_EXT']
|
|
3
3
|
require 'mongo'
|
|
4
4
|
require 'test/unit'
|
|
5
5
|
|
|
6
|
+
def silently
|
|
7
|
+
warn_level = $VERBOSE
|
|
8
|
+
$VERBOSE = nil
|
|
9
|
+
result = yield
|
|
10
|
+
$VERBOSE = warn_level
|
|
11
|
+
result
|
|
12
|
+
end
|
|
13
|
+
|
|
6
14
|
begin
|
|
7
|
-
require 'rubygems'
|
|
8
|
-
require 'shoulda'
|
|
9
|
-
require 'mocha'
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
require 'rubygems' if RUBY_VERSION < "1.9.0" && !ENV['C_EXT']
|
|
16
|
+
silently { require 'shoulda' }
|
|
17
|
+
silently { require 'mocha' }
|
|
18
|
+
rescue LoadError
|
|
19
|
+
puts <<MSG
|
|
12
20
|
|
|
13
21
|
This test suite requires shoulda and mocha.
|
|
14
22
|
You can install them as follows:
|
|
@@ -16,7 +24,8 @@ You can install them as follows:
|
|
|
16
24
|
gem install mocha
|
|
17
25
|
|
|
18
26
|
MSG
|
|
19
|
-
|
|
27
|
+
|
|
28
|
+
exit
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
require 'bson_ext/cbson' if !(RUBY_PLATFORM =~ /java/) && ENV['C_EXT']
|
|
@@ -69,7 +78,17 @@ class Test::Unit::TestCase
|
|
|
69
78
|
self.class.mongo_port
|
|
70
79
|
end
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
def new_mock_socket(host='localhost', port=27017)
|
|
82
|
+
socket = Object.new
|
|
83
|
+
socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
84
|
+
socket.stubs(:close)
|
|
85
|
+
socket
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def new_mock_db
|
|
89
|
+
db = Object.new
|
|
90
|
+
end
|
|
91
|
+
|
|
73
92
|
def assert_raise_error(klass, message)
|
|
74
93
|
begin
|
|
75
94
|
yield
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require File.join((File.expand_path(File.dirname(__FILE__))), 'repl_set_manager')
|
|
2
|
+
|
|
3
|
+
class AuthReplSetManager < ReplSetManager
|
|
4
|
+
def initialize(opts={})
|
|
5
|
+
super(opts)
|
|
6
|
+
|
|
7
|
+
@key_path = opts[:key_path] || File.join(File.expand_path(File.dirname(__FILE__)), "keyfile.txt")
|
|
8
|
+
system("chmod 600 #{@key_path}")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def start_cmd(n)
|
|
12
|
+
super + " --keyFile #{@key_path}"
|
|
13
|
+
end
|
|
14
|
+
end
|
data/test/tools/load.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'mongo'
|
|
3
|
+
require 'sharding_manager'
|
|
4
|
+
|
|
5
|
+
class MongoLoader
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@mongo = Mongo::Connection.new("localhost", 50000)
|
|
9
|
+
@data = BSON::Binary.new(File.open("tools.gz").read)
|
|
10
|
+
@count = 0
|
|
11
|
+
@manager = ShardingManager.new(:config_count => 3)
|
|
12
|
+
@manager.start_cluster
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def kill
|
|
16
|
+
@manager.kill_random
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def restart
|
|
20
|
+
@manager.restart_killed_nodes
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
Thread.new do
|
|
25
|
+
("a".."z").each do |p|
|
|
26
|
+
seed(p)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def seed(prefix)
|
|
32
|
+
@queue = []
|
|
33
|
+
1000.times do |n|
|
|
34
|
+
id = BSON::OrderedHash.new
|
|
35
|
+
id[:p] = prefix
|
|
36
|
+
id[:c] = n
|
|
37
|
+
@queue << {:tid => id, :data => @data}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
while @queue.length > 0 do
|
|
41
|
+
begin
|
|
42
|
+
doc = @queue.pop
|
|
43
|
+
@mongo['app']['photos'].insert(doc, :safe => {:w => 3})
|
|
44
|
+
@count += 1
|
|
45
|
+
p @count
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
p e
|
|
48
|
+
p @count
|
|
49
|
+
@queue.push(doc)
|
|
50
|
+
@count -= 1
|
|
51
|
+
sleep(10)
|
|
52
|
+
retry
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@m = MongoLoader.new
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/ruby
|
|
2
2
|
|
|
3
|
+
require 'thread'
|
|
4
|
+
|
|
3
5
|
STDOUT.sync = true
|
|
4
6
|
|
|
5
7
|
unless defined? Mongo
|
|
@@ -17,6 +19,7 @@ class ReplSetManager
|
|
|
17
19
|
@host = opts[:host] || 'localhost'
|
|
18
20
|
@retries = opts[:retries] || 60
|
|
19
21
|
@config = {"_id" => @name, "members" => []}
|
|
22
|
+
@durable = opts.fetch(:durable, false)
|
|
20
23
|
@path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
|
|
21
24
|
|
|
22
25
|
@arbiter_count = opts[:arbiter_count] || 2
|
|
@@ -61,6 +64,13 @@ class ReplSetManager
|
|
|
61
64
|
ensure_up
|
|
62
65
|
end
|
|
63
66
|
|
|
67
|
+
def cleanup_set
|
|
68
|
+
system("killall mongod")
|
|
69
|
+
@count.times do |n|
|
|
70
|
+
system("rm -rf #{@mongods[n]['db_path']}")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
64
74
|
def init_node(n)
|
|
65
75
|
@mongods[n] ||= {}
|
|
66
76
|
port = @start_port + n
|
|
@@ -71,9 +81,7 @@ class ReplSetManager
|
|
|
71
81
|
system("rm -rf #{@mongods[n]['db_path']}")
|
|
72
82
|
system("mkdir -p #{@mongods[n]['db_path']}")
|
|
73
83
|
|
|
74
|
-
@mongods[n]['start'] =
|
|
75
|
-
" --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
|
|
76
|
-
|
|
84
|
+
@mongods[n]['start'] = start_cmd(n)
|
|
77
85
|
start(n)
|
|
78
86
|
|
|
79
87
|
member = {'_id' => n, 'host' => "#{@host}:#{@mongods[n]['port']}"}
|
|
@@ -88,17 +96,24 @@ class ReplSetManager
|
|
|
88
96
|
@config['members'] << member
|
|
89
97
|
end
|
|
90
98
|
|
|
91
|
-
def
|
|
99
|
+
def start_cmd(n)
|
|
100
|
+
@mongods[n]['start'] = "mongod --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
|
|
101
|
+
" --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
|
|
102
|
+
@mongods[n]['start'] += " --dur" if @durable
|
|
103
|
+
@mongods[n]['start']
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def kill(node, signal=2)
|
|
92
107
|
pid = @mongods[node]['pid']
|
|
93
108
|
puts "** Killing node with pid #{pid} at port #{@mongods[node]['port']}"
|
|
94
|
-
system("kill
|
|
109
|
+
system("kill -#{signal} #{@mongods[node]['pid']}")
|
|
95
110
|
@mongods[node]['up'] = false
|
|
96
111
|
sleep(1)
|
|
97
112
|
end
|
|
98
113
|
|
|
99
|
-
def kill_primary
|
|
114
|
+
def kill_primary(signal=2)
|
|
100
115
|
node = get_node_with_state(1)
|
|
101
|
-
kill(node)
|
|
116
|
+
kill(node, signal)
|
|
102
117
|
return node
|
|
103
118
|
end
|
|
104
119
|
|
|
@@ -174,6 +189,16 @@ class ReplSetManager
|
|
|
174
189
|
get_all_host_pairs_with_state(7)
|
|
175
190
|
end
|
|
176
191
|
|
|
192
|
+
# String used for adding a shard via mongos
|
|
193
|
+
# using the addshard command.
|
|
194
|
+
def shard_string
|
|
195
|
+
str = "#{@name}/"
|
|
196
|
+
str << @mongods.map do |k, mongod|
|
|
197
|
+
"#{@host}:#{mongod['port']}"
|
|
198
|
+
end.join(',')
|
|
199
|
+
str
|
|
200
|
+
end
|
|
201
|
+
|
|
177
202
|
private
|
|
178
203
|
|
|
179
204
|
def initiate
|
|
@@ -229,13 +254,13 @@ class ReplSetManager
|
|
|
229
254
|
while count < @retries do
|
|
230
255
|
begin
|
|
231
256
|
return yield
|
|
232
|
-
rescue Mongo::OperationFailure, Mongo::ConnectionFailure
|
|
257
|
+
rescue Mongo::OperationFailure, Mongo::ConnectionFailure => ex
|
|
233
258
|
sleep(1)
|
|
234
259
|
count += 1
|
|
235
260
|
end
|
|
236
261
|
end
|
|
237
262
|
|
|
238
|
-
raise
|
|
263
|
+
raise ex
|
|
239
264
|
end
|
|
240
265
|
|
|
241
266
|
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
require 'repl_set_manager'
|
|
2
|
+
require 'thread'
|
|
3
|
+
|
|
4
|
+
class ShardingManager
|
|
5
|
+
|
|
6
|
+
attr_accessor :shards
|
|
7
|
+
|
|
8
|
+
def initialize(opts={})
|
|
9
|
+
@durable = opts.fetch(:durable, true)
|
|
10
|
+
@host = "localhost"
|
|
11
|
+
|
|
12
|
+
@mongos_port = opts[:mongos_port] || 50000
|
|
13
|
+
@config_port = opts[:config_port] || 40000
|
|
14
|
+
@shard_start_port = opts[:start_shard_port] || 30000
|
|
15
|
+
@path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
|
|
16
|
+
system("rm -rf #{@path}")
|
|
17
|
+
|
|
18
|
+
@shard_count = 2
|
|
19
|
+
@mongos_count = 1
|
|
20
|
+
@config_count = opts.fetch(:config_count, 1)
|
|
21
|
+
if ![1, 3].include?(@config_count)
|
|
22
|
+
raise ArgumentError, "Must specify 1 or 3 config servers."
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@config_servers = {}
|
|
26
|
+
@mongos_servers = {}
|
|
27
|
+
@shards = []
|
|
28
|
+
@ports = []
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def kill_random
|
|
32
|
+
shard_to_kill = rand(@shard_count)
|
|
33
|
+
@shards[shard_to_kill].kill_primary
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def restart_killed
|
|
37
|
+
threads = []
|
|
38
|
+
@shards.each do |k, shard|
|
|
39
|
+
threads << Thread.new do
|
|
40
|
+
shard.restart_killed_nodes
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def start_cluster
|
|
46
|
+
start_sharding_components
|
|
47
|
+
start_mongos_servers
|
|
48
|
+
configure_cluster
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def configure_cluster
|
|
52
|
+
add_shards
|
|
53
|
+
enable_sharding
|
|
54
|
+
shard_collection
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def enable_sharding
|
|
58
|
+
mongos['admin'].command({:enablesharding => "app"})
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def shard_collection
|
|
62
|
+
cmd = BSON::OrderedHash.new
|
|
63
|
+
cmd[:shardcollection] = "app.photos"
|
|
64
|
+
cmd[:key] = {:tid => 1}
|
|
65
|
+
p mongos['admin'].command(cmd)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def add_shards
|
|
69
|
+
@shards.each do |shard|
|
|
70
|
+
cmd = {:addshard => shard.shard_string}
|
|
71
|
+
p cmd
|
|
72
|
+
p mongos['admin'].command(cmd)
|
|
73
|
+
end
|
|
74
|
+
p mongos['admin'].command({:listshards => 1})
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def mongos
|
|
78
|
+
attempt do
|
|
79
|
+
@mongos ||= Mongo::Connection.new(@host, @mongos_servers[0]['port'])
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def start_sharding_components
|
|
86
|
+
system("killall mongos")
|
|
87
|
+
|
|
88
|
+
threads = []
|
|
89
|
+
threads << Thread.new do
|
|
90
|
+
start_shards
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
threads << Thread.new do
|
|
94
|
+
start_config_servers
|
|
95
|
+
end
|
|
96
|
+
threads.each {|t| t.join}
|
|
97
|
+
puts "\nShards and config servers up!"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def start_shards
|
|
101
|
+
threads = []
|
|
102
|
+
@shard_count.times do |n|
|
|
103
|
+
threads << Thread.new do
|
|
104
|
+
port = @shard_start_port + n * 100
|
|
105
|
+
shard = ReplSetManager.new(:arbiter_count => 0, :secondary_count => 2,
|
|
106
|
+
:passive_count => 0, :start_port => port, :durable => @durable,
|
|
107
|
+
:name => "shard-#{n}")
|
|
108
|
+
shard.start_set
|
|
109
|
+
shard.ensure_up
|
|
110
|
+
@shards << shard
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
threads.each {|t| t.join}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def start_config_servers
|
|
117
|
+
@config_count.times do |n|
|
|
118
|
+
@config_servers[n] ||= {}
|
|
119
|
+
port = @config_port + n
|
|
120
|
+
@ports << port
|
|
121
|
+
@config_servers[n]['port'] = port
|
|
122
|
+
@config_servers[n]['db_path'] = get_path("config-#{port}")
|
|
123
|
+
@config_servers[n]['log_path'] = get_path("log-config-#{port}")
|
|
124
|
+
system("rm -rf #{@config_servers[n]['db_path']}")
|
|
125
|
+
system("mkdir -p #{@config_servers[n]['db_path']}")
|
|
126
|
+
|
|
127
|
+
@config_servers[n]['start'] = start_config_cmd(n)
|
|
128
|
+
|
|
129
|
+
start(@config_servers, n)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def start_mongos_servers
|
|
134
|
+
@mongos_count.times do |n|
|
|
135
|
+
@mongos_servers[n] ||= {}
|
|
136
|
+
port = @mongos_port + n
|
|
137
|
+
@ports << port
|
|
138
|
+
@mongos_servers[n]['port'] = port
|
|
139
|
+
@mongos_servers[n]['db_path'] = get_path("mongos-#{port}")
|
|
140
|
+
@mongos_servers[n]['pidfile_path'] = File.join(@mongos_servers[n]['db_path'], "mongod.lock")
|
|
141
|
+
@mongos_servers[n]['log_path'] = get_path("log-mongos-#{port}")
|
|
142
|
+
system("rm -rf #{@mongos_servers[n]['db_path']}")
|
|
143
|
+
system("mkdir -p #{@mongos_servers[n]['db_path']}")
|
|
144
|
+
|
|
145
|
+
@mongos_servers[n]['start'] = start_mongos_cmd(n)
|
|
146
|
+
|
|
147
|
+
start(@mongos_servers, n)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def start_config_cmd(n)
|
|
152
|
+
cmd = "mongod --configsvr --logpath '#{@config_servers[n]['log_path']}' " +
|
|
153
|
+
" --dbpath #{@config_servers[n]['db_path']} --port #{@config_servers[n]['port']} --fork"
|
|
154
|
+
cmd += " --dur" if @durable
|
|
155
|
+
cmd
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def start_mongos_cmd(n)
|
|
159
|
+
"mongos --configdb #{config_db_string} --logpath '#{@mongos_servers[n]['log_path']}' " +
|
|
160
|
+
"--pidfilepath #{@mongos_servers[n]['pidfile_path']} --port #{@mongos_servers[n]['port']} --fork"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def config_db_string
|
|
164
|
+
@config_servers.map do |k, v|
|
|
165
|
+
"#{@host}:#{v['port']}"
|
|
166
|
+
end.join(',')
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def start(set, node)
|
|
170
|
+
system(set[node]['start'])
|
|
171
|
+
set[node]['up'] = true
|
|
172
|
+
sleep(0.5)
|
|
173
|
+
set[node]['pid'] = File.open(File.join(set[node]['db_path'], 'mongod.lock')).read.strip
|
|
174
|
+
end
|
|
175
|
+
alias :restart :start
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
def cleanup_config
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def get_path(name)
|
|
183
|
+
File.join(@path, name)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# TODO: put this into a shared module
|
|
187
|
+
def attempt
|
|
188
|
+
raise "No block given!" unless block_given?
|
|
189
|
+
count = 0
|
|
190
|
+
|
|
191
|
+
while count < 50 do
|
|
192
|
+
begin
|
|
193
|
+
return yield
|
|
194
|
+
rescue Mongo::OperationFailure, Mongo::ConnectionFailure
|
|
195
|
+
sleep(1)
|
|
196
|
+
count += 1
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
raise exception
|
|
201
|
+
end
|
|
202
|
+
end
|
data/test/tools/test.rb
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'sharding_manager'
|
|
2
2
|
|
|
3
|
-
m =
|
|
4
|
-
m.
|
|
5
|
-
|
|
6
|
-
node = m.kill_secondary
|
|
7
|
-
m.ensure_up
|
|
8
|
-
|
|
9
|
-
puts "Pausing..."
|
|
10
|
-
gets
|
|
11
|
-
|
|
12
|
-
m.start(node)
|
|
13
|
-
m.ensure_up
|
|
3
|
+
m = ShardingManager.new(:config_count => 3)
|
|
4
|
+
m.start_cluster
|