mongo 2.10.5 → 2.11.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/mongo.rb +2 -0
  6. data/lib/mongo/address.rb +4 -0
  7. data/lib/mongo/address/validator.rb +99 -0
  8. data/lib/mongo/auth.rb +7 -2
  9. data/lib/mongo/auth/user.rb +1 -7
  10. data/lib/mongo/background_thread.rb +135 -0
  11. data/lib/mongo/bulk_write/transformable.rb +3 -3
  12. data/lib/mongo/client.rb +74 -16
  13. data/lib/mongo/cluster.rb +193 -41
  14. data/lib/mongo/cluster/periodic_executor.rb +31 -43
  15. data/lib/mongo/cluster/sdam_flow.rb +26 -3
  16. data/lib/mongo/cluster/srv_monitor.rb +127 -0
  17. data/lib/mongo/collection/view/readable.rb +3 -5
  18. data/lib/mongo/collection/view/writable.rb +3 -3
  19. data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
  20. data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
  21. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  22. data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
  23. data/lib/mongo/error.rb +1 -0
  24. data/lib/mongo/error/auth_error.rb +1 -1
  25. data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
  26. data/lib/mongo/error/invalid_address.rb +24 -0
  27. data/lib/mongo/error/notable.rb +2 -2
  28. data/lib/mongo/error/operation_failure.rb +3 -3
  29. data/lib/mongo/error/pool_closed_error.rb +11 -4
  30. data/lib/mongo/event.rb +1 -1
  31. data/lib/mongo/grid/file.rb +0 -5
  32. data/lib/mongo/grid/file/chunk.rb +0 -2
  33. data/lib/mongo/grid/fs_bucket.rb +13 -15
  34. data/lib/mongo/grid/stream/write.rb +3 -9
  35. data/lib/mongo/loggable.rb +5 -1
  36. data/lib/mongo/monitoring.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
  38. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
  39. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
  40. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
  41. data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
  42. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
  43. data/lib/mongo/operation/shared/executable.rb +5 -10
  44. data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
  45. data/lib/mongo/protocol/get_more.rb +1 -2
  46. data/lib/mongo/protocol/kill_cursors.rb +13 -6
  47. data/lib/mongo/protocol/serializers.rb +4 -20
  48. data/lib/mongo/retryable.rb +9 -34
  49. data/lib/mongo/semaphore.rb +1 -1
  50. data/lib/mongo/server.rb +113 -42
  51. data/lib/mongo/server/connection.rb +12 -5
  52. data/lib/mongo/server/connection_pool.rb +250 -40
  53. data/lib/mongo/server/connection_pool/populator.rb +58 -0
  54. data/lib/mongo/server/description.rb +9 -2
  55. data/lib/mongo/server/monitor.rb +68 -93
  56. data/lib/mongo/server/monitor/connection.rb +2 -0
  57. data/lib/mongo/server_selector/selectable.rb +13 -5
  58. data/lib/mongo/session.rb +0 -13
  59. data/lib/mongo/srv.rb +17 -0
  60. data/lib/mongo/srv/monitor.rb +96 -0
  61. data/lib/mongo/srv/resolver.rb +130 -0
  62. data/lib/mongo/srv/result.rb +126 -0
  63. data/lib/mongo/srv/warning_result.rb +35 -0
  64. data/lib/mongo/uri.rb +45 -55
  65. data/lib/mongo/uri/srv_protocol.rb +89 -42
  66. data/lib/mongo/version.rb +1 -1
  67. data/mongo.gemspec +3 -4
  68. data/spec/README.md +6 -1
  69. data/spec/enterprise_auth/kerberos_spec.rb +7 -6
  70. data/spec/integration/change_stream_examples_spec.rb +0 -4
  71. data/spec/integration/client_construction_spec.rb +14 -2
  72. data/spec/integration/connect_single_rs_name_spec.rb +2 -2
  73. data/spec/integration/connection_pool_populator_spec.rb +296 -0
  74. data/spec/integration/connection_spec.rb +31 -22
  75. data/spec/integration/cursor_reaping_spec.rb +1 -2
  76. data/spec/integration/docs_examples_spec.rb +0 -4
  77. data/spec/integration/heartbeat_events_spec.rb +17 -15
  78. data/spec/integration/reconnect_spec.rb +144 -1
  79. data/spec/integration/retryable_writes_errors_spec.rb +0 -4
  80. data/spec/integration/retryable_writes_spec.rb +36 -36
  81. data/spec/integration/sdam_error_handling_spec.rb +31 -25
  82. data/spec/integration/sdam_events_spec.rb +2 -6
  83. data/spec/integration/server_monitor_spec.rb +28 -0
  84. data/spec/integration/server_selector_spec.rb +7 -5
  85. data/spec/integration/srv_monitoring_spec.rb +360 -0
  86. data/spec/integration/step_down_spec.rb +4 -6
  87. data/spec/lite_spec_helper.rb +22 -0
  88. data/spec/mongo/address/validator_spec.rb +51 -0
  89. data/spec/mongo/auth/cr_spec.rb +1 -29
  90. data/spec/mongo/auth/ldap_spec.rb +1 -29
  91. data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
  92. data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
  93. data/spec/mongo/auth/scram_spec.rb +1 -29
  94. data/spec/mongo/auth/user/view_spec.rb +1 -36
  95. data/spec/mongo/auth/user_spec.rb +0 -12
  96. data/spec/mongo/auth/x509_spec.rb +1 -29
  97. data/spec/mongo/bulk_write_spec.rb +2 -2
  98. data/spec/mongo/client_construction_spec.rb +56 -15
  99. data/spec/mongo/client_spec.rb +31 -27
  100. data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
  101. data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
  102. data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
  103. data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
  104. data/spec/mongo/cluster/topology/single_spec.rb +20 -11
  105. data/spec/mongo/cluster_spec.rb +45 -29
  106. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
  107. data/spec/mongo/collection/view/readable_spec.rb +0 -16
  108. data/spec/mongo/collection_spec.rb +0 -44
  109. data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
  110. data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
  111. data/spec/mongo/cursor_spec.rb +27 -7
  112. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
  113. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
  114. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
  115. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
  116. data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
  117. data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
  118. data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
  119. data/spec/mongo/retryable_spec.rb +52 -31
  120. data/spec/mongo/server/app_metadata_spec.rb +0 -8
  121. data/spec/mongo/server/connection_auth_spec.rb +5 -2
  122. data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
  123. data/spec/mongo/server/connection_pool_spec.rb +256 -107
  124. data/spec/mongo/server/connection_spec.rb +22 -33
  125. data/spec/mongo/server/description_spec.rb +42 -4
  126. data/spec/mongo/server/monitor/connection_spec.rb +22 -11
  127. data/spec/mongo/server/monitor_spec.rb +66 -107
  128. data/spec/mongo/server_spec.rb +82 -60
  129. data/spec/mongo/session/session_pool_spec.rb +1 -5
  130. data/spec/mongo/session_spec.rb +0 -4
  131. data/spec/mongo/socket/ssl_spec.rb +2 -2
  132. data/spec/mongo/srv/monitor_spec.rb +211 -0
  133. data/spec/mongo/srv/result_spec.rb +54 -0
  134. data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
  135. data/spec/mongo/uri_spec.rb +125 -4
  136. data/spec/spec_helper.rb +6 -0
  137. data/spec/spec_tests/auth_spec.rb +39 -0
  138. data/spec/spec_tests/cmap_spec.rb +55 -8
  139. data/spec/spec_tests/connection_string_spec.rb +6 -31
  140. data/spec/spec_tests/data/auth/connection-string.yml +297 -0
  141. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
  142. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
  143. data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
  144. data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
  145. data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
  146. data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
  147. data/spec/spec_tests/data/transactions/abort.yml +3 -3
  148. data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
  149. data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
  150. data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
  151. data/spec/spec_tests/max_staleness_spec.rb +7 -2
  152. data/spec/spec_tests/retryable_reads_spec.rb +0 -31
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
  154. data/spec/spec_tests/sdam_spec.rb +4 -7
  155. data/spec/spec_tests/server_selection_spec.rb +6 -2
  156. data/spec/spec_tests/transactions_spec.rb +0 -2
  157. data/spec/spec_tests/uri_options_spec.rb +4 -2
  158. data/spec/stress/connection_pool_stress_spec.rb +203 -0
  159. data/spec/stress/connection_pool_timing_spec.rb +181 -0
  160. data/spec/support/auth.rb +113 -0
  161. data/spec/support/background_thread_registry.rb +63 -0
  162. data/spec/support/client_registry.rb +11 -2
  163. data/spec/support/cluster_config.rb +65 -46
  164. data/spec/support/cluster_tools.rb +2 -2
  165. data/spec/support/cmap.rb +13 -14
  166. data/spec/support/cmap/verifier.rb +4 -5
  167. data/spec/support/command_monitoring.rb +0 -5
  168. data/spec/support/common_shortcuts.rb +101 -1
  169. data/spec/support/constraints.rb +25 -0
  170. data/spec/support/dns.rb +13 -0
  171. data/spec/support/event_subscriber.rb +0 -7
  172. data/spec/support/json_ext_formatter.rb +5 -1
  173. data/spec/support/lite_constraints.rb +22 -6
  174. data/spec/support/local_resource_registry.rb +34 -0
  175. data/spec/support/sdam_monitoring.rb +115 -0
  176. data/spec/support/spec_config.rb +20 -6
  177. data/spec/support/spec_setup.rb +2 -2
  178. data/spec/support/transactions.rb +1 -1
  179. data/spec/support/transactions/test.rb +1 -1
  180. data/spec/support/utils.rb +1 -16
  181. metadata +685 -659
  182. metadata.gz.sig +0 -0
  183. data/lib/mongo/event/description_changed.rb +0 -52
  184. data/spec/integration/bson_symbol_spec.rb +0 -34
  185. data/spec/integration/crud_spec.rb +0 -45
  186. data/spec/integration/get_more_spec.rb +0 -32
  187. data/spec/integration/grid_fs_bucket_spec.rb +0 -48
  188. data/spec/integration/retryable_errors_spec.rb +0 -265
  189. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
  190. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
  191. data/spec/runners/sdam/verifier.rb +0 -88
@@ -22,10 +22,6 @@ describe Mongo::Server::AppMetadata do
22
22
  client.cluster
23
23
  end
24
24
 
25
- after do
26
- client.close(true)
27
- end
28
-
29
25
  it 'sets the app name' do
30
26
  expect(app_metadata.send(:full_client_document)[:application][:name]).to eq('app_metadata_test')
31
27
  end
@@ -40,10 +36,6 @@ describe Mongo::Server::AppMetadata do
40
36
  client.cluster
41
37
  end
42
38
 
43
- after do
44
- client.close(true)
45
- end
46
-
47
39
  it 'raises an error' do
48
40
  expect {
49
41
  app_metadata.send(:validate!)
@@ -25,6 +25,7 @@ describe Mongo::Server::Connection, retry: 3 do
25
25
  allow(cl).to receive(:options).and_return({})
26
26
  allow(cl).to receive(:cluster_time).and_return(nil)
27
27
  allow(cl).to receive(:update_cluster_time)
28
+ allow(cl).to receive(:run_sdam_flow)
28
29
  pool = double('pool')
29
30
  allow(pool).to receive(:disconnect!)
30
31
  allow(cl).to receive(:pool).and_return(pool)
@@ -34,8 +35,10 @@ describe Mongo::Server::Connection, retry: 3 do
34
35
  declare_topology_double
35
36
 
36
37
  let(:server) do
37
- Mongo::Server.new(address, cluster, monitoring, listeners,
38
- SpecConfig.instance.test_options.merge(monitoring_io: false))
38
+ register_server(
39
+ Mongo::Server.new(address, cluster, monitoring, listeners,
40
+ SpecConfig.instance.test_options.merge(monitoring_io: false))
41
+ )
39
42
  end
40
43
 
41
44
  before(:all) do
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Server::Populator do
4
+ let(:options) { {} }
5
+
6
+ let(:client) do
7
+ authorized_client.with(options)
8
+ end
9
+
10
+ let(:server) do
11
+ client.cluster.next_primary
12
+ end
13
+
14
+ let(:pool) do
15
+ server.pool
16
+ end
17
+
18
+ let(:populator) do
19
+ register_background_thread_object(
20
+ described_class.new(pool, pool.options)
21
+ )
22
+ end
23
+
24
+ before do
25
+ # We create our own populator to test; disable pool's background populator
26
+ # and clear the pool, so ours can run
27
+ pool.stop_populator
28
+ pool.clear
29
+ end
30
+
31
+ describe '#log_warn' do
32
+ it 'works' do
33
+ expect do
34
+ populator.log_warn('test warning')
35
+ end.not_to raise_error
36
+ end
37
+ end
38
+
39
+
40
+ describe '#run!' do
41
+ context 'when the min_pool_size is zero' do
42
+ let(:options) { {min_pool_size: 0} }
43
+
44
+ it 'calls populate on pool once' do
45
+ expect(pool).to receive(:populate).once.and_call_original
46
+ populator.run!
47
+ sleep 1
48
+ expect(populator.running?).to be true
49
+ end
50
+ end
51
+
52
+ context 'when the min_pool_size is greater than zero' do
53
+ let(:options) { {min_pool_size: 2, max_pool_size: 3} }
54
+
55
+ it 'calls populate on the pool multiple times' do
56
+ expect(pool).to receive(:populate).at_least(:once).and_call_original
57
+ populator.run!
58
+ sleep 1
59
+ expect(populator.running?).to be true
60
+ end
61
+
62
+ it 'populates the pool up to min_size' do
63
+ populator.run!
64
+ sleep 1
65
+ expect(pool.size).to eq 2
66
+ expect(populator.running?).to be true
67
+ end
68
+ end
69
+
70
+ context 'when populate raises a non socket related error' do
71
+ it 'does not terminate the thread' do
72
+ expect(pool).to receive(:populate).once.and_raise(Mongo::Auth::InvalidMechanism.new(""))
73
+ populator.run!
74
+ sleep 0.5
75
+ expect(populator.running?).to be true
76
+ end
77
+ end
78
+
79
+ context 'when populate raises a socket related error' do
80
+ it 'does not terminate the thread' do
81
+ expect(pool).to receive(:populate).once.and_raise(Mongo::Error::SocketError)
82
+ populator.run!
83
+ sleep 0.5
84
+ expect(populator.running?).to be true
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#stop' do
90
+ it 'stops calling populate on pool and terminates the thread' do
91
+ populator.run!
92
+
93
+ # let populator do work and wait on semaphore
94
+ sleep 0.5
95
+
96
+ expect(pool).not_to receive(:populate)
97
+ populator.stop!
98
+ expect(populator.running?).to be false
99
+ end
100
+ end
101
+ end
@@ -2,10 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongo::Server::ConnectionPool do
4
4
 
5
- let(:options) { {max_pool_size: 2} }
5
+ let(:options) { {} }
6
6
 
7
7
  let(:server_options) do
8
- SpecConfig.instance.test_options.merge(options)
8
+ SpecConfig.instance.ssl_options.merge(SpecConfig.instance.compressor_options)
9
+ .merge(SpecConfig.instance.retry_writes_options).merge(SpecConfig.instance.auth_options)
10
+ .merge(options)
9
11
  end
10
12
 
11
13
  let(:address) do
@@ -28,28 +30,39 @@ describe Mongo::Server::ConnectionPool do
28
30
  allow(cl).to receive(:app_metadata).and_return(app_metadata)
29
31
  allow(cl).to receive(:options).and_return({})
30
32
  allow(cl).to receive(:update_cluster_time)
33
+ allow(cl).to receive(:cluster_time).and_return(nil)
34
+ allow(cl).to receive(:run_sdam_flow)
31
35
  end
32
36
  end
33
37
 
34
38
  let(:server) do
35
- Mongo::Server.new(address, cluster, monitoring, listeners, server_options)
39
+ register_server(
40
+ Mongo::Server.new(address, cluster, monitoring, listeners,
41
+ {monitoring_io: false}.update(server_options)
42
+ ).tap do |server|
43
+ allow(server).to receive(:description).and_return(ClusterConfig.instance.primary_description)
44
+ end
45
+ )
36
46
  end
37
47
 
38
48
  let(:pool) do
39
- described_class.new(server)
49
+ register_pool(described_class.new(server, server_options))
40
50
  end
41
51
 
42
52
  describe '#initialize' do
43
53
 
44
54
  context 'when a min size is provided' do
45
-
46
- let(:pool) do
47
- described_class.new(server, :min_pool_size => 2)
55
+ let (:options) do
56
+ { min_pool_size: 2 }
48
57
  end
49
58
 
50
- it 'creates the pool with no connections' do
51
- expect(pool.size).to eq(0)
52
- expect(pool.available_count).to eq(0)
59
+ it 'creates the pool with min size connections' do
60
+ # Allow background thread to populate pool
61
+ pool
62
+ sleep 1
63
+
64
+ expect(pool.size).to eq(2)
65
+ expect(pool.available_count).to eq(2)
53
66
  end
54
67
 
55
68
  it 'does not use the same objects in the pool' do
@@ -58,9 +71,8 @@ describe Mongo::Server::ConnectionPool do
58
71
  end
59
72
 
60
73
  context 'when min size exceeds default max size' do
61
-
62
- let(:pool) do
63
- described_class.new(server, :min_pool_size => 10)
74
+ let (:options) do
75
+ { min_pool_size: 10 }
64
76
  end
65
77
 
66
78
  it 'sets max size to equal provided min size' do
@@ -70,10 +82,6 @@ describe Mongo::Server::ConnectionPool do
70
82
 
71
83
  context 'when no min size is provided' do
72
84
 
73
- let(:pool) do
74
- described_class.new(server)
75
- end
76
-
77
85
  it 'creates the pool with no connections' do
78
86
  expect(pool.size).to eq(0)
79
87
  expect(pool.available_count).to eq(0)
@@ -81,9 +89,8 @@ describe Mongo::Server::ConnectionPool do
81
89
  end
82
90
 
83
91
  context 'sizes given as min_size and max_size' do
84
-
85
- let(:pool) do
86
- described_class.new(server, min_size: 3, max_size: 7)
92
+ let (:options) do
93
+ { min_size: 3, max_size: 7 }
87
94
  end
88
95
 
89
96
  it 'sets sizes correctly' do
@@ -93,9 +100,8 @@ describe Mongo::Server::ConnectionPool do
93
100
  end
94
101
 
95
102
  context 'sizes given as min_pool_size and max_pool_size' do
96
-
97
- let(:pool) do
98
- described_class.new(server, min_pool_size: 3, max_pool_size: 7)
103
+ let (:options) do
104
+ { min_pool_size: 3, max_pool_size: 7 }
99
105
  end
100
106
 
101
107
  it 'sets sizes correctly' do
@@ -105,9 +111,8 @@ describe Mongo::Server::ConnectionPool do
105
111
  end
106
112
 
107
113
  context 'timeout given as wait_timeout' do
108
-
109
- let(:pool) do
110
- described_class.new(server, wait_timeout: 4)
114
+ let (:options) do
115
+ { wait_timeout: 4 }
111
116
  end
112
117
 
113
118
  it 'sets wait timeout correctly' do
@@ -116,9 +121,8 @@ describe Mongo::Server::ConnectionPool do
116
121
  end
117
122
 
118
123
  context 'timeout given as wait_queue_timeout' do
119
-
120
- let(:pool) do
121
- described_class.new(server, wait_queue_timeout: 4)
124
+ let (:options) do
125
+ { wait_queue_timeout: 4 }
122
126
  end
123
127
 
124
128
  it 'sets wait timeout correctly' do
@@ -128,11 +132,9 @@ describe Mongo::Server::ConnectionPool do
128
132
  end
129
133
 
130
134
  describe '#max_size' do
131
-
132
135
  context 'when a max pool size option is provided' do
133
-
134
- let(:pool) do
135
- described_class.new(server, :max_pool_size => 3)
136
+ let (:options) do
137
+ { max_pool_size: 3 }
136
138
  end
137
139
 
138
140
  it 'returns the max size' do
@@ -141,7 +143,6 @@ describe Mongo::Server::ConnectionPool do
141
143
  end
142
144
 
143
145
  context 'when no pool size option is provided' do
144
-
145
146
  it 'returns the default size' do
146
147
  expect(pool.max_size).to eq(5)
147
148
  end
@@ -159,11 +160,9 @@ describe Mongo::Server::ConnectionPool do
159
160
  end
160
161
 
161
162
  describe '#wait_timeout' do
162
-
163
163
  context 'when the wait timeout option is provided' do
164
-
165
- let(:pool) do
166
- described_class.new(server, :wait_queue_timeout => 3)
164
+ let (:options) do
165
+ { wait_queue_timeout: 3 }
167
166
  end
168
167
 
169
168
  it 'returns the wait timeout' do
@@ -172,9 +171,8 @@ describe Mongo::Server::ConnectionPool do
172
171
  end
173
172
 
174
173
  context 'when the wait timeout option is not provided' do
175
-
176
174
  it 'returns the default wait timeout' do
177
- expect(pool.wait_timeout).to eq(1)
175
+ expect(pool.wait_timeout).to eq(10)
178
176
  end
179
177
  end
180
178
  end
@@ -280,17 +278,17 @@ describe Mongo::Server::ConnectionPool do
280
278
  end
281
279
 
282
280
  describe '#check_in' do
283
-
284
281
  let!(:pool) do
285
282
  server.pool
286
283
  end
287
284
 
288
285
  after do
289
- expect(server).to receive(:pool).and_return(pool)
290
286
  server.disconnect!
291
287
  end
292
288
 
293
- let(:options) { {max_pool_size: 2} }
289
+ let(:options) do
290
+ { max_pool_size: 2 }
291
+ end
294
292
 
295
293
  let(:connection) do
296
294
  pool.check_out
@@ -348,7 +346,7 @@ describe Mongo::Server::ConnectionPool do
348
346
  end
349
347
 
350
348
  before do
351
- expect(connection.generation < pool.generation).to be true
349
+ expect(connection.generation).to be < pool.generation
352
350
  end
353
351
 
354
352
  it_behaves_like 'does not add connection to pool'
@@ -370,8 +368,6 @@ describe Mongo::Server::ConnectionPool do
370
368
  end
371
369
 
372
370
  context 'when pool is closed' do
373
- let(:connection) { pool.check_out }
374
-
375
371
  before do
376
372
  connection
377
373
  pool.close
@@ -385,10 +381,32 @@ describe Mongo::Server::ConnectionPool do
385
381
  expect(pool.instance_variable_get('@available_connections').length).to eq(0)
386
382
  end
387
383
  end
384
+
385
+ context 'when connection is checked in twice' do
386
+ it 'raises an ArgumentError and does not change pool state' do
387
+ pool.check_in(connection)
388
+ expect do
389
+ pool.check_in(connection)
390
+ end.to raise_error(ArgumentError, /Trying to check in a connection which is not currently checked out by this pool.*/)
391
+ expect(pool.size).to eq(1)
392
+ expect(pool.check_out).to eq(connection)
393
+ end
394
+ end
395
+
396
+ context 'when connection is checked in to a different pool' do
397
+ it 'raises an ArgumentError and does not change the state of either pool' do
398
+ pool_other = register_pool(described_class.new(server))
399
+
400
+ expect do
401
+ pool_other.check_in(connection)
402
+ end.to raise_error(ArgumentError, /Trying to check in a connection which was not checked out by this pool.*/)
403
+ expect(pool.size).to eq(1)
404
+ expect(pool_other.size).to eq(0)
405
+ end
406
+ end
388
407
  end
389
408
 
390
409
  describe '#check_out' do
391
-
392
410
  let!(:pool) do
393
411
  server.pool
394
412
  end
@@ -421,7 +439,7 @@ describe Mongo::Server::ConnectionPool do
421
439
 
422
440
  context 'when there is an available connection which is stale' do
423
441
  let(:options) do
424
- {max_pool_size: 2, max_idle_time: 0.1}
442
+ { max_pool_size: 2, max_idle_time: 0.1 }
425
443
  end
426
444
 
427
445
  let(:connection) do
@@ -436,10 +454,6 @@ describe Mongo::Server::ConnectionPool do
436
454
  pool.check_in(connection)
437
455
  end
438
456
 
439
- after do
440
- pool.close(force: true)
441
- end
442
-
443
457
  it 'closes stale connection and creates a new one' do
444
458
  expect(connection).to receive(:disconnect!)
445
459
  expect(Mongo::Server::Connection).to receive(:new).and_call_original
@@ -450,7 +464,7 @@ describe Mongo::Server::ConnectionPool do
450
464
  context 'when there are no available connections' do
451
465
 
452
466
  let(:options) do
453
- {max_pool_size: 1}
467
+ { max_pool_size: 1, min_pool_size: 0 }
454
468
  end
455
469
 
456
470
  context 'when the max size is not reached' do
@@ -503,12 +517,43 @@ describe Mongo::Server::ConnectionPool do
503
517
  end.to raise_error(Mongo::Error::PoolClosedError)
504
518
  end
505
519
  end
520
+
521
+ context 'when connection set up throws an error during check out' do
522
+ let(:client) do
523
+ authorized_client
524
+ end
525
+
526
+ let(:pool) do
527
+ client.cluster.next_primary.pool
528
+ end
529
+
530
+ it 'raises an error and emits ConnectionCheckOutFailedEvent' do
531
+ pool
532
+
533
+ subscriber = EventSubscriber.new
534
+ client.subscribe(Mongo::Monitoring::CONNECTION_POOL, subscriber)
535
+
536
+ subscriber.clear_events!
537
+ expect(Mongo::Auth).to receive(:get).at_least(:once).and_raise(Mongo::Error)
538
+ expect { pool.check_out }.to raise_error(Mongo::Error)
539
+ expect(pool.size).to eq(0)
540
+
541
+ checkout_failed_events = subscriber.published_events.select do |event|
542
+ event.is_a?(Mongo::Monitoring::Event::Cmap::ConnectionCheckOutFailed)
543
+ end
544
+ expect(checkout_failed_events.size).to eq(1)
545
+ expect(checkout_failed_events.first.reason).to be(:connection_error)
546
+ end
547
+ end
506
548
  end
507
549
 
508
550
  describe '#disconnect!' do
509
-
510
551
  def create_pool(min_pool_size)
511
- described_class.new(server, max_pool_size: 3, min_pool_size: min_pool_size).tap do |pool|
552
+ opts = SpecConfig.instance.test_options.merge(max_pool_size: 3, min_pool_size: min_pool_size)
553
+ described_class.new(server, opts).tap do |pool|
554
+ # kill background thread to test disconnect behavior
555
+ pool.stop_populator
556
+ expect(pool.instance_variable_get('@populator').running?).to be false
512
557
  # make pool be of size 2 so that it has enqueued connections
513
558
  # when told to disconnect
514
559
  c1 = pool.check_out
@@ -546,7 +591,7 @@ describe Mongo::Server::ConnectionPool do
546
591
 
547
592
  context 'min size is 0' do
548
593
  let(:pool) do
549
- create_pool(0)
594
+ register_pool(create_pool(0))
550
595
  end
551
596
 
552
597
  it_behaves_like 'disconnects and removes all connections in the pool and bumps generation'
@@ -554,7 +599,7 @@ describe Mongo::Server::ConnectionPool do
554
599
 
555
600
  context 'min size is not 0' do
556
601
  let(:pool) do
557
- create_pool(1)
602
+ register_pool(create_pool(1))
558
603
  end
559
604
 
560
605
  it_behaves_like 'disconnects and removes all connections in the pool and bumps generation'
@@ -597,15 +642,17 @@ describe Mongo::Server::ConnectionPool do
597
642
  end
598
643
 
599
644
  describe '#inspect' do
600
- let(:options) { {min_pool_size: 3, max_pool_size: 7, wait_timeout: 9, wait_queue_timeout: 9} }
645
+ let(:options) do
646
+ { min_pool_size: 3, max_pool_size: 7, wait_timeout: 9, wait_queue_timeout: 9 }
647
+ end
601
648
 
602
649
  let!(:pool) do
603
650
  server.pool
604
651
  end
605
652
 
606
653
  after do
607
- expect(server).to receive(:pool).and_return(pool)
608
654
  server.disconnect!
655
+ pool.close # this will no longer be needed after server disconnect kills bg thread
609
656
  end
610
657
 
611
658
  it 'includes the object id' do
@@ -625,7 +672,7 @@ describe Mongo::Server::ConnectionPool do
625
672
  end
626
673
 
627
674
  it 'includes the current size' do
628
- expect(pool.inspect).to include('current_size=0')
675
+ expect(pool.inspect).to include('current_size=')
629
676
  end
630
677
 
631
678
  =begin obsolete
@@ -654,7 +701,6 @@ describe Mongo::Server::ConnectionPool do
654
701
  end
655
702
 
656
703
  describe '#with_connection' do
657
-
658
704
  let!(:pool) do
659
705
  server.pool
660
706
  end
@@ -662,10 +708,11 @@ describe Mongo::Server::ConnectionPool do
662
708
  context 'when a connection cannot be checked out' do
663
709
 
664
710
  it 'does not add the connection to the pool' do
665
- pending 'Re-enable when connections are connected prior to being returned from check_out method'
666
-
711
+ # fails because with_connection raises the SocketError which is not caught anywhere
667
712
  allow(pool).to receive(:check_out).and_raise(Mongo::Error::SocketError)
668
- pool.with_connection { |c| c }
713
+ expect do
714
+ pool.with_connection { |c| c }
715
+ end.to raise_error(Mongo::Error::SocketError)
669
716
 
670
717
  expect(pool.size).to eq(0)
671
718
  end
@@ -685,13 +732,12 @@ describe Mongo::Server::ConnectionPool do
685
732
  end
686
733
 
687
734
  context 'when the connection does not finish authenticating before the thread is killed' do
688
-
689
735
  let!(:pool) do
690
736
  server.pool
691
737
  end
692
738
 
693
- let(:server_options) do
694
- { user: SpecConfig.instance.root_user.name, password: SpecConfig.instance.root_user.password }.merge(SpecConfig.instance.test_options).merge(max_pool_size: 1)
739
+ let(:options) do
740
+ { min_pool_size:0, max_pool_size: 1 }
695
741
  end
696
742
 
697
743
  before do
@@ -700,32 +746,23 @@ describe Mongo::Server::ConnectionPool do
700
746
  end
701
747
 
702
748
  it 'creates a new connection' do
703
- invoked = nil
704
-
705
749
  t = Thread.new do
706
- # Kill the thread when it's authenticating
707
- expect(Mongo::Auth).to receive(:get) do
708
- if Thread.current != t
709
- raise 'Auth invoked on unexpected thread'
750
+ expect {
751
+ pool.with_connection do |c|
752
+ expect(c).to receive(:connect!).and_raise(Mongo::Error)
753
+ c.send(:ensure_connected) { |socket| socket}
710
754
  end
711
- invoked = true
712
- t.kill
713
- raise 'Should not get here'
714
- end
715
- pool.with_connection do |c|
716
- c.send(:ensure_connected) { |socket| socket }
717
- end
755
+ }.to raise_error(Mongo::Error)
756
+ expect(pool.size).to be(0)
718
757
  end
719
758
  t.join
720
759
 
721
- #expect(Mongo::Auth).to receive(:get).and_call_original
722
760
  expect(pool.check_out).to be_a(Mongo::Server::Connection)
723
- expect(invoked).to be true
761
+ expect(pool.size).to be(1)
724
762
  end
725
763
  end
726
764
 
727
765
  describe '#close_idle_sockets' do
728
-
729
766
  let!(:pool) do
730
767
  server.pool
731
768
  end
@@ -733,7 +770,7 @@ describe Mongo::Server::ConnectionPool do
733
770
  context 'when there is a max_idle_time specified' do
734
771
 
735
772
  let(:options) do
736
- {max_pool_size: 2, max_idle_time: 0.5}
773
+ { max_pool_size: 2, max_idle_time: 0.5 }
737
774
  end
738
775
 
739
776
  after do
@@ -762,7 +799,7 @@ describe Mongo::Server::ConnectionPool do
762
799
  context 'when min size is 0' do
763
800
 
764
801
  let(:options) do
765
- {max_pool_size: 2, min_pool_size: 0, max_idle_time: 0.5}
802
+ { max_pool_size: 2, min_pool_size: 0, max_idle_time: 0.5 }
766
803
  end
767
804
 
768
805
  before do
@@ -782,9 +819,15 @@ describe Mongo::Server::ConnectionPool do
782
819
  end
783
820
 
784
821
  context 'when min size is > 0' do
822
+ before do
823
+ # Kill background thread to test close_idle_socket behavior
824
+ pool.stop_populator
825
+ expect(pool.instance_variable_get('@populator').running?).to be false
826
+ end
827
+
785
828
  context 'when more than the number of min_size are checked out' do
786
829
  let(:options) do
787
- {max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5}
830
+ { max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5 }
788
831
  end
789
832
 
790
833
  it 'closes and removes connections with idle sockets and does not connect new ones' do
@@ -813,7 +856,7 @@ describe Mongo::Server::ConnectionPool do
813
856
  context 'when between 0 and min_size number of connections are checked out' do
814
857
 
815
858
  let(:options) do
816
- {max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5}
859
+ { max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5 }
817
860
  end
818
861
 
819
862
  it 'closes and removes connections with idle sockets and does not connect new ones' do
@@ -855,8 +898,8 @@ describe Mongo::Server::ConnectionPool do
855
898
  end
856
899
 
857
900
  context 'when available connections include idle and non-idle ones' do
858
- let(:pool) do
859
- described_class.new(server, max_pool_size: 2, max_idle_time: 0.5)
901
+ let (:options) do
902
+ { max_pool_size: 2, max_idle_time: 0.5 }
860
903
  end
861
904
 
862
905
  let(:connection) do
@@ -866,25 +909,30 @@ describe Mongo::Server::ConnectionPool do
866
909
  end
867
910
 
868
911
  it 'disconnects all expired and only expired connections' do
869
- c1 = pool.check_out
870
- expect(c1).to receive(:disconnect!)
871
- c2 = pool.check_out
872
- expect(c2).not_to receive(:disconnect!)
912
+ # Since per-test cleanup will close the pool and disconnect
913
+ # the connection, we need to explicitly define the scope for the
914
+ # assertions
915
+ RSpec::Mocks.with_temporary_scope do
916
+ c1 = pool.check_out
917
+ expect(c1).to receive(:disconnect!)
918
+ c2 = pool.check_out
919
+ expect(c2).not_to receive(:disconnect!)
873
920
 
874
- pool.check_in(c1)
875
- Timecop.travel(Time.now + 1)
876
- pool.check_in(c2)
921
+ pool.check_in(c1)
922
+ Timecop.travel(Time.now + 1)
923
+ pool.check_in(c2)
877
924
 
878
- expect(pool.size).to eq(2)
879
- expect(pool.available_count).to eq(2)
925
+ expect(pool.size).to eq(2)
926
+ expect(pool.available_count).to eq(2)
880
927
 
881
- expect(c1).not_to receive(:connect!)
882
- expect(c2).not_to receive(:connect!)
928
+ expect(c1).not_to receive(:connect!)
929
+ expect(c2).not_to receive(:connect!)
883
930
 
884
- pool.close_idle_sockets
931
+ pool.close_idle_sockets
885
932
 
886
- expect(pool.size).to eq(1)
887
- expect(pool.available_count).to eq(1)
933
+ expect(pool.size).to eq(1)
934
+ expect(pool.available_count).to eq(1)
935
+ end
888
936
  end
889
937
  end
890
938
 
@@ -897,13 +945,114 @@ describe Mongo::Server::ConnectionPool do
897
945
  conn
898
946
  end
899
947
 
948
+ it 'does not close any sockets' do
949
+ # Since per-test cleanup will close the pool and disconnect
950
+ # the connection, we need to explicitly define the scope for the
951
+ # assertions
952
+ RSpec::Mocks.with_temporary_scope do
953
+ expect(connection).not_to receive(:disconnect!)
954
+ pool.close_idle_sockets
955
+ expect(connection.connected?).to be(true)
956
+ end
957
+ end
958
+ end
959
+ end
960
+
961
+ describe '#populate' do
962
+ before do
963
+ # Disable the populator and clear the pool to isolate populate behavior
964
+ pool.stop_populator
965
+ pool.clear
966
+ end
967
+
968
+ let(:options) { {min_pool_size: 2, max_pool_size: 3} }
969
+
970
+ context 'when pool size is at least min_pool_size' do
900
971
  before do
901
- expect(connection).not_to receive(:disconnect!)
902
- pool.close_idle_sockets
972
+ first_connection = pool.check_out
973
+ second_connection = pool.check_out
974
+ expect(pool.size).to eq 2
975
+ expect(pool.available_count).to eq 0
903
976
  end
904
977
 
905
- it 'does not close any sockets' do
906
- expect(connection.connected?).to be(true)
978
+ it 'does not create a connection and returns false' do
979
+ expect(pool.populate).to be false
980
+ expect(pool.size).to eq 2
981
+ expect(pool.available_count).to eq 0
982
+ end
983
+ end
984
+
985
+ context 'when pool size is less than min_pool_size' do
986
+ before do
987
+ first_connection = pool.check_out
988
+ expect(pool.size).to eq 1
989
+ expect(pool.available_count).to eq 0
990
+ end
991
+
992
+ it 'creates one connection, connects it, and returns true' do
993
+ expect(pool.populate).to be true
994
+ expect(pool.size).to eq 2
995
+ expect(pool.available_count).to eq 1
996
+ end
997
+ end
998
+
999
+ context 'when pool is closed' do
1000
+ before do
1001
+ pool.close
1002
+ end
1003
+
1004
+ it 'does not create a connection and returns false' do
1005
+ expect(pool.populate).to be false
1006
+
1007
+ # Can't just check pool size; size errors when pool is closed
1008
+ expect(pool.instance_variable_get('@available_connections').length).to eq(0)
1009
+ expect(pool.instance_variable_get('@checked_out_connections').length).to eq(0)
1010
+ expect(pool.instance_variable_get('@pending_connections').length).to eq(0)
1011
+ end
1012
+ end
1013
+
1014
+ context 'when connect fails with socket related error once' do
1015
+ before do
1016
+ i = 0
1017
+ expect(pool).to receive(:connect_connection).exactly(:twice).and_wrap_original{ |m, *args|
1018
+ i += 1
1019
+ if i == 1
1020
+ raise Mongo::Error::SocketError
1021
+ else
1022
+ m.call(*args)
1023
+ end
1024
+ }
1025
+ expect(pool.size).to eq 0
1026
+ end
1027
+
1028
+ it 'retries then succeeds in creating a connection' do
1029
+ expect(pool.populate).to be true
1030
+ expect(pool.size).to eq 1
1031
+ expect(pool.available_count).to eq 1
1032
+ end
1033
+ end
1034
+
1035
+ context 'when connect fails with socket related error twice' do
1036
+ before do
1037
+ expect(pool).to receive(:connect_connection).exactly(:twice).and_raise(Mongo::Error::SocketError)
1038
+ expect(pool.size).to eq 0
1039
+ end
1040
+
1041
+ it 'retries, raises the second error, and fails to create a connection' do
1042
+ expect{ pool.populate }.to raise_error(Mongo::Error::SocketError)
1043
+ expect(pool.size).to eq 0
1044
+ end
1045
+ end
1046
+
1047
+ context 'when connect fails with non socket related error' do
1048
+ before do
1049
+ expect(pool).to receive(:connect_connection).once.and_raise(Mongo::Auth::InvalidMechanism.new(""))
1050
+ expect(pool.size).to eq 0
1051
+ end
1052
+
1053
+ it 'does not retry, raises the error, and fails to create a connection' do
1054
+ expect{ pool.populate }.to raise_error(Mongo::Auth::InvalidMechanism)
1055
+ expect(pool.size).to eq 0
907
1056
  end
908
1057
  end
909
1058
  end