mongo 1.6.4 → 1.7.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/README.md +13 -13
  2. data/Rakefile +7 -10
  3. data/docs/{GridFS.md → GRID_FS.md} +0 -0
  4. data/docs/HISTORY.md +16 -0
  5. data/docs/READ_PREFERENCE.md +70 -10
  6. data/docs/TUTORIAL.md +2 -2
  7. data/lib/mongo.rb +2 -0
  8. data/lib/mongo/collection.rb +62 -11
  9. data/lib/mongo/connection.rb +31 -41
  10. data/lib/mongo/cursor.rb +42 -86
  11. data/lib/mongo/db.rb +10 -8
  12. data/lib/mongo/networking.rb +30 -65
  13. data/lib/mongo/repl_set_connection.rb +91 -170
  14. data/lib/mongo/sharded_connection.rb +221 -0
  15. data/lib/mongo/util/node.rb +29 -36
  16. data/lib/mongo/util/pool.rb +10 -3
  17. data/lib/mongo/util/pool_manager.rb +77 -90
  18. data/lib/mongo/util/sharding_pool_manager.rb +143 -0
  19. data/lib/mongo/util/support.rb +22 -2
  20. data/lib/mongo/util/tcp_socket.rb +10 -15
  21. data/lib/mongo/util/uri_parser.rb +17 -10
  22. data/lib/mongo/version.rb +1 -1
  23. data/test/collection_test.rb +133 -1
  24. data/test/connection_test.rb +50 -4
  25. data/test/db_api_test.rb +3 -3
  26. data/test/db_test.rb +6 -1
  27. data/test/replica_sets/basic_test.rb +3 -6
  28. data/test/replica_sets/complex_connect_test.rb +14 -2
  29. data/test/replica_sets/complex_read_preference_test.rb +237 -0
  30. data/test/replica_sets/connect_test.rb +47 -67
  31. data/test/replica_sets/count_test.rb +1 -1
  32. data/test/replica_sets/cursor_test.rb +70 -0
  33. data/test/replica_sets/read_preference_test.rb +171 -118
  34. data/test/replica_sets/refresh_test.rb +3 -3
  35. data/test/replica_sets/refresh_with_threads_test.rb +2 -2
  36. data/test/replica_sets/rs_test_helper.rb +2 -2
  37. data/test/sharded_cluster/basic_test.rb +112 -0
  38. data/test/sharded_cluster/mongo_config_test.rb +126 -0
  39. data/test/sharded_cluster/sc_test_helper.rb +39 -0
  40. data/test/test_helper.rb +3 -3
  41. data/test/threading/threading_with_large_pool_test.rb +1 -1
  42. data/test/tools/mongo_config.rb +307 -0
  43. data/test/tools/repl_set_manager.rb +12 -12
  44. data/test/unit/collection_test.rb +1 -1
  45. data/test/unit/cursor_test.rb +11 -6
  46. data/test/unit/db_test.rb +4 -0
  47. data/test/unit/grid_test.rb +2 -0
  48. data/test/unit/read_test.rb +39 -8
  49. data/test/uri_test.rb +4 -8
  50. metadata +144 -127
@@ -5,7 +5,7 @@ class ReplicaSetCountTest < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
7
  ensure_rs
8
- @conn = ReplSetConnection.new(build_seeds(3), :read => :secondary)
8
+ @conn = ReplSetConnection.new(build_seeds(3), :read => :primary_preferred)
9
9
  assert @conn.primary_pool
10
10
  @primary = Connection.new(@conn.primary_pool.host, @conn.primary_pool.port)
11
11
  @db = @conn.db(MONGO_TEST_DB)
@@ -0,0 +1,70 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require './test/replica_sets/rs_test_helper'
3
+
4
+ class ReplicaSetCursorTest < Test::Unit::TestCase
5
+ def setup
6
+ ensure_rs
7
+ end
8
+
9
+ def test_cursors_get_closed
10
+ setup_connection
11
+ assert_cursor_count
12
+ end
13
+
14
+ def test_cursors_get_closed_secondary
15
+ setup_connection(:secondary)
16
+ assert_cursor_count
17
+ end
18
+
19
+ private
20
+
21
+ def setup_connection(read=:primary)
22
+ # Setup ReplicaSet Connection
23
+ @replconn = Mongo::ReplSetConnection.new(
24
+ build_seeds(3),
25
+ :read => read
26
+ )
27
+
28
+ @db = @replconn.db(MONGO_TEST_DB)
29
+ @db.drop_collection("cursor_tests")
30
+ @coll = @db.collection("cursor_tests")
31
+
32
+ @coll.insert({:a => 1}, :safe => true, :w => 3)
33
+ @coll.insert({:b => 2}, :safe => true, :w => 3)
34
+ @coll.insert({:c => 3}, :safe => true, :w => 3)
35
+
36
+ # Pin reader
37
+ @coll.find_one
38
+
39
+ # Setup Direct Connections
40
+ @primary = Mongo::Connection.new(*@replconn.manager.primary)
41
+ @read = Mongo::Connection.new(*@replconn.manager.read)
42
+ end
43
+
44
+ def cursor_count(connection)
45
+ connection['cursor_tests'].command({:cursorInfo => 1})['totalOpen']
46
+ end
47
+
48
+ def query_count(connection)
49
+ connection['admin'].command({:serverStatus => 1})['opcounters']['query']
50
+ end
51
+
52
+ def assert_cursor_count
53
+ before_primary = cursor_count(@primary)
54
+ before_read = cursor_count(@read)
55
+ before_query = query_count(@read)
56
+
57
+ @coll.find.limit(2).to_a
58
+ sleep(1)
59
+
60
+ after_primary = cursor_count(@primary)
61
+ after_read = cursor_count(@read)
62
+ after_query = query_count(@read)
63
+
64
+ assert_equal before_primary, after_primary
65
+ assert_equal before_read, after_read
66
+ assert_equal 1, after_query - before_query
67
+ end
68
+
69
+ end
70
+
@@ -5,80 +5,169 @@ require 'logger'
5
5
  class ReadPreferenceTest < Test::Unit::TestCase
6
6
 
7
7
  def setup
8
- ensure_rs
9
- log = Logger.new("test.log")
10
- seeds = build_seeds(2)
11
- args = {
12
- :read => :secondary,
13
- :pool_size => 50,
14
- :refresh_mode => false,
15
- :refresh_interval => 5,
16
- :logger => log
17
- }
18
- @conn = ReplSetConnection.new(seeds, args)
19
- @db = @conn.db(MONGO_TEST_DB)
20
- @db.drop_collection("test-sets")
21
- end
8
+ ensure_rs(:secondary_count => 1, :arbiter_count => 1)
22
9
 
23
- def teardown
24
- @rs.restart_killed_nodes
10
+ # Insert data
11
+ conn = Connection.new(@rs.host, @rs.primary[1])
12
+ db = conn.db(MONGO_TEST_DB)
13
+ coll = db.collection("test-sets")
14
+ coll.save({:a => 20}, :safe => {:w => 2})
25
15
  end
26
16
 
27
17
  def test_read_primary
18
+ conn = make_connection
19
+ rescue_connection_failure do
20
+ assert conn.read_primary?
21
+ assert conn.primary?
22
+ end
23
+
24
+ conn = make_connection(:primary_preferred)
28
25
  rescue_connection_failure do
29
- assert !@conn.read_primary?
30
- assert !@conn.primary?
26
+ assert conn.read_primary?
27
+ assert conn.primary?
28
+ end
29
+
30
+ conn = make_connection(:secondary)
31
+ rescue_connection_failure do
32
+ assert !conn.read_primary?
33
+ assert !conn.primary?
34
+ end
35
+
36
+ conn = make_connection(:secondary_preferred)
37
+ rescue_connection_failure do
38
+ assert !conn.read_primary?
39
+ assert !conn.primary?
31
40
  end
32
41
  end
33
42
 
34
- def test_con
35
- assert @conn.primary_pool, "No primary pool!"
36
- assert @conn.read_pool, "No read pool!"
37
- assert @conn.primary_pool.port != @conn.read_pool.port,
38
- "Primary port and read port at the same!"
43
+ def test_connection_pools
44
+ conn = make_connection
45
+ assert conn.primary_pool, "No primary pool!"
46
+ assert conn.read_pool, "No read pool!"
47
+ assert conn.primary_pool.port == conn.read_pool.port,
48
+ "Primary port and read port are not the same!"
49
+
50
+ conn = make_connection(:primary_preferred)
51
+ assert conn.primary_pool, "No primary pool!"
52
+ assert conn.read_pool, "No read pool!"
53
+ assert conn.primary_pool.port == conn.read_pool.port,
54
+ "Primary port and read port are not the same!"
55
+
56
+ conn = make_connection(:secondary)
57
+ assert conn.primary_pool, "No primary pool!"
58
+ assert conn.read_pool, "No read pool!"
59
+ assert conn.primary_pool.port != conn.read_pool.port,
60
+ "Primary port and read port are the same!"
61
+
62
+ conn = make_connection(:secondary_preferred)
63
+ assert conn.primary_pool, "No primary pool!"
64
+ assert conn.read_pool, "No read pool!"
65
+ assert conn.primary_pool.port != conn.read_pool.port,
66
+ "Primary port and read port are the same!"
39
67
  end
40
68
 
41
- def test_read_secondary_only
42
- @rs.add_arbiter
43
- @rs.remove_secondary_node
44
-
45
- @conn = ReplSetConnection.new(build_seeds(3), :read => :secondary_only)
69
+ def test_read_routing
70
+ prepare_routing_test
46
71
 
47
- @db = @conn.db(MONGO_TEST_DB)
48
- @coll = @db.collection("test-sets")
49
-
50
- @coll.save({:a => 20}, :safe => {:w => 2})
72
+ # Test that reads are going to the right members
73
+ assert_query_route(@primary, @primary_direct)
74
+ assert_query_route(@primary_preferred, @primary_direct)
75
+ assert_query_route(@secondary, @secondary_direct)
76
+ assert_query_route(@secondary_preferred, @secondary_direct)
77
+ end
51
78
 
52
- # Test that reads are going to secondary on ReplSetConnection
53
- @secondary = Connection.new(@rs.host, @conn.secondary_pool.port, :slave_ok => true)
54
- queries_before = @secondary['admin'].command({:serverStatus => 1})['opcounters']['query']
55
- @coll.find_one
56
- queries_after = @secondary['admin'].command({:serverStatus => 1})['opcounters']['query']
57
- assert_equal 1, queries_after - queries_before
79
+ def test_read_routing_with_primary_down
80
+ prepare_routing_test
81
+
82
+ # Test that reads are going to the right members
83
+ assert_query_route(@primary, @primary_direct)
84
+ assert_query_route(@primary_preferred, @primary_direct)
85
+ assert_query_route(@secondary, @secondary_direct)
86
+ assert_query_route(@secondary_preferred, @secondary_direct)
87
+
88
+ # Kill the primary so only a single secondary exists
89
+ @rs.kill_primary
58
90
 
91
+ # Test that reads are going to the right members
92
+ assert_raise_error ConnectionFailure do
93
+ @primary[MONGO_TEST_DB]['test-sets'].find_one
94
+ end
95
+ assert_query_route(@primary_preferred, @secondary_direct)
96
+ assert_query_route(@secondary, @secondary_direct)
97
+ assert_query_route(@secondary_preferred, @secondary_direct)
98
+
99
+ # Restore set
100
+ @rs.restart_killed_nodes
101
+ sleep(1)
102
+ @repl_cons.each { |con| con.refresh }
103
+ sleep(1)
104
+ @primary_direct = Connection.new(
105
+ @rs.host,
106
+ @primary.read_pool.port
107
+ )
108
+
109
+ # Test that reads are going to the right members
110
+ assert_query_route(@primary, @primary_direct)
111
+ assert_query_route(@primary_preferred, @primary_direct)
112
+ assert_query_route(@secondary, @secondary_direct)
113
+ assert_query_route(@secondary_preferred, @secondary_direct)
114
+ end
115
+
116
+ def test_read_routing_with_secondary_down
117
+ prepare_routing_test
118
+
119
+ # Test that reads are going to the right members
120
+ assert_query_route(@primary, @primary_direct)
121
+ assert_query_route(@primary_preferred, @primary_direct)
122
+ assert_query_route(@secondary, @secondary_direct)
123
+ assert_query_route(@secondary_preferred, @secondary_direct)
124
+
125
+ # Kill the secondary so that only primary exists
59
126
  @rs.kill_secondary
60
- @conn.refresh
61
-
62
- # Test that reads are only allowed from secondaries
63
- assert_raise ConnectionFailure.new("Could not checkout a socket.") do
64
- @coll.find_one
127
+
128
+ # Test that reads are going to the right members
129
+ assert_query_route(@primary, @primary_direct)
130
+ assert_query_route(@primary_preferred, @primary_direct)
131
+ assert_raise_error ConnectionFailure do
132
+ @secondary[MONGO_TEST_DB]['test-sets'].find_one
65
133
  end
66
-
67
- @rs = ReplSetManager.new
68
- @rs.start_set
134
+ assert_query_route(@secondary_preferred, @primary_direct)
135
+
136
+ # Restore set
137
+ @rs.restart_killed_nodes
138
+ sleep(1)
139
+ @repl_cons.each { |con| con.refresh }
140
+ sleep(1)
141
+ @secondary_direct = Connection.new(
142
+ @rs.host,
143
+ @secondary.read_pool.port,
144
+ :slave_ok => true
145
+ )
146
+
147
+ # Test that reads are going to the right members
148
+ assert_query_route(@primary, @primary_direct)
149
+ assert_query_route(@primary_preferred, @primary_direct)
150
+ assert_query_route(@secondary, @secondary_direct)
151
+ assert_query_route(@secondary_preferred, @secondary_direct)
69
152
  end
70
153
 
71
- def test_query_secondaries
72
- @secondary = Connection.new(@rs.host, @conn.read_pool.port, :slave_ok => true)
73
- @coll = @db.collection("test-sets", :safe => {:w => 3, :wtimeout => 20000})
154
+ def test_write_conecern
155
+ @conn = make_connection(:secondary_preferred)
156
+ @db = @conn[MONGO_TEST_DB]
157
+ @coll = @db.collection("test-sets", :safe => {
158
+ :w => 2, :wtimeout => 20000
159
+ })
74
160
  @coll.save({:a => 20})
75
161
  @coll.save({:a => 30})
76
162
  @coll.save({:a => 40})
163
+
164
+ # pin the read pool
165
+ @coll.find_one
166
+ @secondary = Connection.new(@rs.host, @conn.read_pool.port, :slave_ok => true)
167
+
77
168
  results = []
78
- queries_before = @secondary['admin'].command({:serverStatus => 1})['opcounters']['query']
79
169
  @coll.find.each {|r| results << r["a"]}
80
- queries_after = @secondary['admin'].command({:serverStatus => 1})['opcounters']['query']
81
- assert_equal 1, queries_after - queries_before
170
+
82
171
  assert results.include?(20)
83
172
  assert results.include?(30)
84
173
  assert results.include?(40)
@@ -87,57 +176,17 @@ class ReadPreferenceTest < Test::Unit::TestCase
87
176
 
88
177
  results = []
89
178
  rescue_connection_failure do
90
- #puts "@coll.find().each"
91
179
  @coll.find.each {|r| results << r}
92
180
  [20, 30, 40].each do |a|
93
181
  assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
94
182
  end
95
183
  end
96
- end
97
-
98
- def test_kill_primary
99
- @coll = @db.collection("test-sets", :safe => {:w => 3, :wtimeout => 10000})
100
- @coll.save({:a => 20})
101
- @coll.save({:a => 30})
102
- assert_equal 2, @coll.find.to_a.length
103
-
104
- # Should still be able to read immediately after killing master node
105
- @rs.kill_primary
106
- assert_equal 2, @coll.find.to_a.length
107
- rescue_connection_failure do
108
- @coll.save({:a => 50}, :safe => {:w => 2, :wtimeout => 10000})
109
- end
110
184
  @rs.restart_killed_nodes
111
- sleep(1)
112
- @coll.save({:a => 50}, :safe => {:w => 2, :wtimeout => 10000})
113
- assert_equal 4, @coll.find.to_a.length
114
- end
115
-
116
- def test_kill_secondary
117
- @coll = @db.collection("test-sets", {:safe => {:w => 3, :wtimeout => 20000}})
118
- @coll.save({:a => 20})
119
- @coll.save({:a => 30})
120
- assert_equal 2, @coll.find.to_a.length
121
-
122
- read_node = @rs.get_node_from_port(@conn.read_pool.port)
123
- @rs.kill(read_node)
124
-
125
- # Should fail immediately on next read
126
- old_read_pool_port = @conn.read_pool.port
127
- assert_raise ConnectionFailure do
128
- @coll.find.to_a.length
129
- end
130
-
131
- # Should eventually reconnect and be able to read
132
- rescue_connection_failure do
133
- length = @coll.find.to_a.length
134
- assert_equal 2, length
135
- end
136
- new_read_pool_port = @conn.read_pool.port
137
- assert old_read_pool_port != new_read_pool_port
138
185
  end
139
186
 
140
187
  def test_write_lots_of_data
188
+ @conn = make_connection(:secondary_preferred)
189
+ @db = @conn[MONGO_TEST_DB]
141
190
  @coll = @db.collection("test-sets", {:safe => {:w => 2}})
142
191
 
143
192
  6000.times do |n|
@@ -149,33 +198,37 @@ class ReadPreferenceTest < Test::Unit::TestCase
149
198
  cursor.close
150
199
  end
151
200
 
152
- # TODO: enable this once we enable reads from tags.
153
- # def test_query_tagged
154
- # col = @db['mongo-test']
201
+ private
155
202
 
156
- # col.insert({:a => 1}, :safe => {:w => 3})
157
- # col.find_one({}, :read => {:db => "main"})
158
- # col.find_one({}, :read => {:dc => "ny"})
159
- # col.find_one({}, :read => {:dc => "sf"})
203
+ def prepare_routing_test
204
+ # Setup replica set connections
205
+ @primary = make_connection(:primary)
206
+ @primary_preferred = make_connection(:primary_preferred)
207
+ @secondary = make_connection(:secondary)
208
+ @secondary_preferred = make_connection(:secondary_preferred)
209
+ @repl_cons = [@primary, @primary_preferred, @secondary, @secondary_preferred]
160
210
 
161
- # assert_raise Mongo::NodeWithTagsNotFound do
162
- # col.find_one({}, :read => {:foo => "bar"})
163
- # end
164
-
165
- # threads = []
166
- # 100.times do
167
- # threads << Thread.new do
168
- # col.find_one({}, :read => {:dc => "sf"})
169
- # end
170
- # end
171
-
172
- # threads.each {|t| t.join }
211
+ # Setup direct connections
212
+ @primary_direct = Connection.new(@rs.host, @primary.read_pool.port)
213
+ @secondary_direct = Connection.new(@rs.host, @secondary.read_pool.port, :slave_ok => true)
214
+ end
173
215
 
174
- # col.remove
175
- # end
216
+ def make_connection(mode = :primary, opts = {})
217
+ opts.merge!({:read => mode})
218
+ ReplSetConnection.new(build_seeds(3), opts)
219
+ end
176
220
 
177
- #def teardown
178
- # @rs.restart_killed_nodes
179
- #end
221
+ def query_count(connection)
222
+ connection['admin'].command({:serverStatus => 1})['opcounters']['query']
223
+ end
180
224
 
225
+ def assert_query_route(test_connection, expected_target)
226
+ #puts "#{test_connection.read_pool.port} #{expected_target.read_pool.port}"
227
+ queries_before = query_count(expected_target)
228
+ assert_nothing_raised do
229
+ test_connection['MONGO_TEST_DB']['test-sets'].find_one
230
+ end
231
+ queries_after = query_count(expected_target)
232
+ assert_equal 1, queries_after - queries_before
233
+ end
181
234
  end
@@ -69,12 +69,12 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
69
69
 
70
70
  rescue_connection_failure do
71
71
  @conn = ReplSetConnection.new(build_seeds(3),
72
- :refresh_interval => 2, :refresh_mode => :sync)
72
+ :refresh_interval => 2, :refresh_mode => :sync, :read => :secondary_preferred)
73
73
  end
74
74
 
75
75
  assert_equal [], @conn.secondaries
76
76
  assert @conn.connected?
77
- assert_equal @conn.read_pool, @conn.primary_pool
77
+ assert_equal @conn.manager.read, @conn.manager.primary
78
78
  old_refresh_version = @conn.refresh_version
79
79
 
80
80
  @rs.restart_killed_nodes
@@ -86,7 +86,7 @@ class ReplicaSetRefreshTest < Test::Unit::TestCase
86
86
  "Refresh version hasn't changed."
87
87
  assert @conn.secondaries.length > 0,
88
88
  "No secondaries have been added."
89
- assert @conn.read_pool != @conn.primary_pool,
89
+ assert @conn.manager.read != @conn.manager.primary,
90
90
  "Read pool and primary pool are identical."
91
91
  end
92
92
 
@@ -15,13 +15,13 @@ class ReplicaSetRefreshWithThreadsTest < Test::Unit::TestCase
15
15
 
16
16
  def test_read_write_load_with_added_nodes
17
17
  # MongoDB < 2.0 will disconnect clients on rs.reconfig()
18
- return true if @rs.version.first < 2
18
+ return true if @rs.version < "2"
19
19
 
20
20
  seeds = build_seeds(3)
21
21
  args = {
22
22
  :refresh_interval => 5,
23
23
  :refresh_mode => :sync,
24
- :read => :secondary
24
+ :read => :secondary_preferred
25
25
  }
26
26
  @conn = ReplSetConnection.new(seeds, args)
27
27
  @duplicate = @conn[MONGO_TEST_DB]['duplicate']