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.
Files changed (51) hide show
  1. data/Rakefile +24 -8
  2. data/docs/HISTORY.md +15 -0
  3. data/docs/READ_PREFERENCE.md +1 -1
  4. data/docs/RELEASES.md +18 -4
  5. data/docs/REPLICA_SETS.md +5 -5
  6. data/docs/WRITE_CONCERN.md +1 -1
  7. data/lib/mongo/collection.rb +49 -10
  8. data/lib/mongo/connection.rb +7 -24
  9. data/lib/mongo/cursor.rb +3 -1
  10. data/lib/mongo/db.rb +5 -1
  11. data/lib/mongo/exceptions.rb +0 -3
  12. data/lib/mongo/gridfs/grid.rb +1 -1
  13. data/lib/mongo/gridfs/grid_file_system.rb +11 -3
  14. data/lib/mongo/networking.rb +7 -3
  15. data/lib/mongo/repl_set_connection.rb +58 -82
  16. data/lib/mongo/util/logging.rb +26 -18
  17. data/lib/mongo/util/node.rb +11 -2
  18. data/lib/mongo/util/pool_manager.rb +7 -5
  19. data/lib/mongo/util/support.rb +2 -2
  20. data/lib/mongo/util/uri_parser.rb +74 -36
  21. data/lib/mongo/version.rb +1 -1
  22. data/mongo.gemspec +2 -2
  23. data/test/auxillary/authentication_test.rb +8 -0
  24. data/test/auxillary/repl_set_auth_test.rb +1 -2
  25. data/test/bson/ordered_hash_test.rb +1 -1
  26. data/test/bson/test_helper.rb +2 -1
  27. data/test/collection_test.rb +71 -0
  28. data/test/connection_test.rb +9 -0
  29. data/test/db_test.rb +7 -0
  30. data/test/grid_file_system_test.rb +12 -0
  31. data/test/load/thin/load.rb +1 -1
  32. data/test/replica_sets/basic_test.rb +36 -26
  33. data/test/replica_sets/complex_connect_test.rb +44 -0
  34. data/test/replica_sets/connect_test.rb +48 -22
  35. data/test/replica_sets/count_test.rb +4 -6
  36. data/test/replica_sets/insert_test.rb +13 -14
  37. data/test/replica_sets/pooled_insert_test.rb +9 -10
  38. data/test/replica_sets/query_test.rb +4 -4
  39. data/test/replica_sets/read_preference_test.rb +48 -14
  40. data/test/replica_sets/refresh_test.rb +43 -42
  41. data/test/replica_sets/refresh_with_threads_test.rb +10 -9
  42. data/test/replica_sets/replication_ack_test.rb +3 -3
  43. data/test/replica_sets/rs_test_helper.rb +17 -11
  44. data/test/test_helper.rb +2 -1
  45. data/test/tools/repl_set_manager.rb +63 -39
  46. data/test/unit/collection_test.rb +2 -1
  47. data/test/unit/connection_test.rb +22 -0
  48. data/test/unit/cursor_test.rb +6 -3
  49. data/test/unit/read_test.rb +1 -1
  50. data/test/uri_test.rb +17 -2
  51. 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
- @conn = nil
8
+ ensure_rs
10
9
  end
11
10
 
12
11
  def teardown
13
- self.rs.restart_killed_nodes
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([self.rs.host, self.rs.ports[0]],
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
- @con = ReplSetConnection.new([self.rs.host, self.rs.ports[0]],
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
- self.rs.kill_all_secondaries
36
+ @rs.kill_all_secondaries
37
+ sleep(4)
44
38
 
45
39
  rescue_connection_failure do
46
- @conn = ReplSetConnection.new([self.rs.host, self.rs.ports[0]],
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
- self.rs.restart_killed_nodes
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
- self.rs.kill_all_secondaries
75
-
65
+ @rs.kill_all_secondaries
66
+ sleep(4)
67
+
76
68
  rescue_connection_failure do
77
- @conn = ReplSetConnection.new([self.rs.host, self.rs.ports[0]],
78
- [self.rs.host, self.rs.ports[1]],
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
- self.rs.restart_killed_nodes
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([self.rs.host, self.rs.ports[0]],
104
- [self.rs.host, self.rs.ports[1]],
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 = self.rs.remove_secondary_node
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
- self.rs.add_node(n)
126
+ @rs.add_node(n)
122
127
  end
123
128
 
124
129
  def test_adding_and_removing_nodes
125
- @conn = ReplSetConnection.new([self.rs.host, self.rs.ports[0]],
126
- [self.rs.host, self.rs.ports[1]],
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
- self.rs.add_node
133
+ @rs.add_node
131
134
  sleep(4)
132
135
  @conn['foo']['bar'].find_one
133
136
 
134
- @conn2 = ReplSetConnection.new([self.rs.host, self.rs.ports[0]],
135
- [self.rs.host, self.rs.ports[1]],
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
- self.rs.remove_secondary_node
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
- self.rs.restart_killed_nodes
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
- @conn = ReplSetConnection.new([self.rs.host, self.rs.ports[0]],
19
- [self.rs.host, self.rs.ports[1]],
20
- [self.rs.host, self.rs.ports[2]],
21
- :refresh_interval => 5,
22
- :refresh_mode => :sync,
23
- :read => :secondary)
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
- self.rs.add_node
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
- @conn = ReplSetConnection.new([self.rs.host, self.rs.ports[0]])
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
- self.rs.restart_killed_nodes
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
- module ReplicaSetTest
6
-
7
- def self.rs
8
- unless defined?(@rs)
9
- @rs = ReplSetManager.new
10
- @rs.start_set
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
@@ -1,6 +1,7 @@
1
1
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- require 'rubygems' if RUBY_VERSION < '1.9.0' && ENV['C_EXT']
2
+ require 'rubygems' if RUBY_VERSION
3
3
  require 'mongo'
4
+ gem 'test-unit'
4
5
  require 'test/unit'
5
6
 
6
7
  def silently
@@ -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 = `mongod --version`
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
- @mongods[node]['up'] = false
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
- [1, 2, 7].include?(m['state']) } &&
252
- status['members'].any? { |m| m['state'] == 1 }
253
-
254
- connections = []
255
- states = []
256
- status['members'].each do |member|
257
- begin
258
- host, port = member['name'].split(':')
259
- port = port.to_i
260
- conn = Mongo::Connection.new(host, port, :slave_ok => true)
261
- connections << conn
262
- state = conn['admin'].command({:ismaster => 1})
263
- states << state
264
- rescue Mongo::ConnectionFailure
265
- connections.each {|c| c.close }
266
- con.close
267
- raise Mongo::OperationFailure
268
- end
269
- end
270
-
271
- if states.any? {|s| s['ismaster']}
272
- print "all members up!\n\n"
273
- connections.each {|c| c.close }
274
- con.close
275
- return status
276
- else
277
- con.close
278
- raise Mongo::OperationFailure
279
- end
280
- else
281
- con.close
282
- raise Mongo::OperationFailure
283
- end
284
- end
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
 
@@ -5,7 +5,8 @@ class CollectionTest < Test::Unit::TestCase
5
5
  context "Basic operations: " do
6
6
  setup do
7
7
  @logger = mock()
8
- @logger.expects(:warn)
8
+ @logger.stubs(:level => 0)
9
+ @logger.expects(:debug)
9
10
  end
10
11
 
11
12
  should "send update message" do