mongo 1.5.2 → 1.6.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/Rakefile +24 -8
- data/docs/HISTORY.md +15 -0
- data/docs/READ_PREFERENCE.md +1 -1
- data/docs/RELEASES.md +18 -4
- data/docs/REPLICA_SETS.md +5 -5
- data/docs/WRITE_CONCERN.md +1 -1
- data/lib/mongo/collection.rb +49 -10
- data/lib/mongo/connection.rb +7 -24
- data/lib/mongo/cursor.rb +3 -1
- data/lib/mongo/db.rb +5 -1
- data/lib/mongo/exceptions.rb +0 -3
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/gridfs/grid_file_system.rb +11 -3
- data/lib/mongo/networking.rb +7 -3
- data/lib/mongo/repl_set_connection.rb +58 -82
- data/lib/mongo/util/logging.rb +26 -18
- data/lib/mongo/util/node.rb +11 -2
- data/lib/mongo/util/pool_manager.rb +7 -5
- data/lib/mongo/util/support.rb +2 -2
- data/lib/mongo/util/uri_parser.rb +74 -36
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +2 -2
- data/test/auxillary/authentication_test.rb +8 -0
- data/test/auxillary/repl_set_auth_test.rb +1 -2
- data/test/bson/ordered_hash_test.rb +1 -1
- data/test/bson/test_helper.rb +2 -1
- data/test/collection_test.rb +71 -0
- data/test/connection_test.rb +9 -0
- data/test/db_test.rb +7 -0
- data/test/grid_file_system_test.rb +12 -0
- data/test/load/thin/load.rb +1 -1
- data/test/replica_sets/basic_test.rb +36 -26
- data/test/replica_sets/complex_connect_test.rb +44 -0
- data/test/replica_sets/connect_test.rb +48 -22
- data/test/replica_sets/count_test.rb +4 -6
- data/test/replica_sets/insert_test.rb +13 -14
- data/test/replica_sets/pooled_insert_test.rb +9 -10
- data/test/replica_sets/query_test.rb +4 -4
- data/test/replica_sets/read_preference_test.rb +48 -14
- data/test/replica_sets/refresh_test.rb +43 -42
- data/test/replica_sets/refresh_with_threads_test.rb +10 -9
- data/test/replica_sets/replication_ack_test.rb +3 -3
- data/test/replica_sets/rs_test_helper.rb +17 -11
- data/test/test_helper.rb +2 -1
- data/test/tools/repl_set_manager.rb +63 -39
- data/test/unit/collection_test.rb +2 -1
- data/test/unit/connection_test.rb +22 -0
- data/test/unit/cursor_test.rb +6 -3
- data/test/unit/read_test.rb +1 -1
- data/test/uri_test.rb +17 -2
- metadata +151 -167
@@ -3,14 +3,13 @@ require './test/replica_sets/rs_test_helper'
|
|
3
3
|
require 'benchmark'
|
4
4
|
|
5
5
|
class ReplicaSetRefreshTest < Test::Unit::TestCase
|
6
|
-
include ReplicaSetTest
|
7
6
|
|
8
7
|
def setup
|
9
|
-
|
8
|
+
ensure_rs
|
10
9
|
end
|
11
10
|
|
12
11
|
def teardown
|
13
|
-
|
12
|
+
@rs.restart_killed_nodes
|
14
13
|
@conn.close if @conn
|
15
14
|
end
|
16
15
|
|
@@ -18,17 +17,11 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
18
17
|
Benchmark.bm do |x|
|
19
18
|
x.report("Connect") do
|
20
19
|
10.times do
|
21
|
-
ReplSetConnection.new(
|
22
|
-
[self.rs.host, self.rs.ports[1]],
|
23
|
-
[self.rs.host, self.rs.ports[2]],
|
24
|
-
:refresh_mode => false)
|
20
|
+
ReplSetConnection.new(build_seeds(3), :refresh_mode => false)
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
28
|
-
|
29
|
-
[self.rs.host, self.rs.ports[1]],
|
30
|
-
[self.rs.host, self.rs.ports[2]],
|
31
|
-
:refresh_mode => false)
|
24
|
+
@con = ReplSetConnection.new(build_seeds(3), :refresh_mode => false)
|
32
25
|
|
33
26
|
x.report("manager") do
|
34
27
|
man = Mongo::PoolManager.new(@con, @con.seeds)
|
@@ -40,13 +33,11 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
40
33
|
end
|
41
34
|
|
42
35
|
def test_connect_and_manual_refresh_with_secondaries_down
|
43
|
-
|
36
|
+
@rs.kill_all_secondaries
|
37
|
+
sleep(4)
|
44
38
|
|
45
39
|
rescue_connection_failure do
|
46
|
-
@conn = ReplSetConnection.new(
|
47
|
-
[self.rs.host, self.rs.ports[1]],
|
48
|
-
[self.rs.host, self.rs.ports[2]],
|
49
|
-
:refresh_mode => false)
|
40
|
+
@conn = ReplSetConnection.new(build_seeds(3), :refresh_mode => false)
|
50
41
|
end
|
51
42
|
|
52
43
|
assert_equal [], @conn.secondaries
|
@@ -59,7 +50,7 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
59
50
|
assert @conn.connected?
|
60
51
|
assert_equal @conn.read_pool, @conn.primary_pool
|
61
52
|
|
62
|
-
|
53
|
+
@rs.restart_killed_nodes
|
63
54
|
assert_equal [], @conn.secondaries
|
64
55
|
assert @conn.connected?
|
65
56
|
assert_equal @conn.read_pool, @conn.primary_pool
|
@@ -71,14 +62,12 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
71
62
|
end
|
72
63
|
|
73
64
|
def test_automated_refresh_with_secondaries_down
|
74
|
-
|
75
|
-
|
65
|
+
@rs.kill_all_secondaries
|
66
|
+
sleep(4)
|
67
|
+
|
76
68
|
rescue_connection_failure do
|
77
|
-
@conn = ReplSetConnection.new(
|
78
|
-
|
79
|
-
[self.rs.host, self.rs.ports[2]],
|
80
|
-
:refresh_interval => 2,
|
81
|
-
:refresh_mode => :sync)
|
69
|
+
@conn = ReplSetConnection.new(build_seeds(3),
|
70
|
+
:refresh_interval => 2, :refresh_mode => :sync)
|
82
71
|
end
|
83
72
|
|
84
73
|
assert_equal [], @conn.secondaries
|
@@ -86,7 +75,7 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
86
75
|
assert_equal @conn.read_pool, @conn.primary_pool
|
87
76
|
old_refresh_version = @conn.refresh_version
|
88
77
|
|
89
|
-
|
78
|
+
@rs.restart_killed_nodes
|
90
79
|
sleep(4)
|
91
80
|
@conn['foo']['bar'].find_one
|
92
81
|
@conn['foo']['bar'].insert({:a => 1})
|
@@ -98,18 +87,34 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
98
87
|
assert @conn.read_pool != @conn.primary_pool,
|
99
88
|
"Read pool and primary pool are identical."
|
100
89
|
end
|
90
|
+
|
91
|
+
def test_automated_refresh_when_secondary_goes_down
|
92
|
+
@conn = ReplSetConnection.new(build_seeds(3),
|
93
|
+
:refresh_interval => 2, :refresh_mode => :sync)
|
94
|
+
|
95
|
+
num_secondaries = @conn.secondary_pools.length
|
96
|
+
old_refresh_version = @conn.refresh_version
|
97
|
+
|
98
|
+
n = @rs.kill_secondary
|
99
|
+
sleep(4)
|
100
|
+
@conn['foo']['bar'].find_one
|
101
|
+
|
102
|
+
assert @conn.refresh_version > old_refresh_version,
|
103
|
+
"Refresh version hasn't changed."
|
104
|
+
assert_equal num_secondaries - 1, @conn.secondaries.length
|
105
|
+
assert_equal num_secondaries - 1, @conn.secondary_pools.length
|
106
|
+
|
107
|
+
@rs.restart_killed_nodes
|
108
|
+
end
|
101
109
|
|
102
110
|
def test_automated_refresh_with_removed_node
|
103
|
-
@conn = ReplSetConnection.new(
|
104
|
-
|
105
|
-
[self.rs.host, self.rs.ports[2]],
|
106
|
-
:refresh_interval => 2,
|
107
|
-
:refresh_mode => :sync)
|
111
|
+
@conn = ReplSetConnection.new(build_seeds(3),
|
112
|
+
:refresh_interval => 2, :refresh_mode => :sync)
|
108
113
|
|
109
114
|
num_secondaries = @conn.secondary_pools.length
|
110
115
|
old_refresh_version = @conn.refresh_version
|
111
116
|
|
112
|
-
n =
|
117
|
+
n = @rs.remove_secondary_node
|
113
118
|
sleep(4)
|
114
119
|
@conn['foo']['bar'].find_one
|
115
120
|
|
@@ -118,23 +123,19 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
118
123
|
assert_equal num_secondaries - 1, @conn.secondaries.length
|
119
124
|
assert_equal num_secondaries - 1, @conn.secondary_pools.length
|
120
125
|
|
121
|
-
|
126
|
+
@rs.add_node(n)
|
122
127
|
end
|
123
128
|
|
124
129
|
def test_adding_and_removing_nodes
|
125
|
-
@conn = ReplSetConnection.new(
|
126
|
-
|
127
|
-
[self.rs.host, self.rs.ports[2]],
|
128
|
-
:refresh_interval => 2, :refresh_mode => :sync)
|
130
|
+
@conn = ReplSetConnection.new(build_seeds(3),
|
131
|
+
:refresh_interval => 2, :refresh_mode => :sync)
|
129
132
|
|
130
|
-
|
133
|
+
@rs.add_node
|
131
134
|
sleep(4)
|
132
135
|
@conn['foo']['bar'].find_one
|
133
136
|
|
134
|
-
@conn2 = ReplSetConnection.new(
|
135
|
-
|
136
|
-
[self.rs.host, self.rs.ports[2]],
|
137
|
-
:refresh_interval => 2, :refresh_mode => :sync)
|
137
|
+
@conn2 = ReplSetConnection.new(build_seeds(3),
|
138
|
+
:refresh_interval => 2, :refresh_mode => :sync)
|
138
139
|
|
139
140
|
assert @conn2.secondaries.sort == @conn.secondaries.sort,
|
140
141
|
"Second connection secondaries not equal to first."
|
@@ -143,7 +144,7 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
|
|
143
144
|
|
144
145
|
config = @conn['admin'].command({:ismaster => 1})
|
145
146
|
|
146
|
-
|
147
|
+
@rs.remove_secondary_node
|
147
148
|
sleep(4)
|
148
149
|
config = @conn['admin'].command({:ismaster => 1})
|
149
150
|
|
@@ -3,24 +3,25 @@ require './test/replica_sets/rs_test_helper'
|
|
3
3
|
require 'benchmark'
|
4
4
|
|
5
5
|
class ReplicaSetRefreshWithThreadsTest < Test::Unit::TestCase
|
6
|
-
include ReplicaSetTest
|
7
6
|
|
8
7
|
def setup
|
8
|
+
ensure_rs
|
9
9
|
@conn = nil
|
10
10
|
end
|
11
11
|
|
12
12
|
def teardown
|
13
|
-
|
13
|
+
@rs.restart_killed_nodes
|
14
14
|
@conn.close if @conn
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_read_write_load_with_added_nodes
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
seeds = build_seeds(3)
|
19
|
+
args = {
|
20
|
+
:refresh_interval => 5,
|
21
|
+
:refresh_mode => :sync,
|
22
|
+
:read => :secondary
|
23
|
+
}
|
24
|
+
@conn = ReplSetConnection.new(seeds, args)
|
24
25
|
@duplicate = @conn[MONGO_TEST_DB]['duplicate']
|
25
26
|
@unique = @conn[MONGO_TEST_DB]['unique']
|
26
27
|
@duplicate.insert("test" => "insert")
|
@@ -44,7 +45,7 @@ class ReplicaSetRefreshWithThreadsTest < Test::Unit::TestCase
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
|
+
@rs.add_node
|
48
49
|
threads.each {|t| t.join }
|
49
50
|
|
50
51
|
config = @conn['admin'].command({:ismaster => 1})
|
@@ -2,10 +2,10 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
2
|
require './test/replica_sets/rs_test_helper'
|
3
3
|
|
4
4
|
class ReplicaSetAckTest < Test::Unit::TestCase
|
5
|
-
include ReplicaSetTest
|
6
5
|
|
7
6
|
def setup
|
8
|
-
|
7
|
+
ensure_rs
|
8
|
+
@conn = ReplSetConnection.new(build_seeds(1))
|
9
9
|
|
10
10
|
@slave1 = Connection.new(@conn.secondary_pools[0].host,
|
11
11
|
@conn.secondary_pools[0].port, :slave_ok => true)
|
@@ -18,7 +18,7 @@ class ReplicaSetAckTest < Test::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def teardown
|
21
|
-
|
21
|
+
@rs.restart_killed_nodes
|
22
22
|
@conn.close if @conn
|
23
23
|
end
|
24
24
|
|
@@ -2,18 +2,16 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
2
|
require './test/test_helper'
|
3
3
|
require './test/tools/repl_set_manager'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
class Test::Unit::TestCase
|
6
|
+
# Ensure replica set is available as an instance variable and that
|
7
|
+
# a new set is spun up for each TestCase class
|
8
|
+
def ensure_rs
|
9
|
+
unless defined?(@@current_class) and @@current_class == self.class
|
10
|
+
@@current_class = self.class
|
11
|
+
@@rs = ReplSetManager.new
|
12
|
+
@@rs.start_set
|
11
13
|
end
|
12
|
-
@rs
|
13
|
-
end
|
14
|
-
|
15
|
-
def rs
|
16
|
-
ReplicaSetTest.rs
|
14
|
+
@rs = @@rs
|
17
15
|
end
|
18
16
|
|
19
17
|
# Generic code for rescuing connection failures and retrying operations.
|
@@ -30,4 +28,12 @@ module ReplicaSetTest
|
|
30
28
|
retry
|
31
29
|
end
|
32
30
|
end
|
31
|
+
|
32
|
+
def build_seeds(num_hosts)
|
33
|
+
seeds = []
|
34
|
+
num_hosts.times do |n|
|
35
|
+
seeds << "#{@rs.host}:#{@rs.ports[n]}"
|
36
|
+
end
|
37
|
+
seeds
|
38
|
+
end
|
33
39
|
end
|
data/test/test_helper.rb
CHANGED
@@ -13,6 +13,7 @@ class ReplSetManager
|
|
13
13
|
attr_accessor :host, :start_port, :ports, :name, :mongods, :tags, :version
|
14
14
|
|
15
15
|
def initialize(opts={})
|
16
|
+
@mongod = ENV['mongod'] || 'mongod'
|
16
17
|
@start_port = opts[:start_port] || 30000
|
17
18
|
@ports = []
|
18
19
|
@name = opts[:name] || 'replica-set-foo'
|
@@ -21,6 +22,7 @@ class ReplSetManager
|
|
21
22
|
@config = {"_id" => @name, "members" => []}
|
22
23
|
@durable = opts.fetch(:durable, false)
|
23
24
|
@smallfiles = opts.fetch(:smallfiles, true)
|
25
|
+
@prealloc = opts.fetch(:prealloc, false)
|
24
26
|
@path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
|
25
27
|
@oplog_size = opts.fetch(:oplog_size, 16)
|
26
28
|
@tags = [{"dc" => "ny", "rack" => "a", "db" => "main"},
|
@@ -38,7 +40,7 @@ class ReplSetManager
|
|
38
40
|
end
|
39
41
|
|
40
42
|
@mongods = {}
|
41
|
-
version_string =
|
43
|
+
version_string = `#{@mongod} --version`
|
42
44
|
version_string =~ /(\d\.\d\.\d)/
|
43
45
|
@version = $1.split(".").map {|d| d.to_i }
|
44
46
|
end
|
@@ -124,10 +126,11 @@ class ReplSetManager
|
|
124
126
|
end
|
125
127
|
|
126
128
|
def start_cmd(n)
|
127
|
-
@mongods[n]['start'] = "mongod --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
|
129
|
+
@mongods[n]['start'] = "#{@mongod} --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
|
128
130
|
"--oplogSize #{@oplog_size} #{journal_switch} --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
|
129
131
|
@mongods[n]['start'] += " --dur" if @durable
|
130
132
|
@mongods[n]['start'] += " --smallfiles" if @smallfiles
|
133
|
+
@mongods[n]['start'] += " --noprealloc" unless @prealloc
|
131
134
|
@mongods[n]['start']
|
132
135
|
end
|
133
136
|
|
@@ -152,11 +155,11 @@ class ReplSetManager
|
|
152
155
|
return secondary
|
153
156
|
end
|
154
157
|
|
155
|
-
def add_node(n=nil)
|
158
|
+
def add_node(n=nil, &block)
|
156
159
|
primary = get_node_with_state(1)
|
157
160
|
con = get_connection(primary)
|
158
161
|
|
159
|
-
init_node(n || @mongods.length)
|
162
|
+
init_node(n || @mongods.length, &block)
|
160
163
|
config = con['local']['system.replset'].find_one
|
161
164
|
@config['version'] = config['version'] + 1
|
162
165
|
|
@@ -169,12 +172,33 @@ class ReplSetManager
|
|
169
172
|
con.close
|
170
173
|
ensure_up
|
171
174
|
end
|
175
|
+
|
176
|
+
def add_arbiter
|
177
|
+
add_node do |attrs|
|
178
|
+
attrs['arbiterOnly'] = true
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def wait_for_death(pid)
|
183
|
+
@retries.times do
|
184
|
+
if `ps a | grep mongod`.include?("#{pid}")
|
185
|
+
puts "waiting for mongod @ pid #{pid} to die..."
|
186
|
+
sleep(1)
|
187
|
+
else
|
188
|
+
puts "mongod @ pid #{pid} was killed successfully"
|
189
|
+
return true
|
190
|
+
end
|
191
|
+
end
|
192
|
+
puts "mongod never died"
|
193
|
+
return false
|
194
|
+
end
|
172
195
|
|
173
196
|
def kill(node, signal=2)
|
174
197
|
pid = @mongods[node]['pid']
|
175
198
|
puts "** Killing node with pid #{pid} at port #{@mongods[node]['port']}"
|
176
199
|
system("kill #{pid}")
|
177
|
-
|
200
|
+
dead = wait_for_death(pid)
|
201
|
+
@mongods[node]['up'] = false if dead
|
178
202
|
end
|
179
203
|
|
180
204
|
def kill_primary(signal=2)
|
@@ -248,40 +272,40 @@ class ReplSetManager
|
|
248
272
|
raise ex
|
249
273
|
end
|
250
274
|
if status['members'].all? { |m| m['health'] == 1 &&
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
275
|
+
[1, 2, 7].include?(m['state']) } &&
|
276
|
+
status['members'].any? { |m| m['state'] == 1 }
|
277
|
+
|
278
|
+
connections = []
|
279
|
+
states = []
|
280
|
+
status['members'].each do |member|
|
281
|
+
begin
|
282
|
+
host, port = member['name'].split(':')
|
283
|
+
port = port.to_i
|
284
|
+
conn = Mongo::Connection.new(host, port, :slave_ok => true)
|
285
|
+
connections << conn
|
286
|
+
state = conn['admin'].command({:ismaster => 1})
|
287
|
+
states << state
|
288
|
+
rescue Mongo::ConnectionFailure
|
289
|
+
connections.each {|c| c.close }
|
290
|
+
con.close
|
291
|
+
raise Mongo::OperationFailure
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
if states.any? {|s| s['ismaster']}
|
296
|
+
print "all members up!\n\n"
|
297
|
+
connections.each {|c| c.close }
|
298
|
+
con.close
|
299
|
+
return status
|
300
|
+
else
|
301
|
+
con.close
|
302
|
+
raise Mongo::OperationFailure
|
303
|
+
end
|
304
|
+
else
|
305
|
+
con.close
|
306
|
+
raise Mongo::OperationFailure
|
307
|
+
end
|
308
|
+
end
|
285
309
|
return false
|
286
310
|
end
|
287
311
|
|