mongo 2.4.3 → 2.5.0.beta

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 (235) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -2
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +3 -2
  5. data/lib/mongo/auth/cr.rb +6 -4
  6. data/lib/mongo/auth/cr/conversation.rb +33 -17
  7. data/lib/mongo/auth/ldap.rb +4 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +19 -9
  9. data/lib/mongo/auth/scram.rb +7 -4
  10. data/lib/mongo/auth/scram/conversation.rb +62 -24
  11. data/lib/mongo/auth/user.rb +10 -0
  12. data/lib/mongo/auth/user/view.rb +44 -22
  13. data/lib/mongo/auth/x509.rb +4 -2
  14. data/lib/mongo/auth/x509/conversation.rb +19 -9
  15. data/lib/mongo/bulk_write.rb +33 -27
  16. data/lib/mongo/bulk_write/combineable.rb +5 -0
  17. data/lib/mongo/bulk_write/transformable.rb +2 -0
  18. data/lib/mongo/bulk_write/validatable.rb +4 -0
  19. data/lib/mongo/client.rb +123 -12
  20. data/lib/mongo/cluster.rb +52 -11
  21. data/lib/mongo/cluster/app_metadata.rb +8 -2
  22. data/lib/mongo/cluster/cursor_reaper.rb +0 -1
  23. data/lib/mongo/cluster/topology.rb +1 -1
  24. data/lib/mongo/collection.rb +114 -27
  25. data/lib/mongo/collection/view.rb +8 -2
  26. data/lib/mongo/collection/view/aggregation.rb +11 -7
  27. data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
  28. data/lib/mongo/collection/view/builder/find_command.rb +5 -3
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
  30. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  31. data/lib/mongo/collection/view/change_stream.rb +160 -0
  32. data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
  33. data/lib/mongo/collection/view/iterable.rb +11 -10
  34. data/lib/mongo/collection/view/map_reduce.rb +22 -18
  35. data/lib/mongo/collection/view/readable.rb +51 -37
  36. data/lib/mongo/collection/view/writable.rb +72 -40
  37. data/lib/mongo/cursor.rb +25 -4
  38. data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
  39. data/lib/mongo/database.rb +22 -11
  40. data/lib/mongo/database/view.rb +16 -12
  41. data/lib/mongo/error.rb +5 -0
  42. data/lib/mongo/error/invalid_session.rb +36 -0
  43. data/lib/mongo/error/missing_resume_token.rb +39 -0
  44. data/lib/mongo/error/operation_failure.rb +17 -0
  45. data/lib/mongo/error/parser.rb +3 -2
  46. data/lib/mongo/error/unknown_payload_type.rb +41 -0
  47. data/lib/mongo/error/unsupported_array_filters.rb +51 -0
  48. data/lib/mongo/error/unsupported_message_type.rb +23 -0
  49. data/lib/mongo/grid/fs_bucket.rb +5 -4
  50. data/lib/mongo/grid/stream/read.rb +3 -2
  51. data/lib/mongo/grid/stream/write.rb +2 -2
  52. data/lib/mongo/index/view.rb +35 -25
  53. data/lib/mongo/monitoring/event/secure.rb +14 -0
  54. data/lib/mongo/operation.rb +16 -0
  55. data/lib/mongo/operation/commands.rb +1 -0
  56. data/lib/mongo/operation/commands/aggregate.rb +9 -5
  57. data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
  58. data/lib/mongo/operation/commands/collections_info.rb +6 -6
  59. data/lib/mongo/operation/commands/command.rb +2 -1
  60. data/lib/mongo/operation/commands/create.rb +6 -2
  61. data/lib/mongo/operation/commands/drop.rb +6 -2
  62. data/lib/mongo/operation/commands/drop_database.rb +6 -2
  63. data/lib/mongo/operation/commands/explain.rb +27 -0
  64. data/lib/mongo/operation/commands/explain/result.rb +52 -0
  65. data/lib/mongo/operation/commands/indexes.rb +1 -1
  66. data/lib/mongo/operation/commands/list_collections.rb +1 -1
  67. data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
  68. data/lib/mongo/operation/commands/list_indexes.rb +1 -1
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
  70. data/lib/mongo/operation/commands/map_reduce.rb +8 -4
  71. data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
  72. data/lib/mongo/operation/commands/user_query.rb +1 -1
  73. data/lib/mongo/operation/commands/users_info.rb +6 -2
  74. data/lib/mongo/operation/executable.rb +4 -1
  75. data/lib/mongo/operation/read_preference.rb +10 -5
  76. data/lib/mongo/operation/result.rb +26 -2
  77. data/lib/mongo/operation/specifiable.rb +13 -1
  78. data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
  79. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
  80. data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
  81. data/lib/mongo/operation/write/command/create_index.rb +6 -1
  82. data/lib/mongo/operation/write/command/delete.rb +28 -4
  83. data/lib/mongo/operation/write/command/drop_index.rb +6 -1
  84. data/lib/mongo/operation/write/command/insert.rb +22 -18
  85. data/lib/mongo/operation/write/command/update.rb +24 -9
  86. data/lib/mongo/operation/write/command/writable.rb +14 -1
  87. data/lib/mongo/operation/write/insert.rb +4 -1
  88. data/lib/mongo/operation/write/insert/result.rb +2 -2
  89. data/lib/mongo/operation/write/update.rb +7 -1
  90. data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
  91. data/lib/mongo/protocol.rb +3 -0
  92. data/lib/mongo/protocol/bit_vector.rb +2 -2
  93. data/lib/mongo/protocol/compressed.rb +135 -0
  94. data/lib/mongo/protocol/delete.rb +8 -6
  95. data/lib/mongo/protocol/get_more.rb +8 -6
  96. data/lib/mongo/protocol/insert.rb +8 -6
  97. data/lib/mongo/protocol/kill_cursors.rb +8 -6
  98. data/lib/mongo/protocol/message.rb +31 -3
  99. data/lib/mongo/protocol/msg.rb +172 -0
  100. data/lib/mongo/protocol/query.rb +26 -6
  101. data/lib/mongo/protocol/registry.rb +76 -0
  102. data/lib/mongo/protocol/reply.rb +10 -5
  103. data/lib/mongo/protocol/serializers.rb +224 -0
  104. data/lib/mongo/protocol/update.rb +8 -6
  105. data/lib/mongo/retryable.rb +4 -2
  106. data/lib/mongo/server.rb +6 -3
  107. data/lib/mongo/server/connectable.rb +1 -1
  108. data/lib/mongo/server/connection.rb +30 -8
  109. data/lib/mongo/server/description.rb +25 -1
  110. data/lib/mongo/server/description/features.rb +4 -1
  111. data/lib/mongo/server/monitor.rb +5 -0
  112. data/lib/mongo/server/monitor/connection.rb +50 -2
  113. data/lib/mongo/server_selector/nearest.rb +10 -4
  114. data/lib/mongo/server_selector/primary.rb +20 -0
  115. data/lib/mongo/server_selector/primary_preferred.rb +10 -4
  116. data/lib/mongo/server_selector/secondary.rb +10 -4
  117. data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
  118. data/lib/mongo/session.rb +180 -0
  119. data/lib/mongo/session/server_session.rb +73 -0
  120. data/lib/mongo/session/session_pool.rb +161 -0
  121. data/lib/mongo/uri.rb +11 -0
  122. data/lib/mongo/version.rb +1 -1
  123. data/mongo.gemspec +2 -1
  124. data/spec/mongo/auth/cr_spec.rb +12 -0
  125. data/spec/mongo/auth/ldap_spec.rb +2 -0
  126. data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
  127. data/spec/mongo/auth/scram_spec.rb +25 -1
  128. data/spec/mongo/auth/user/view_spec.rb +268 -76
  129. data/spec/mongo/auth/x509_spec.rb +2 -0
  130. data/spec/mongo/bulk_write_spec.rb +435 -5
  131. data/spec/mongo/client_spec.rb +356 -39
  132. data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
  133. data/spec/mongo/cluster_spec.rb +176 -0
  134. data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
  135. data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
  136. data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
  137. data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
  138. data/spec/mongo/collection/view/readable_spec.rb +3 -12
  139. data/spec/mongo/collection_spec.rb +1048 -42
  140. data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
  141. data/spec/mongo/cursor_spec.rb +2 -2
  142. data/spec/mongo/database_spec.rb +50 -1
  143. data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
  144. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  145. data/spec/mongo/index/view_spec.rb +146 -8
  146. data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
  147. data/spec/mongo/operation/read/query_spec.rb +2 -1
  148. data/spec/mongo/operation/specifiable_spec.rb +2 -2
  149. data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
  150. data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
  151. data/spec/mongo/operation/write/command/update_spec.rb +93 -10
  152. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  153. data/spec/mongo/operation/write/insert_spec.rb +1 -1
  154. data/spec/mongo/operation/write/update_spec.rb +1 -1
  155. data/spec/mongo/protocol/compressed_spec.rb +66 -0
  156. data/spec/mongo/protocol/delete_spec.rb +14 -0
  157. data/spec/mongo/protocol/get_more_spec.rb +14 -0
  158. data/spec/mongo/protocol/insert_spec.rb +14 -0
  159. data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
  160. data/spec/mongo/protocol/msg_spec.rb +499 -0
  161. data/spec/mongo/protocol/query_spec.rb +45 -0
  162. data/spec/mongo/protocol/registry_spec.rb +31 -0
  163. data/spec/mongo/protocol/reply_spec.rb +14 -0
  164. data/spec/mongo/protocol/update_spec.rb +14 -0
  165. data/spec/mongo/retryable_spec.rb +6 -2
  166. data/spec/mongo/sdam_spec.rb +4 -0
  167. data/spec/mongo/server/connection_spec.rb +4 -2
  168. data/spec/mongo/server/description_spec.rb +28 -1
  169. data/spec/mongo/session/server_session_spec.rb +16 -0
  170. data/spec/mongo/session/session_pool_spec.rb +194 -0
  171. data/spec/mongo/uri_spec.rb +31 -2
  172. data/spec/spec_helper.rb +104 -0
  173. data/spec/support/authorization.rb +6 -1
  174. data/spec/support/crud.rb +3 -1
  175. data/spec/support/crud/write.rb +6 -1
  176. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
  177. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
  178. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
  179. data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
  180. data/spec/support/sdam/rs/discover_passives.yml +2 -2
  181. data/spec/support/sdam/rs/discover_primary.yml +1 -1
  182. data/spec/support/sdam/rs/discover_secondary.yml +1 -1
  183. data/spec/support/sdam/rs/discovery.yml +4 -4
  184. data/spec/support/sdam/rs/equal_electionids.yml +1 -0
  185. data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
  186. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
  187. data/spec/support/sdam/rs/ls_timeout.yml +88 -0
  188. data/spec/support/sdam/rs/member_reconfig.yml +2 -2
  189. data/spec/support/sdam/rs/member_standalone.yml +2 -2
  190. data/spec/support/sdam/rs/new_primary.yml +2 -2
  191. data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
  192. data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
  193. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
  194. data/spec/support/sdam/rs/non_rs_member.yml +1 -1
  195. data/spec/support/sdam/rs/normalize_case.yml +1 -1
  196. data/spec/support/sdam/rs/null_election_id.yml +4 -0
  197. data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
  198. data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
  199. data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
  200. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
  201. data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
  202. data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
  203. data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
  204. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
  205. data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
  206. data/spec/support/sdam/rs/response_from_removed.yml +2 -2
  207. data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
  208. data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
  209. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
  210. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
  211. data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
  212. data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
  213. data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
  214. data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
  215. data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
  216. data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
  217. data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
  218. data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
  219. data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
  220. data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  225. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  226. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  227. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  228. data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
  229. data/spec/support/sdam/single/not_ok_response.yml +1 -1
  230. data/spec/support/sdam/single/standalone_removed.yml +1 -1
  231. data/spec/support/sdam/single/unavailable_seed.yml +1 -1
  232. data/spec/support/server_discovery_and_monitoring.rb +4 -0
  233. data/spec/support/shared/session.rb +236 -0
  234. metadata +53 -15
  235. metadata.gz.sig +0 -0
@@ -296,4 +296,49 @@ describe Mongo::Protocol::Query do
296
296
  end
297
297
  end
298
298
  end
299
+
300
+ describe '#registry' do
301
+
302
+ context 'when the class is loaded' do
303
+
304
+ it 'registers the op code in the Protocol Registry' do
305
+ expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
306
+ end
307
+
308
+ it 'creates an #op_code instance method' do
309
+ expect(message.op_code).to eq(described_class::OP_CODE)
310
+ end
311
+ end
312
+ end
313
+
314
+ describe '#compress' do
315
+
316
+ context 'when the selector represents a command that can be compressed' do
317
+
318
+ let(:selector) do
319
+ { ping: 1 }
320
+ end
321
+
322
+ it 'returns a compressed message' do
323
+ expect(message.compress!('zlib')).to be_a(Mongo::Protocol::Compressed)
324
+ end
325
+ end
326
+
327
+ context 'when the selector represents a command for which compression is not allowed' do
328
+
329
+ Mongo::Monitoring::Event::Secure::REDACTED_COMMANDS.each do |command|
330
+
331
+ let(:selector) do
332
+ { command => 1 }
333
+ end
334
+
335
+ context "when the command is #{command}" do
336
+
337
+ it 'does not allow compression for the command' do
338
+ expect(message.compress!('zlib')).to be(message)
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
299
344
  end
@@ -0,0 +1,31 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongo::Protocol::Registry do
4
+
5
+ describe ".get" do
6
+
7
+ context "when the type has a correspoding class" do
8
+
9
+ before do
10
+ described_class.register(Mongo::Protocol::Query::OP_CODE, Mongo::Protocol::Query)
11
+ end
12
+
13
+ let(:klass) do
14
+ described_class.get(Mongo::Protocol::Query::OP_CODE, "message")
15
+ end
16
+
17
+ it "returns the class" do
18
+ expect(klass).to eq(Mongo::Protocol::Query)
19
+ end
20
+ end
21
+
22
+ context "when the type has no corresponding class" do
23
+
24
+ it "raises an error" do
25
+ expect {
26
+ described_class.get(-100)
27
+ }.to raise_error(Mongo::Error::UnsupportedMessageType)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -178,4 +178,18 @@ describe Mongo::Protocol::Reply do
178
178
  end
179
179
  end
180
180
  end
181
+
182
+ describe '#registry' do
183
+
184
+ context 'when the class is loaded' do
185
+
186
+ it 'registers the op code in the Protocol Registry' do
187
+ expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
188
+ end
189
+
190
+ it 'creates an #op_code instance method' do
191
+ expect(reply.op_code).to eq(described_class::OP_CODE)
192
+ end
193
+ end
194
+ end
181
195
  end
@@ -183,4 +183,18 @@ describe Mongo::Protocol::Update do
183
183
  end
184
184
  end
185
185
  end
186
+
187
+ describe '#registry' do
188
+
189
+ context 'when the class is loaded' do
190
+
191
+ it 'registers the op code in the Protocol Registry' do
192
+ expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
193
+ end
194
+
195
+ it 'creates an #op_code instance method' do
196
+ expect(message.op_code).to eq(described_class::OP_CODE)
197
+ end
198
+ end
199
+ end
186
200
  end
@@ -29,7 +29,7 @@ describe Mongo::Retryable do
29
29
  end
30
30
 
31
31
  def write
32
- write_with_retry do
32
+ write_with_retry(nil, Proc.new { cluster.next_primary }) do
33
33
  operation.execute
34
34
  end
35
35
  end
@@ -41,7 +41,11 @@ describe Mongo::Retryable do
41
41
  end
42
42
 
43
43
  let(:cluster) do
44
- double('cluster')
44
+ double('cluster', next_primary: server_selector)
45
+ end
46
+
47
+ let(:server_selector) do
48
+ double('server_selector', select_server: double('server'))
45
49
  end
46
50
 
47
51
  let(:retryable) do
@@ -63,6 +63,10 @@ describe 'Server Discovery and Monitoring' do
63
63
  expect(@client.cluster.replica_set_name).to eq(phase.outcome.set_name)
64
64
  end
65
65
 
66
+ it "sets the cluster logical session timeout minutes to #{phase.outcome.logical_session_timeout.inspect}" do
67
+ expect(@client.cluster.logical_session_timeout).to eq(phase.outcome.logical_session_timeout)
68
+ end
69
+
66
70
  it "has the expected servers in the cluster" do
67
71
  expect(cluster_addresses).to eq(phase_addresses)
68
72
  end
@@ -22,6 +22,8 @@ describe Mongo::Server::Connection do
22
22
  double('cluster').tap do |cl|
23
23
  allow(cl).to receive(:topology).and_return(topology)
24
24
  allow(cl).to receive(:app_metadata).and_return(app_metadata)
25
+ allow(cl).to receive(:cluster_time).and_return(nil)
26
+ allow(cl).to receive(:update_cluster_time)
25
27
  end
26
28
  end
27
29
 
@@ -334,11 +336,11 @@ describe Mongo::Server::Connection do
334
336
  it 'closes the socket and does not use it for subsequent requests' do
335
337
  t = Thread.new {
336
338
  # Kill the thread just before the reply is read
337
- allow(Mongo::Protocol::Reply).to receive(:deserialize_header) { t.kill }
339
+ allow(Mongo::Protocol::Message).to receive(:deserialize_header) { t.kill }
338
340
  connection.dispatch([ query_bob ])
339
341
  }
340
342
  t.join
341
- allow(Mongo::Protocol::Reply).to receive(:deserialize_header).and_call_original
343
+ allow(Mongo::Protocol::Message).to receive(:deserialize_header).and_call_original
342
344
  expect(connection.dispatch([ query_alice ]).documents.first['name']).to eq('alice')
343
345
  end
344
346
  end
@@ -24,6 +24,7 @@ describe Mongo::Server::Description do
24
24
  'minWireVersion' => 0,
25
25
  'localTime' => Time.now,
26
26
  'lastWrite' => { 'lastWriteDate' => Time.now },
27
+ 'logicalSessionTimeoutMinutes' => 7,
27
28
  'ok' => 1
28
29
  }
29
30
  end
@@ -832,6 +833,31 @@ describe Mongo::Server::Description do
832
833
  end
833
834
  end
834
835
 
836
+ describe '#logical_session_timeout_minutes' do
837
+
838
+ context 'when a logical session timeout value is in the config' do
839
+
840
+ let(:description) do
841
+ described_class.new(address, replica)
842
+ end
843
+
844
+ it 'returns the logical session timeout value' do
845
+ expect(description.logical_session_timeout).to eq(7)
846
+ end
847
+ end
848
+
849
+ context 'when a logical session timeout value is not in the config' do
850
+
851
+ let(:description) do
852
+ described_class.new(address, { 'ismaster' => true, 'ok' => 1 })
853
+ end
854
+
855
+ it 'returns nil' do
856
+ expect(description.logical_session_timeout).to be(nil)
857
+ end
858
+ end
859
+ end
860
+
835
861
  describe '#==' do
836
862
 
837
863
  let(:description) do
@@ -841,7 +867,8 @@ describe Mongo::Server::Description do
841
867
  let(:other) do
842
868
  described_class.new(address, replica.merge(
843
869
  'localTime' => 1,
844
- 'lastWrite' => { 'lastWriteDate' => 1 }
870
+ 'lastWrite' => { 'lastWriteDate' => 1 },
871
+ 'operationTime' => 1
845
872
  ))
846
873
  end
847
874
 
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Session::ServerSession do
4
+
5
+ describe '#initialize' do
6
+
7
+ it 'sets the last use variable to the current time' do
8
+ expect(described_class.new.last_use).to be_within(0.2).of(Time.now)
9
+ end
10
+
11
+ it 'sets a UUID as the session id' do
12
+ expect(described_class.new.session_id).to be_a(BSON::Document)
13
+ expect(described_class.new.session_id[:id]).to be_a(BSON::Binary)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Session::SessionPool do
4
+
5
+ describe '.create' do
6
+
7
+ let(:client) do
8
+ authorized_client
9
+ end
10
+
11
+ let!(:pool) do
12
+ described_class.create(client)
13
+ end
14
+
15
+ it 'creates a session pool' do
16
+ expect(pool).to be_a(Mongo::Session::SessionPool)
17
+ end
18
+
19
+ it 'adds the pool as an instance variable on the client' do
20
+ expect(client.instance_variable_get(:@session_pool)).to eq(pool)
21
+ end
22
+ end
23
+
24
+ describe '#initialize' do
25
+
26
+ let(:pool) do
27
+ described_class.new(authorized_client)
28
+ end
29
+
30
+ it 'sets the client' do
31
+ expect(pool.instance_variable_get(:@client)).to be(authorized_client)
32
+ end
33
+ end
34
+
35
+ describe 'checkout', if: sessions_enabled? do
36
+
37
+ let(:pool) do
38
+ described_class.new(authorized_client)
39
+ end
40
+
41
+ context 'when a session is checked out' do
42
+
43
+ let!(:session_a) do
44
+ pool.checkout
45
+ end
46
+
47
+ let!(:session_b) do
48
+ pool.checkout
49
+ end
50
+
51
+ before do
52
+ pool.checkin(session_a)
53
+ pool.checkin(session_b)
54
+ end
55
+
56
+ it 'is returned to the front of the queue' do
57
+ expect(pool.checkout).to be(session_b)
58
+ expect(pool.checkout).to be(session_a)
59
+ end
60
+ end
61
+
62
+ context 'when there are sessions about to expire in the queue' do
63
+
64
+ let(:old_session_a) do
65
+ pool.checkout
66
+ end
67
+
68
+ let(:old_session_b) do
69
+ pool.checkout
70
+ end
71
+
72
+ before do
73
+ pool.checkin(old_session_a)
74
+ pool.checkin(old_session_b)
75
+ allow(old_session_a).to receive(:last_use).and_return(Time.now - 1800)
76
+ allow(old_session_b).to receive(:last_use).and_return(Time.now - 1800)
77
+ end
78
+
79
+ context 'when a session is checked out' do
80
+
81
+ let(:checked_out_session) do
82
+ pool.checkout
83
+ end
84
+
85
+ it 'disposes of the old session and returns a new one' do
86
+ expect(checked_out_session).not_to be(old_session_a)
87
+ expect(checked_out_session).not_to be(old_session_b)
88
+ expect(pool.instance_variable_get(:@queue)).to be_empty
89
+ end
90
+ end
91
+ end
92
+
93
+ context 'when a sessions that is about to expire is checked in' do
94
+
95
+ let(:old_session_a) do
96
+ pool.checkout
97
+ end
98
+
99
+ let(:old_session_b) do
100
+ pool.checkout
101
+ end
102
+
103
+ before do
104
+ allow(old_session_a).to receive(:last_use).and_return(Time.now - 1800)
105
+ allow(old_session_b).to receive(:last_use).and_return(Time.now - 1800)
106
+ pool.checkin(old_session_a)
107
+ pool.checkin(old_session_b)
108
+ end
109
+
110
+ it 'disposes of the old sessions instead of adding them to the pool' do
111
+ expect(pool.checkout).not_to be(old_session_a)
112
+ expect(pool.checkout).not_to be(old_session_b)
113
+ expect(pool.instance_variable_get(:@queue)).to be_empty
114
+ end
115
+ end
116
+ end
117
+
118
+ describe '#end_sessions', if: sessions_enabled? do
119
+
120
+ let(:pool) do
121
+ described_class.create(client)
122
+ end
123
+
124
+ let!(:session_a) do
125
+ pool.checkout
126
+ end
127
+
128
+ let!(:session_b) do
129
+ pool.checkout
130
+ end
131
+
132
+ let(:client) do
133
+ authorized_client.with(heartbeat_frequency: 100).tap do |cl|
134
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
135
+ end
136
+ end
137
+
138
+ let(:subscriber) do
139
+ EventSubscriber.new
140
+ end
141
+
142
+ before do
143
+ client.database.command(ping: 1)
144
+ pool.checkin(session_a)
145
+ pool.checkin(session_b)
146
+ pool.end_sessions
147
+ end
148
+
149
+ after do
150
+ client.close
151
+ end
152
+
153
+ let(:end_sessions_command) do
154
+ subscriber.started_events.find { |c| c.command_name == :endSessions}
155
+ end
156
+
157
+ it 'sends the endSessions command with all the session ids' do
158
+ expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_a.session_id))
159
+ expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_b.session_id))
160
+ end
161
+
162
+ context 'when talking to a mongos', if: sessions_enabled? && sharded? do
163
+
164
+ it 'sends the endSessions command with all the session ids' do
165
+ expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_a.session_id))
166
+ expect(end_sessions_command.command[:ids]).to include(BSON::Document.new(session_b.session_id))
167
+ expect(end_sessions_command.command[:$clusterTime]).to eq(client.cluster.cluster_time)
168
+ end
169
+ end
170
+
171
+ context 'when the number of ids is larger than 10_000' do
172
+
173
+ before do
174
+ queue = []
175
+ 10_001.times do |i|
176
+ queue << double('session', session_id: i)
177
+ end
178
+ pool.instance_variable_set(:@queue, queue)
179
+ expect(Mongo::Operation::Commands::Command).to receive(:new).at_least(:twice)
180
+ end
181
+
182
+ let(:end_sessions_commands) do
183
+ subscriber.started_events.select { |c| c.command_name == :endSessions}
184
+ end
185
+
186
+ it 'sends the command more than once' do
187
+ pool.end_sessions
188
+ # expect(end_sessions_commands.size).to eq(2)
189
+ # expect(end_sessions_commands[0].command[:ids]).to eq([*0...10_000])
190
+ # expect(end_sessions_commands[1].command[:ids]).to eq([10_000])
191
+ end
192
+ end
193
+ end
194
+ end
@@ -566,7 +566,7 @@ describe Mongo::URI do
566
566
 
567
567
  it 'raises an exception when read preference is accessed on the client' do
568
568
  expect {
569
- Mongo::Client.new(string).read_preference
569
+ Mongo::Client.new(string).server_selector
570
570
  }.to raise_exception(Mongo::Error::InvalidServerPreference)
571
571
  end
572
572
  end
@@ -578,7 +578,7 @@ describe Mongo::URI do
578
578
  end
579
579
 
580
580
  it 'does not raise an exception until the read preference is used' do
581
- expect(Mongo::Client.new(string).read_preference).to be_a(Mongo::ServerSelector::Secondary)
581
+ expect(Mongo::Client.new(string).read_preference).to eq(BSON::Document.new(mode: :secondary, max_staleness: 89))
582
582
  end
583
583
  end
584
584
  end
@@ -929,5 +929,34 @@ describe Mongo::URI do
929
929
  expect(Mongo::Client.new(string).options[:app_name]).to eq(:reports)
930
930
  end
931
931
  end
932
+
933
+ context 'when a supported compressors option is provided' do
934
+ let(:options) { "compressors=zlib" }
935
+
936
+ it 'sets the compressors as an array on the client' do
937
+ expect(Mongo::Client.new(string).options[:compressors]).to eq(['zlib'])
938
+ end
939
+ end
940
+
941
+ context 'when a non-supported compressors option is provided' do
942
+ let(:options) { "compressors=snoopy" }
943
+
944
+ let(:client) do
945
+ Mongo::Client.new(string)
946
+ end
947
+
948
+ it 'sets no compressors on the client and warns' do
949
+ expect(Mongo::Logger.logger).to receive(:warn)
950
+ expect(client.options[:compressors]).to be_nil
951
+ end
952
+ end
953
+
954
+ context 'when a zlibCompressionLevel option is provided' do
955
+ let(:options) { "zlibCompressionLevel=6" }
956
+
957
+ it 'sets the zlib compression level on the client' do
958
+ expect(Mongo::Client.new(string).options[:zlib_compression_level]).to eq(6)
959
+ end
960
+ end
932
961
  end
933
962
  end