mongo 2.10.5 → 2.11.0.rc0

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 (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