mongo 2.9.2 → 2.10.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 (227) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +1 -0
  5. data/lib/mongo/auth/user/view.rb +4 -4
  6. data/lib/mongo/bulk_write.rb +14 -8
  7. data/lib/mongo/bulk_write/result.rb +1 -1
  8. data/lib/mongo/bulk_write/result_combiner.rb +2 -2
  9. data/lib/mongo/bulk_write/transformable.rb +17 -9
  10. data/lib/mongo/client.rb +107 -16
  11. data/lib/mongo/cluster.rb +47 -25
  12. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
  13. data/lib/mongo/cluster_time.rb +139 -0
  14. data/lib/mongo/collection.rb +84 -25
  15. data/lib/mongo/collection/view.rb +7 -3
  16. data/lib/mongo/collection/view/aggregation.rb +4 -4
  17. data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
  18. data/lib/mongo/collection/view/builder/find_command.rb +4 -1
  19. data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
  20. data/lib/mongo/collection/view/change_stream.rb +54 -66
  21. data/lib/mongo/collection/view/iterable.rb +2 -2
  22. data/lib/mongo/collection/view/map_reduce.rb +6 -4
  23. data/lib/mongo/collection/view/readable.rb +36 -16
  24. data/lib/mongo/collection/view/writable.rb +68 -22
  25. data/lib/mongo/cursor.rb +87 -20
  26. data/lib/mongo/database.rb +47 -43
  27. data/lib/mongo/database/view.rb +54 -11
  28. data/lib/mongo/error.rb +13 -4
  29. data/lib/mongo/error/invalid_write_concern.rb +2 -2
  30. data/lib/mongo/error/operation_failure.rb +65 -11
  31. data/lib/mongo/error/parser.rb +41 -8
  32. data/lib/mongo/grid/fs_bucket.rb +26 -6
  33. data/lib/mongo/grid/stream/read.rb +9 -2
  34. data/lib/mongo/grid/stream/write.rb +21 -5
  35. data/lib/mongo/index/view.rb +3 -3
  36. data/lib/mongo/lint.rb +10 -3
  37. data/lib/mongo/operation.rb +2 -0
  38. data/lib/mongo/operation/aggregate/result.rb +19 -6
  39. data/lib/mongo/operation/collections_info.rb +1 -1
  40. data/lib/mongo/operation/get_more/result.rb +9 -0
  41. data/lib/mongo/operation/list_collections/command.rb +1 -3
  42. data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
  43. data/lib/mongo/operation/parallel_scan/command.rb +4 -1
  44. data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
  45. data/lib/mongo/operation/result.rb +27 -4
  46. data/lib/mongo/operation/shared/executable.rb +19 -5
  47. data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
  48. data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
  49. data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
  50. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  51. data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
  52. data/lib/mongo/operation/shared/specifiable.rb +40 -0
  53. data/lib/mongo/operation/shared/unpinnable.rb +39 -0
  54. data/lib/mongo/operation/shared/write.rb +1 -1
  55. data/lib/mongo/protocol/update.rb +6 -2
  56. data/lib/mongo/retryable.rb +79 -39
  57. data/lib/mongo/server/connection.rb +10 -3
  58. data/lib/mongo/server/description.rb +25 -1
  59. data/lib/mongo/server/monitor/connection.rb +1 -1
  60. data/lib/mongo/server_selector.rb +10 -0
  61. data/lib/mongo/server_selector/selectable.rb +172 -32
  62. data/lib/mongo/session.rb +654 -581
  63. data/lib/mongo/session/session_pool.rb +1 -1
  64. data/lib/mongo/socket.rb +7 -28
  65. data/lib/mongo/socket/ssl.rb +26 -1
  66. data/lib/mongo/socket/tcp.rb +3 -0
  67. data/lib/mongo/socket/unix.rb +3 -0
  68. data/lib/mongo/uri.rb +112 -265
  69. data/lib/mongo/uri/srv_protocol.rb +4 -1
  70. data/lib/mongo/version.rb +1 -1
  71. data/lib/mongo/write_concern.rb +10 -29
  72. data/lib/mongo/write_concern/acknowledged.rb +12 -0
  73. data/lib/mongo/write_concern/base.rb +17 -13
  74. data/lib/mongo/write_concern/unacknowledged.rb +12 -0
  75. data/spec/atlas/atlas_connectivity_spec.rb +7 -37
  76. data/spec/atlas/operations_spec.rb +25 -0
  77. data/spec/integration/change_stream_examples_spec.rb +45 -31
  78. data/spec/integration/change_stream_spec.rb +305 -5
  79. data/spec/integration/client_spec.rb +44 -0
  80. data/spec/integration/command_monitoring_spec.rb +1 -0
  81. data/spec/integration/command_spec.rb +7 -1
  82. data/spec/integration/mmapv1_spec.rb +28 -0
  83. data/spec/integration/mongos_pinning_spec.rb +34 -0
  84. data/spec/integration/operation_failure_code_spec.rb +2 -2
  85. data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
  86. data/spec/integration/read_preference_spec.rb +485 -0
  87. data/spec/integration/retryable_writes_spec.rb +8 -19
  88. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  89. data/spec/integration/sdam_events_spec.rb +2 -2
  90. data/spec/integration/server_description_spec.rb +14 -17
  91. data/spec/integration/server_selector_spec.rb +7 -3
  92. data/spec/integration/server_spec.rb +48 -0
  93. data/spec/integration/ssl_uri_options_spec.rb +1 -1
  94. data/spec/integration/step_down_spec.rb +10 -4
  95. data/spec/integration/transactions_examples_spec.rb +11 -10
  96. data/spec/lite_spec_helper.rb +19 -16
  97. data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
  98. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
  99. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
  100. data/spec/mongo/bulk_write_spec.rb +12 -2
  101. data/spec/mongo/client_construction_spec.rb +160 -8
  102. data/spec/mongo/client_spec.rb +5 -4
  103. data/spec/mongo/cluster_spec.rb +6 -6
  104. data/spec/mongo/cluster_time_spec.rb +148 -0
  105. data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
  106. data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
  107. data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
  108. data/spec/mongo/collection/view/readable_spec.rb +4 -4
  109. data/spec/mongo/collection_spec.rb +331 -14
  110. data/spec/mongo/cursor_spec.rb +117 -5
  111. data/spec/mongo/database_spec.rb +240 -8
  112. data/spec/mongo/error/operation_failure_spec.rb +47 -1
  113. data/spec/mongo/error/parser_spec.rb +160 -23
  114. data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
  115. data/spec/mongo/operation/result_spec.rb +27 -0
  116. data/spec/mongo/operation/update/bulk_spec.rb +1 -0
  117. data/spec/mongo/retryable_spec.rb +2 -0
  118. data/spec/mongo/server/app_metadata_spec.rb +2 -2
  119. data/spec/mongo/server/connection_spec.rb +13 -17
  120. data/spec/mongo/server/monitor/connection_spec.rb +13 -10
  121. data/spec/mongo/server_selector_spec.rb +34 -2
  122. data/spec/mongo/session/session_pool_spec.rb +14 -3
  123. data/spec/mongo/session_spec.rb +3 -3
  124. data/spec/mongo/session_transaction_spec.rb +4 -3
  125. data/spec/mongo/socket/ssl_spec.rb +19 -5
  126. data/spec/mongo/socket_spec.rb +1 -62
  127. data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
  128. data/spec/mongo/uri_option_parsing_spec.rb +94 -8
  129. data/spec/mongo/uri_spec.rb +23 -10
  130. data/spec/mongo/write_concern_spec.rb +56 -3
  131. data/spec/spec_tests/change_streams_spec.rb +2 -1
  132. data/spec/spec_tests/cmap_spec.rb +1 -1
  133. data/spec/spec_tests/crud_spec.rb +12 -2
  134. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
  135. data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
  136. data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
  137. data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
  138. data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
  139. data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
  140. data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
  141. data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
  142. data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
  143. data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
  144. data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
  145. data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
  146. data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
  147. data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
  148. data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
  149. data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
  150. data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
  151. data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
  152. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
  153. data/spec/spec_tests/data/transactions/abort.yml +3 -0
  154. data/spec/spec_tests/data/transactions/bulk.yml +3 -8
  155. data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
  156. data/spec/spec_tests/data/transactions/commit.yml +3 -1
  157. data/spec/spec_tests/data/transactions/count.yml +3 -0
  158. data/spec/spec_tests/data/transactions/delete.yml +3 -0
  159. data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
  160. data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
  161. data/spec/spec_tests/data/transactions/errors.yml +3 -0
  162. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
  163. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
  164. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
  165. data/spec/spec_tests/data/transactions/insert.yml +3 -0
  166. data/spec/spec_tests/data/transactions/isolation.yml +3 -0
  167. data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
  168. data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
  169. data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
  170. data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
  171. data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
  172. data/spec/spec_tests/data/transactions/reads.yml +3 -0
  173. data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
  174. data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
  175. data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
  176. data/spec/spec_tests/data/transactions/run-command.yml +3 -0
  177. data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
  178. data/spec/spec_tests/data/transactions/update.yml +3 -8
  179. data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
  180. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
  181. data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
  182. data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
  183. data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
  184. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
  185. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
  186. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
  187. data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
  188. data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
  189. data/spec/spec_tests/retryable_reads_spec.rb +5 -2
  190. data/spec/spec_tests/retryable_writes_spec.rb +5 -2
  191. data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
  192. data/spec/spec_tests/sdam_spec.rb +2 -2
  193. data/spec/spec_tests/transactions_api_spec.rb +1 -67
  194. data/spec/spec_tests/transactions_spec.rb +2 -66
  195. data/spec/support/authorization.rb +4 -0
  196. data/spec/support/change_streams.rb +30 -10
  197. data/spec/support/change_streams/operation.rb +27 -0
  198. data/spec/support/client_registry.rb +44 -25
  199. data/spec/support/cluster_config.rb +25 -14
  200. data/spec/support/cluster_tools.rb +32 -10
  201. data/spec/support/command_monitoring.rb +1 -1
  202. data/spec/support/common_shortcuts.rb +30 -0
  203. data/spec/support/connection_string.rb +8 -3
  204. data/spec/support/constraints.rb +34 -0
  205. data/spec/support/crud.rb +31 -16
  206. data/spec/support/crud/context.rb +23 -0
  207. data/spec/support/crud/operation.rb +311 -14
  208. data/spec/support/crud/spec.rb +2 -1
  209. data/spec/support/crud/test.rb +24 -27
  210. data/spec/support/crud/test_base.rb +22 -0
  211. data/spec/support/crud/verifier.rb +15 -1
  212. data/spec/support/event_subscriber.rb +12 -0
  213. data/spec/support/sdam_formatter_integration.rb +12 -6
  214. data/spec/support/shared/server_selector.rb +10 -0
  215. data/spec/support/shared/session.rb +13 -12
  216. data/spec/support/spec_config.rb +32 -22
  217. data/spec/support/spec_setup.rb +2 -2
  218. data/spec/support/transactions.rb +87 -0
  219. data/spec/support/transactions/context.rb +33 -0
  220. data/spec/support/transactions/operation.rb +99 -349
  221. data/spec/support/transactions/spec.rb +1 -3
  222. data/spec/support/transactions/test.rb +110 -49
  223. data/spec/support/utils.rb +74 -1
  224. metadata +52 -10
  225. metadata.gz.sig +0 -0
  226. data/spec/support/crud/read.rb +0 -265
  227. data/spec/support/crud/write.rb +0 -284
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Client' do
4
+ context 'after client is disconnected' do
5
+ let(:client) { authorized_client.with(server_selection_timeout: 1) }
6
+
7
+ before do
8
+ client.close
9
+ end
10
+
11
+ it 'is still usable for operations' do
12
+ resp = client.database.command(ismaster: 1)
13
+ expect(resp).to be_a(Mongo::Operation::Result)
14
+ end
15
+
16
+ it 'is still usable for operations that can use sessions' do
17
+ client['collection'].insert_one(test: 1)
18
+ end
19
+
20
+ context 'after all servers are marked unknown' do
21
+ before do
22
+ client.cluster.servers.each do |server|
23
+ server.unknown!
24
+ end
25
+ end
26
+
27
+ context 'operation that never uses sessions' do
28
+ it 'fails server selection' do
29
+ expect do
30
+ client.database.command(ismaster: 1)
31
+ end.to raise_error(Mongo::Error::NoServerAvailable)
32
+ end
33
+ end
34
+
35
+ context 'operation that can use sessions' do
36
+ it 'fails server selection' do
37
+ expect do
38
+ client['collection'].insert_one(test: 1)
39
+ end.to raise_error(Mongo::Error::NoServerAvailable)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -79,6 +79,7 @@ describe 'Command monitoring' do
79
79
  end
80
80
 
81
81
  context 'when write concern is specified outside of command document' do
82
+ require_wired_tiger
82
83
  require_topology :replica_set
83
84
  min_server_fcv '4.0'
84
85
 
@@ -61,7 +61,13 @@ describe 'Command' do
61
61
  context 'with session' do
62
62
  min_server_fcv '3.6'
63
63
 
64
- let(:session) { authorized_client.start_session }
64
+ let(:session) do
65
+ authorized_client.start_session.tap do |session|
66
+ # We are bypassing the normal transaction lifecycle, which would
67
+ # set txn_options
68
+ allow(session).to receive(:txn_options).and_return({})
69
+ end
70
+ end
65
71
 
66
72
  let(:expected_payload) do
67
73
  {
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ # This test is a marker used to verify that the test suite runs on
4
+ # mmapv1 storage engine.
5
+ describe 'mmapv1' do
6
+ require_mmapv1
7
+
8
+ context 'standalone' do
9
+ require_topology :single
10
+
11
+ it 'is exercised' do
12
+ end
13
+ end
14
+
15
+ context 'replica set' do
16
+ require_topology :replica_set
17
+
18
+ it 'is exercised' do
19
+ end
20
+ end
21
+
22
+ context 'sharded' do
23
+ require_topology :sharded
24
+
25
+ it 'is exercised' do
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Mongos pinning' do
4
+ require_topology :sharded
5
+ min_server_fcv '4.2'
6
+
7
+ let(:client) { authorized_client }
8
+ let(:collection) { client['mongos_pinning_spec'] }
9
+
10
+ before do
11
+ collection.create
12
+ end
13
+
14
+ context 'successful operations' do
15
+ it 'pins and unpins' do
16
+ session = client.start_session
17
+ expect(session.pinned_server).to be nil
18
+
19
+ session.start_transaction
20
+ expect(session.pinned_server).to be nil
21
+
22
+ primary = client.cluster.next_primary
23
+
24
+ collection.insert_one({a: 1}, session: session)
25
+ expect(session.pinned_server).not_to be nil
26
+
27
+ session.commit_transaction
28
+ expect(session.pinned_server).not_to be nil
29
+
30
+ collection.insert_one({a: 1}, session: session)
31
+ expect(session.pinned_server).to be nil
32
+ end
33
+ end
34
+ end
@@ -16,8 +16,8 @@ describe 'OperationFailure code' do
16
16
  fail('Should have raised')
17
17
  rescue Mongo::Error::OperationFailure => e
18
18
  expect(e.code).to eq(11000)
19
- # 4.0 and 4.1 sharded clusters set code name.
20
- # 4.0 and 4.1 replica sets and standalones do not,
19
+ # 4.0 and 4.2 sharded clusters set code name.
20
+ # 4.0 and 4.2 replica sets and standalones do not,
21
21
  # and neither do older versions.
22
22
  expect([nil, 'DuplicateKey']).to include(e.code_name)
23
23
  end
@@ -1,11 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'read concern' do
4
+ min_server_version '3.2'
5
+
4
6
  let(:subscriber) do
5
7
  EventSubscriber.new
6
8
  end
7
9
 
8
10
  let(:specified_read_concern) do
11
+ { :level => :local }
12
+ end
13
+
14
+ let(:expected_read_concern) do
9
15
  { 'level' => 'local' }
10
16
  end
11
17
 
@@ -17,7 +23,7 @@ describe 'read concern' do
17
23
 
18
24
  shared_examples_for 'a read concern is specified' do
19
25
  it 'sends a read concern to the server' do
20
- expect(sent_read_concern).to eq(specified_read_concern)
26
+ expect(sent_read_concern).to eq(expected_read_concern)
21
27
  end
22
28
  end
23
29
 
@@ -0,0 +1,485 @@
1
+ require 'spec_helper'
2
+
3
+ # The only allowed read preference in transaction is primary.
4
+ # Because of this, the tests assert that the final read preference is primary.
5
+ # It would be preferable to assert that some other read preference is selected,
6
+ # but this would only work for non-transactional tests and would require
7
+ # duplicating the examples.
8
+
9
+ describe 'Read preference' do
10
+ clean_slate_on_evergreen
11
+
12
+ let(:client) do
13
+ authorized_client.with(client_options)
14
+ end
15
+
16
+ let(:subscriber) { EventSubscriber.new }
17
+
18
+ before do
19
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
20
+ end
21
+
22
+ let(:client_options) do
23
+ {}
24
+ end
25
+
26
+ let(:session_options) do
27
+ {}
28
+ end
29
+
30
+ let(:tx_options) do
31
+ {}
32
+ end
33
+
34
+ let(:collection) { client['tx_read_pref_test'] }
35
+
36
+ before do
37
+ collection.drop
38
+ collection.create(write_concern: {w: :majority})
39
+ end
40
+
41
+ let(:find_options) do
42
+ {}
43
+ end
44
+
45
+ shared_examples_for 'sends expected read preference when reading' do
46
+ it 'sends expected read preference when reading' do
47
+ read_operation
48
+
49
+ event = subscriber.single_command_started_event('find')
50
+ actual_preference = event.command['$readPreference']
51
+ expect(actual_preference).to eq(expected_read_preference)
52
+ end
53
+ end
54
+
55
+ shared_examples_for 'does not send read preference when reading' do
56
+ it 'does not send read preference when reading' do
57
+ read_operation
58
+
59
+ event = subscriber.single_command_started_event('find')
60
+ actual_preference = event.command['$readPreference']
61
+ expect(actual_preference).to be nil
62
+ end
63
+ end
64
+
65
+ shared_examples_for 'non-transactional read preference examples' do
66
+ it 'does not send read preference when writing' do
67
+ write_operation
68
+
69
+ event = subscriber.single_command_started_event('insert')
70
+ actual_preference = event.command['$readPreference']
71
+ expect(actual_preference).to be nil
72
+ end
73
+
74
+ context 'standalone' do
75
+ require_topology :single
76
+
77
+ it_behaves_like 'does not send read preference when reading'
78
+ end
79
+
80
+ context 'RS, sharded cluster' do
81
+ # Supposedly read preference should only be sent in a sharded cluster
82
+ # topology. However, transactions spec tests contain read preference
83
+ # assertions also when they are run in RS topologies.
84
+ require_topology :sharded, :replica_set
85
+
86
+ context 'pre-OP_MSG server' do
87
+ max_server_version '3.4'
88
+
89
+ it_behaves_like 'does not send read preference when reading'
90
+ end
91
+
92
+ context 'server supporting OP_MSG' do
93
+ min_server_fcv '3.6'
94
+
95
+ it_behaves_like 'sends expected read preference when reading'
96
+ end
97
+ end
98
+ end
99
+
100
+ shared_examples_for 'sends expected read preference' do
101
+ it_behaves_like 'non-transactional read preference examples'
102
+ end
103
+
104
+ shared_context 'non-transactional read preference specifications' do
105
+
106
+ context 'when read preference is not explicitly given' do
107
+ let(:client_options) do
108
+ {}
109
+ end
110
+
111
+ let(:expected_read_preference) do
112
+ nil
113
+ end
114
+
115
+ it_behaves_like 'sends expected read preference'
116
+ end
117
+
118
+ context 'when read preference is given in client options' do
119
+ let(:client_options) do
120
+ {read: { mode: :primary }}
121
+ end
122
+
123
+ let(:expected_read_preference) do
124
+ {'mode' => 'primary'}
125
+ end
126
+
127
+ it_behaves_like 'sends expected read preference'
128
+ end
129
+
130
+ context 'when read preference is given in operation options' do
131
+ let(:expected_read_preference) do
132
+ {'mode' => 'primary'}
133
+ end
134
+
135
+ let(:find_options) do
136
+ {read: {mode: :primary}}
137
+ end
138
+
139
+ it_behaves_like 'sends expected read preference'
140
+ end
141
+
142
+ context 'when read preference is given in client and operation options' do
143
+ let(:client_options) do
144
+ {read: { mode: :secondary }}
145
+ end
146
+
147
+ # Operation should override the client.
148
+ let(:expected_read_preference) do
149
+ {'mode' => 'primary'}
150
+ end
151
+
152
+ let(:find_options) do
153
+ {read: {mode: :primary}}
154
+ end
155
+
156
+ it_behaves_like 'sends expected read preference'
157
+ end
158
+
159
+ context 'when read preference is given in collection and operation options' do
160
+ let(:collection) do
161
+ client['tx_read_pref_test', {read: {mode: :secondary}}]
162
+ end
163
+
164
+ # Operation should override the collection.
165
+ let(:expected_read_preference) do
166
+ {'mode' => 'primary'}
167
+ end
168
+
169
+ let(:find_options) do
170
+ {read: {mode: :primary}}
171
+ end
172
+
173
+ it_behaves_like 'sends expected read preference'
174
+ end
175
+ end
176
+
177
+ context 'not in transaction' do
178
+
179
+ let(:write_operation) do
180
+ collection.insert_one(hello: 'world')
181
+ end
182
+
183
+ let(:read_operation) do
184
+ collection.with(write: {w: :majority}).insert_one(hello: 'world')
185
+ res = collection.find({}, find_options || {}).to_a.count
186
+ expect(res).to eq(1)
187
+ end
188
+
189
+ include_context 'non-transactional read preference specifications'
190
+
191
+ context 'when read preference is given in collection options' do
192
+ let(:client_options) do
193
+ {}
194
+ end
195
+
196
+ let(:collection) do
197
+ client['tx_read_pref_test', {read: {mode: :primary}}]
198
+ end
199
+
200
+ let(:expected_read_preference) do
201
+ {'mode' => 'primary'}
202
+ end
203
+
204
+ it_behaves_like 'sends expected read preference'
205
+ end
206
+
207
+ context 'when read preference is given in collection options via #with' do
208
+ let(:collection) do
209
+ client['tx_read_pref_test'].with(read: {mode: :primary})
210
+ end
211
+
212
+ let(:expected_read_preference) do
213
+ {'mode' => 'primary'}
214
+ end
215
+
216
+ it_behaves_like 'sends expected read preference'
217
+ end
218
+
219
+ context 'when read preference is given in client and collection options' do
220
+ let(:client_options) do
221
+ {read: { mode: :secondary }}
222
+ end
223
+
224
+ let(:collection) do
225
+ client['tx_read_pref_test', {read: {mode: :primary}}]
226
+ end
227
+
228
+ # Collection should override the client.
229
+ let(:expected_read_preference) do
230
+ {'mode' => 'primary'}
231
+ end
232
+
233
+ it_behaves_like 'sends expected read preference'
234
+ end
235
+ end
236
+
237
+ context 'in transaction' do
238
+ # 4.0/RS is a valid topology to test against, but our tooling doesn't
239
+ # support multiple constraint specifications like runOn does.
240
+ # There is no loss of generality to constrain these tests to 4.2+.
241
+ min_server_fcv '4.2'
242
+ require_topology :sharded, :replica_set
243
+
244
+ let(:write_operation) do
245
+ expect do
246
+ session = client.start_session(session_options)
247
+ session.with_transaction(tx_options) do
248
+ collection.insert_one({hello: 'world'}, session: session)
249
+ end
250
+ end.not_to raise_error
251
+ end
252
+
253
+ let(:read_operation) do
254
+ expect do
255
+ session = client.start_session(session_options)
256
+ session.with_transaction(tx_options) do
257
+ collection.insert_one({hello: 'world'}, session: session)
258
+ res = collection.find({}, {session: session}.merge(find_options || {})).to_a.count
259
+ expect(res).to eq(1)
260
+ end
261
+ end.not_to raise_error
262
+ end
263
+
264
+ shared_examples_for 'sends expected read preference' do
265
+ it_behaves_like 'non-transactional read preference examples'
266
+
267
+ it 'sends expected read preference when starting transaction' do
268
+ collection.insert_one(hello: 'world')
269
+
270
+ session = client.start_session(session_options)
271
+ session.with_transaction(tx_options) do
272
+ res = collection.find({}, {session: session}.merge(find_options || {})).to_a.count
273
+ expect(res).to eq(1)
274
+ end
275
+
276
+ event = subscriber.single_command_started_event('find')
277
+ actual_preference = event.command['$readPreference']
278
+ expect(actual_preference).to eq(expected_read_preference)
279
+ end
280
+ end
281
+
282
+ include_context 'non-transactional read preference specifications'
283
+
284
+ context 'when read preference is given in collection options' do
285
+ let(:client_options) do
286
+ {}
287
+ end
288
+
289
+ let(:collection) do
290
+ client['tx_read_pref_test', {read: {mode: :primary}}]
291
+ end
292
+
293
+ # collection read preference is ignored
294
+ let(:expected_read_preference) do
295
+ nil
296
+ end
297
+
298
+ it_behaves_like 'sends expected read preference'
299
+ end
300
+
301
+ context 'when read preference is given in collection options via #with' do
302
+ let(:collection) do
303
+ client['tx_read_pref_test'].with(read: {mode: :primary})
304
+ end
305
+
306
+ # collection read preference is ignored
307
+ let(:expected_read_preference) do
308
+ nil
309
+ end
310
+
311
+ it_behaves_like 'sends expected read preference'
312
+ end
313
+
314
+ context 'when read preference is given in client and collection options' do
315
+ let(:client_options) do
316
+ {read: { mode: :primary }}
317
+ end
318
+
319
+ let(:collection) do
320
+ client['tx_read_pref_test', {read: {mode: :secondary}}]
321
+ end
322
+
323
+ # collection read preference is ignored, client read preference is used
324
+ let(:expected_read_preference) do
325
+ {'mode' => 'primary'}
326
+ end
327
+
328
+ it_behaves_like 'sends expected read preference'
329
+ end
330
+
331
+ context 'when read preference is given in default transaction options' do
332
+ let(:session_options) do
333
+ {default_transaction_options: {read: { mode: :primary }}}
334
+ end
335
+
336
+ let(:expected_read_preference) do
337
+ {'mode' => 'primary'}
338
+ end
339
+
340
+ it_behaves_like 'sends expected read preference'
341
+ end
342
+
343
+ context 'when read preference is given in client and default transaction options' do
344
+ let(:client_options) do
345
+ {read: { mode: :secondary }}
346
+ end
347
+
348
+ let(:session_options) do
349
+ {default_transaction_options: {read: { mode: :primary }}}
350
+ end
351
+
352
+ let(:expected_read_preference) do
353
+ {'mode' => 'primary'}
354
+ end
355
+
356
+ it_behaves_like 'sends expected read preference'
357
+ end
358
+
359
+ context 'when read preference is given in collection and default transaction options' do
360
+ let(:collection) do
361
+ client['tx_read_pref_test', {read: {mode: :secondary}}]
362
+ end
363
+
364
+ let(:session_options) do
365
+ {default_transaction_options: {read: { mode: :primary }}}
366
+ end
367
+
368
+ let(:expected_read_preference) do
369
+ {'mode' => 'primary'}
370
+ end
371
+
372
+ it_behaves_like 'sends expected read preference'
373
+ end
374
+
375
+ context 'when read preference is given in default transaction and transaction options' do
376
+ let(:session_options) do
377
+ {default_transaction_options: {read: { mode: :secondary }}}
378
+ end
379
+
380
+ let(:tx_options) do
381
+ {read: { mode: :primary }}
382
+ end
383
+
384
+ let(:expected_read_preference) do
385
+ {'mode' => 'primary'}
386
+ end
387
+
388
+ it_behaves_like 'sends expected read preference'
389
+ end
390
+
391
+ context 'when read preference is given in default transaction and operation options' do
392
+ let(:session_options) do
393
+ {default_transaction_options: {read: { mode: :primary }}}
394
+ end
395
+
396
+ let(:find_options) do
397
+ {read: {mode: :secondary}}
398
+ end
399
+
400
+ let(:expected_read_preference) do
401
+ {'mode' => 'primary'}
402
+ end
403
+
404
+ it 'sends operation read preference and fails' do
405
+ expect do
406
+ session = client.start_session(session_options)
407
+ session.with_transaction(tx_options) do
408
+ collection.insert_one({hello: 'world'}, session: session)
409
+ res = collection.find({}, {session: session}.merge(find_options || {})).to_a.count
410
+ expect(res).to eq(1)
411
+ end
412
+ end.to raise_error(Mongo::Error::InvalidTransactionOperation, "read preference in a transaction must be primary (requested: secondary)")
413
+ end
414
+ end
415
+
416
+ context 'when read preference is given in transaction options' do
417
+ let(:tx_options) do
418
+ {read: { mode: :primary }}
419
+ end
420
+
421
+ let(:expected_read_preference) do
422
+ {'mode' => 'primary'}
423
+ end
424
+
425
+ it_behaves_like 'sends expected read preference'
426
+ end
427
+
428
+ context 'when read preference is given in client and transaction options' do
429
+ let(:client_options) do
430
+ {read: { mode: :secondary }}
431
+ end
432
+
433
+ let(:tx_options) do
434
+ {read: { mode: :primary }}
435
+ end
436
+
437
+ let(:expected_read_preference) do
438
+ {'mode' => 'primary'}
439
+ end
440
+
441
+ it_behaves_like 'sends expected read preference'
442
+ end
443
+
444
+ context 'when read preference is given in collection and transaction options' do
445
+ let(:collection) do
446
+ client['tx_read_pref_test', {read: {mode: :secondary}}]
447
+ end
448
+
449
+ let(:tx_options) do
450
+ {read: { mode: :primary }}
451
+ end
452
+
453
+ let(:expected_read_preference) do
454
+ {'mode' => 'primary'}
455
+ end
456
+
457
+ it_behaves_like 'sends expected read preference'
458
+ end
459
+
460
+ context 'when read preference is given in transaction and operation options' do
461
+ let(:tx_options) do
462
+ {read: { mode: :primary }}
463
+ end
464
+
465
+ let(:find_options) do
466
+ {read: {mode: :secondary}}
467
+ end
468
+
469
+ let(:expected_read_preference) do
470
+ {'mode' => 'primary'}
471
+ end
472
+
473
+ it 'sends operation read preference and fails' do
474
+ expect do
475
+ session = client.start_session(session_options)
476
+ session.with_transaction(tx_options) do
477
+ collection.insert_one({hello: 'world'}, session: session)
478
+ res = collection.find({}, {session: session}.merge(find_options || {})).to_a.count
479
+ expect(res).to eq(1)
480
+ end
481
+ end.to raise_error(Mongo::Error::InvalidTransactionOperation, "read preference in a transaction must be primary (requested: secondary)")
482
+ end
483
+ end
484
+ end
485
+ end