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
@@ -103,7 +103,10 @@ module Mongo
103
103
 
104
104
  records = get_records(hostname)
105
105
  @txt_options = get_txt_opts(hostname) || {}
106
- @servers = parse_servers!(records.join(','))
106
+ records.each do |record|
107
+ validate_host!(record)
108
+ end
109
+ @servers = records
107
110
  end
108
111
 
109
112
  # Validates the hostname used in an SRV URI.
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.9.2'.freeze
20
+ VERSION = '2.10.0.rc0'.freeze
21
21
  end
@@ -27,26 +27,31 @@ module Mongo
27
27
  # The number of servers write concern.
28
28
  #
29
29
  # @since 2.0.0
30
+ # @deprecated
30
31
  W = :w.freeze
31
32
 
32
33
  # The journal write concern.
33
34
  #
34
35
  # @since 2.0.0
36
+ # @deprecated
35
37
  J = :j.freeze
36
38
 
37
39
  # The file sync write concern.
38
40
  #
39
41
  # @since 2.0.0
42
+ # @deprecated
40
43
  FSYNC = :fsync.freeze
41
44
 
42
45
  # The wtimeout write concern.
43
46
  #
44
47
  # @since 2.0.0
48
+ # @deprecated
45
49
  WTIMEOUT = :wtimeout.freeze
46
50
 
47
51
  # The GLE command name.
48
52
  #
49
53
  # @since 2.0.0
54
+ # @deprecated
50
55
  GET_LAST_ERROR = :getlasterror.freeze
51
56
 
52
57
  # The default write concern is to acknowledge on a single server.
@@ -56,6 +61,8 @@ module Mongo
56
61
 
57
62
  # Create a write concern object for the provided options.
58
63
  #
64
+ # If options are nil, returns nil.
65
+ #
59
66
  # @example Get a write concern.
60
67
  # Mongo::WriteConcern.get(:w => 1)
61
68
  #
@@ -70,46 +77,20 @@ module Mongo
70
77
  # @option options :wtimeout [ Integer ] The number of milliseconds to
71
78
  # wait for acknowledgement before raising an error.
72
79
  #
73
- # @return [ Unacknowledged, Acknowledged ] The appropriate concern.
80
+ # @return [ nil | Unacknowledged | Acknowledged ] The appropriate concern.
74
81
  #
75
82
  # @raise [ Error::InvalidWriteConcern ] If the options are invalid.
76
83
  #
77
84
  # @since 2.0.0
78
85
  def get(options)
79
- return options if options.is_a?(Unacknowledged) || options.is_a?(Acknowledged)
86
+ return options if options.is_a?(Base)
80
87
  if options
81
- validate!(options)
82
- if unacknowledged?(options)
88
+ if (options[:w] || options['w']) == 0
83
89
  Unacknowledged.new(options)
84
90
  else
85
91
  Acknowledged.new(options)
86
92
  end
87
93
  end
88
94
  end
89
-
90
- private
91
-
92
- def validate!(options)
93
- if options[W]
94
- if options[W] == 0 && (options[J] || options[FSYNC])
95
- raise Mongo::Error::InvalidWriteConcern.new
96
- elsif options[W].is_a?(Integer) && options[W] < 0
97
- raise Mongo::Error::InvalidWriteConcern.new
98
- end
99
- end
100
- end
101
-
102
- # Determine if the options are for an unacknowledged write concern.
103
- #
104
- # @api private
105
- #
106
- # @param [ Hash ] options The options to check.
107
- #
108
- # @return [ true, false ] If the options are unacknowledged.
109
- #
110
- # @since 2.0.0
111
- def unacknowledged?(options)
112
- options[W] == 0
113
- end
114
95
  end
115
96
  end
@@ -34,6 +34,18 @@ module Mongo
34
34
  )
35
35
  end
36
36
 
37
+ # Is this write concern acknowledged.
38
+ #
39
+ # @example Whether this write concern object is acknowledged.
40
+ # write_concern.acknowledged?
41
+ #
42
+ # @return [ true, false ] Whether this write concern is acknowledged.
43
+ #
44
+ # @since 2.5.0
45
+ def acknowledged?
46
+ true
47
+ end
48
+
37
49
  # Get a human-readable string representation of an acknowledged write concern.
38
50
  #
39
51
  # @example Inspect the write concern.
@@ -43,20 +43,24 @@ module Mongo
43
43
  #
44
44
  # @since 2.0.0
45
45
  def initialize(options)
46
- opts = Options::Mapper.transform_keys_to_symbols(options)
47
- @options = Options::Mapper.transform_values_to_strings(opts).freeze
48
- end
46
+ options = Options::Mapper.transform_keys_to_symbols(options)
47
+ options = Options::Mapper.transform_values_to_strings(options).freeze
49
48
 
50
- # Is this write concern acknowledged.
51
- #
52
- # @example Whether this write concern object is acknowledged.
53
- # write_concern.acknowledged?
54
- #
55
- # @return [ true, false ] Whether this write concern is acknowledged.
56
- #
57
- # @since 2.5.0
58
- def acknowledged?
59
- !!get_last_error
49
+ if options[:w]
50
+ if options[:w] == 0 && options[:j]
51
+ raise Error::InvalidWriteConcern, "Invalid write concern options: :j cannot be true when :w is 0: #{options.inspect}"
52
+ elsif options[:w] == 0 && options[:fsync]
53
+ raise Error::InvalidWriteConcern, "Invalid write concern options: :fsync cannot be true when :w is 0: #{options.inspect}"
54
+ elsif options[:w].is_a?(Integer) && options[:w] < 0
55
+ raise Error::InvalidWriteConcern, "Invalid write concern options: :w cannot be negative (#{options[:w]}): #{options.inspect}"
56
+ end
57
+ end
58
+
59
+ if options[:journal]
60
+ raise Error::InvalidWriteConcern, "Invalid write concern options: use :j for journal: #{options.inspect}"
61
+ end
62
+
63
+ @options = options
60
64
  end
61
65
  end
62
66
  end
@@ -37,6 +37,18 @@ module Mongo
37
37
  NOOP
38
38
  end
39
39
 
40
+ # Is this write concern acknowledged.
41
+ #
42
+ # @example Whether this write concern object is acknowledged.
43
+ # write_concern.acknowledged?
44
+ #
45
+ # @return [ true, false ] Whether this write concern is acknowledged.
46
+ #
47
+ # @since 2.5.0
48
+ def acknowledged?
49
+ false
50
+ end
51
+
40
52
  # Get a human-readable string representation of an unacknowledged write concern.
41
53
  #
42
54
  # @example Inspect the write concern.
@@ -1,16 +1,16 @@
1
1
  require 'lite_spec_helper'
2
2
 
3
3
  describe 'Atlas connectivity' do
4
- shared_examples 'connects to Atlas' do
5
- let(:uri) { ENV[var] }
6
- let(:client) { Mongo::Client.new(uri) }
4
+ let(:uri) { ENV['ATLAS_URI'] }
5
+ let(:client) { Mongo::Client.new(uri) }
7
6
 
8
- before do
9
- if uri.nil?
10
- skip "#{var} not set in environment"
11
- end
7
+ before do
8
+ if uri.nil?
9
+ skip "ATLAS_URI not set in environment"
12
10
  end
11
+ end
13
12
 
13
+ describe 'connection to Atlas' do
14
14
  it 'runs ismaster successfully' do
15
15
  result = client.database.command(:ismaster => 1)
16
16
  expect(result.documents.first['ismaster']).to be true
@@ -21,34 +21,4 @@ describe 'Atlas connectivity' do
21
21
  expect(result).to be_a(Array)
22
22
  end
23
23
  end
24
-
25
- context 'Atlas replica set' do
26
- let(:var) { 'ATLAS_REPLICA_SET_URI' }
27
-
28
- it_behaves_like 'connects to Atlas'
29
- end
30
-
31
- context 'Atlas sharded cluster' do
32
- let(:var) { 'ATLAS_SHARDED_URI' }
33
-
34
- it_behaves_like 'connects to Atlas'
35
- end
36
-
37
- context 'Atlas free tier replica set' do
38
- let(:var) { 'ATLAS_FREE_TIER_URI' }
39
-
40
- it_behaves_like 'connects to Atlas'
41
- end
42
-
43
- context 'Atlas TLS 1.1 only replica set' do
44
- let(:var) { 'ATLAS_TLS11_URI' }
45
-
46
- it_behaves_like 'connects to Atlas'
47
- end
48
-
49
- context 'Atlas TLS 1.2 only replica set' do
50
- let(:var) { 'ATLAS_TLS12_URI' }
51
-
52
- it_behaves_like 'connects to Atlas'
53
- end
54
24
  end
@@ -0,0 +1,25 @@
1
+ require 'lite_spec_helper'
2
+
3
+ describe 'Operations' do
4
+ let(:uri) { ENV['ATLAS_URI'] }
5
+ let(:client) { Mongo::Client.new(uri) }
6
+
7
+ before do
8
+ if uri.nil?
9
+ skip "ATLAS_URI not set in environment"
10
+ end
11
+ end
12
+
13
+ describe 'list_collections' do
14
+ # Atlas free tier proxy enforces restrictions on list_collections
15
+ # arguments. This tests verifies that list_collections works on Atlas
16
+
17
+ it 'works' do
18
+ # We are not allowed to mutate the database, therefore the list of
19
+ # collections would generally be empty.
20
+ expect do
21
+ client.database.list_collections
22
+ end.not_to raise_error
23
+ end
24
+ end
25
+ end
@@ -3,6 +3,12 @@ require 'spec_helper'
3
3
  describe 'change streams examples in Ruby' do
4
4
  min_server_fcv '3.6'
5
5
  require_topology :replica_set
6
+ require_wired_tiger
7
+
8
+ # On JRuby, change streams should be accessed using try_next on the
9
+ # change stream objects rather than using the Enumerable interface.
10
+ # https://jira.mongodb.org/browse/RUBY-1877
11
+ fails_on_jruby
6
12
 
7
13
  let!(:inventory) do
8
14
  client[:inventory]
@@ -17,7 +23,7 @@ describe 'change streams examples in Ruby' do
17
23
  end
18
24
 
19
25
  after do
20
- client.close
26
+ client.close(true)
21
27
  end
22
28
 
23
29
  context 'example 1 - basic watching'do
@@ -103,10 +109,30 @@ describe 'change streams examples in Ruby' do
103
109
 
104
110
  it 'returns the correct change when resuming' do
105
111
 
106
- stream = inventory.watch
107
- cursor = stream.to_enum
108
- inventory.insert_one(x: 1)
109
- next_change = cursor.next
112
+ insert_thread = Thread.new do
113
+ sleep 2
114
+ inventory.insert_one(x: 1)
115
+ inventory.insert_one(x: 2)
116
+ end
117
+
118
+ next_change = nil
119
+ resume_stream_thread = Thread.new do
120
+
121
+ # Start Changestream Example 3
122
+
123
+ change_stream = inventory.watch
124
+ cursor = change_stream.to_enum
125
+ next_change = cursor.next
126
+ resume_token = change_stream.resume_token
127
+
128
+ new_cursor = inventory.watch([], resume_after: resume_token).to_enum
129
+ resumed_change = new_cursor.next
130
+
131
+ # End Changestream Example 3
132
+ end
133
+
134
+ insert_thread.value
135
+ resumed_change = resume_stream_thread.value
110
136
 
111
137
  expect(next_change['_id']).not_to be_nil
112
138
  expect(next_change['_id']['_data']).not_to be_nil
@@ -120,32 +146,20 @@ describe 'change streams examples in Ruby' do
120
146
  expect(next_change['documentKey']).not_to be_nil
121
147
  expect(next_change['documentKey']['_id']).to eq(next_change['fullDocument']['_id'])
122
148
 
123
- inventory.insert_one(x: 2)
124
- next_next_change = cursor.next
125
- stream.close
126
-
127
- expect(next_next_change['_id']).not_to be_nil
128
- expect(next_next_change['_id']['_data']).not_to be_nil
129
- expect(next_next_change['operationType']).to eq('insert')
130
- expect(next_next_change['fullDocument']).not_to be_nil
131
- expect(next_next_change['fullDocument']['_id']).not_to be_nil
132
- expect(next_next_change['fullDocument']['x']).to eq(2)
133
- expect(next_next_change['ns']).not_to be_nil
134
- expect(next_next_change['ns']['db']).to eq(SpecConfig.instance.test_db)
135
- expect(next_next_change['ns']['coll']).to eq(inventory.name)
136
- expect(next_next_change['documentKey']).not_to be_nil
137
- expect(next_next_change['documentKey']['_id']).to eq(next_next_change['fullDocument']['_id'])
138
-
139
- # Start Changestream Example 3
140
-
141
- resume_token = next_change['_id']
142
- cursor = inventory.watch([], resume_after: resume_token).to_enum
143
- resumed_change = cursor.next
144
-
145
- # End Changestream Example 3
146
-
147
- expect(resumed_change.length).to eq(next_next_change.length)
148
- resumed_change.each { |key| expect(resumed_change[key]).to eq(next_next_change[key]) }
149
+ expect(resumed_change['_id']).not_to be_nil
150
+ expect(resumed_change['_id']['_data']).not_to be_nil
151
+ expect(resumed_change['operationType']).to eq('insert')
152
+ expect(resumed_change['fullDocument']).not_to be_nil
153
+ expect(resumed_change['fullDocument']['_id']).not_to be_nil
154
+ expect(resumed_change['fullDocument']['x']).to eq(2)
155
+ expect(resumed_change['ns']).not_to be_nil
156
+ expect(resumed_change['ns']['db']).to eq(SpecConfig.instance.test_db)
157
+ expect(resumed_change['ns']['coll']).to eq(inventory.name)
158
+ expect(resumed_change['documentKey']).not_to be_nil
159
+ expect(resumed_change['documentKey']['_id']).to eq(resumed_change['fullDocument']['_id'])
160
+
161
+ expect(resumed_change.length).to eq(resumed_change.length)
162
+ resumed_change.each { |key| expect(resumed_change[key]).to eq(resumed_change[key]) }
149
163
  end
150
164
  end
151
165
 
@@ -5,6 +5,7 @@ describe 'Change stream integration', retry: 4 do
5
5
  max_example_run_time 7
6
6
  min_server_fcv '3.6'
7
7
  require_topology :replica_set
8
+ require_wired_tiger
8
9
 
9
10
  let(:fail_point_base_command) do
10
11
  { 'configureFailPoint' => "failCommand" }
@@ -45,6 +46,25 @@ describe 'Change stream integration', retry: 4 do
45
46
  end
46
47
  end
47
48
 
49
+ shared_examples_for 'raises an exception' do
50
+ it 'raises an exception and does not attempt to resume' do
51
+ change_stream
52
+
53
+ subscriber = EventSubscriber.new
54
+ authorized_client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
55
+
56
+ expect do
57
+ change_stream.to_enum.next
58
+ end.to raise_error(Mongo::Error::OperationFailure)
59
+
60
+ aggregate_commands = subscriber.started_events.select { |e| e.command_name == 'aggregate' }
61
+ expect(aggregate_commands.length).to be 0
62
+
63
+ get_more_commands = subscriber.started_events.select { |e| e.command_name == 'getMore' }
64
+ expect(get_more_commands.length).to be 1
65
+ end
66
+ end
67
+
48
68
  context 'no errors' do
49
69
  it 'next returns changes' do
50
70
  change_stream
@@ -86,10 +106,36 @@ describe 'Change stream integration', retry: 4 do
86
106
  before do
87
107
  authorized_collection.client.use(:admin).command(fail_point_base_command.merge(
88
108
  :mode => {:times => 1},
89
- :data => {:failCommands => ['getMore'], errorCode: 100}))
109
+ :data => {:failCommands => ['getMore'], errorCode: errorCode}))
90
110
  end
91
111
 
92
- it_behaves_like 'returns a change document'
112
+ context 'when the error is resumable' do
113
+ let(:errorCode) do
114
+ 100
115
+ end
116
+ it_behaves_like 'returns a change document'
117
+ end
118
+
119
+ context 'when the error is Interrupted' do
120
+ let(:errorCode) do
121
+ 11601
122
+ end
123
+ it_behaves_like 'raises an exception'
124
+ end
125
+
126
+ context 'when the error is CappedPositionLost' do
127
+ let(:errorCode) do
128
+ 136
129
+ end
130
+ it_behaves_like 'raises an exception'
131
+ end
132
+
133
+ context 'when the error is CursorKilled' do
134
+ let(:errorCode) do
135
+ 237
136
+ end
137
+ it_behaves_like 'raises an exception'
138
+ end
93
139
  end
94
140
 
95
141
  context 'error on a getMore other than first' do
@@ -105,10 +151,36 @@ describe 'Change stream integration', retry: 4 do
105
151
 
106
152
  authorized_collection.client.use(:admin).command(fail_point_base_command.merge(
107
153
  :mode => {:times => 1},
108
- :data => {:failCommands => ['getMore'], errorCode: 100}))
154
+ :data => {:failCommands => ['getMore'], errorCode: errorCode}))
109
155
  end
110
156
 
111
- it_behaves_like 'returns a change document'
157
+ context 'when the error is resumable' do
158
+ let(:errorCode) do
159
+ 100
160
+ end
161
+ it_behaves_like 'returns a change document'
162
+ end
163
+
164
+ context 'when the error is Interrupted' do
165
+ let(:errorCode) do
166
+ 11601
167
+ end
168
+ it_behaves_like 'raises an exception'
169
+ end
170
+
171
+ context 'when the error is CappedPositionLost' do
172
+ let(:errorCode) do
173
+ 136
174
+ end
175
+ it_behaves_like 'raises an exception'
176
+ end
177
+
178
+ context 'when the error is CursorKilled' do
179
+ let(:errorCode) do
180
+ 237
181
+ end
182
+ it_behaves_like 'raises an exception'
183
+ end
112
184
  end
113
185
  end
114
186
 
@@ -369,7 +441,7 @@ describe 'Change stream integration', retry: 4 do
369
441
 
370
442
  describe ':start_after option' do
371
443
  require_topology :replica_set
372
- min_server_version '4.1'
444
+ min_server_fcv '4.2'
373
445
 
374
446
  let(:start_after) do
375
447
  stream = authorized_collection.watch([])
@@ -430,4 +502,232 @@ describe 'Change stream integration', retry: 4 do
430
502
  end
431
503
  end
432
504
  end
505
+
506
+ describe 'resume_token' do
507
+ let(:stream) { authorized_collection.watch }
508
+
509
+ let(:events) do
510
+ subscriber = EventSubscriber.new
511
+ authorized_client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
512
+ use_stream
513
+ subscriber.succeeded_events.select { |e|
514
+ e.command_name == 'aggregate' || e.command_name === 'getMore'
515
+ }
516
+ end
517
+
518
+ let!(:sample_resume_token) do
519
+ cs = authorized_collection.watch
520
+ authorized_collection.insert_one(a: 1)
521
+ doc = cs.to_enum.next
522
+ cs.close
523
+ doc[:_id]
524
+ end
525
+
526
+ let(:use_stream) do
527
+ stream
528
+ authorized_collection.insert_one(x: 1)
529
+ stream.to_enum.next
530
+ end
531
+
532
+ context 'when batch has been emptied' do
533
+ context '4.2+' do
534
+ min_server_fcv '4.2'
535
+ it 'returns post batch resume token from current command response' do
536
+ expect(events.size).to eq(2)
537
+
538
+ aggregate_response = events.first.reply
539
+ get_more_response = events.last.reply
540
+ expect(aggregate_response['cursor'].key?('postBatchResumeToken')).to eq(true)
541
+ expect(get_more_response['cursor'].key?('postBatchResumeToken')).to eq(true)
542
+
543
+ res_tok = stream.resume_token
544
+ expect(res_tok).to eq(get_more_response['cursor']['postBatchResumeToken'])
545
+ expect(res_tok).to_not eq(aggregate_response['cursor']['postBatchResumeToken'])
546
+ end
547
+ end
548
+
549
+ context '4.0-' do
550
+ max_server_version '4.0'
551
+
552
+ it 'returns _id of previous document returned if one exists' do
553
+ doc = use_stream
554
+ expect(stream.resume_token).to eq(doc['_id'])
555
+ end
556
+
557
+ context 'when start_after is specified' do
558
+ min_server_fcv '4.2'
559
+
560
+ it 'must return startAfter from the initial aggregate if the option was specified' do
561
+ start_after = sample_resume_token
562
+ authorized_collection.insert_one(:a => 1)
563
+ stream = authorized_collection.watch([], { start_after: start_after })
564
+
565
+ expect(stream.resume_token).to eq(start_after)
566
+ end
567
+ end
568
+
569
+ it 'must return resumeAfter from the initial aggregate if the option was specified' do
570
+ resume_after = sample_resume_token
571
+ authorized_collection.insert_one(:a => 1)
572
+ stream = authorized_collection.watch([], { resume_after: resume_after })
573
+
574
+ expect(stream.resume_token).to eq(resume_after)
575
+ end
576
+
577
+ it 'must be empty if neither the startAfter nor resumeAfter options were specified' do
578
+ authorized_collection.insert_one(:a => 1)
579
+ stream = authorized_collection.watch
580
+
581
+ expect(stream.resume_token).to be(nil)
582
+ end
583
+ end
584
+ end
585
+
586
+ context 'before batch has been emptied' do
587
+ it 'returns _id of previous document returned' do
588
+ stream
589
+
590
+ authorized_collection.insert_one(:a => 1)
591
+ authorized_collection.insert_one(:a => 1)
592
+ authorized_collection.insert_one(:a => 1)
593
+ stream.to_enum.next
594
+
595
+ change = stream.to_enum.next
596
+
597
+ expect(stream.resume_token).to eq(change['_id'])
598
+ end
599
+ end
600
+
601
+ # Note that the watch method executes the initial aggregate command
602
+ context 'for non-empty, non-iterated batch, only the initial aggregate command executed' do
603
+
604
+ let (:use_stream) do
605
+ authorized_collection.insert_one(:a => 1)
606
+ stream
607
+ end
608
+
609
+ context 'if startAfter was specified' do
610
+ min_server_fcv '4.2'
611
+
612
+ let (:stream) do
613
+ authorized_collection.watch([], { start_after: sample_resume_token })
614
+ end
615
+
616
+ it 'must return startAfter from the initial aggregate' do
617
+ # Need to sample a doc id from the stream before we use the stream, so
618
+ # the events subscriber does not record these commands as part of the example.
619
+ sample_resume_token
620
+
621
+ # Verify that only the initial aggregate command was executed
622
+ expect(events.size).to eq(1)
623
+ expect(events.first.command_name).to eq('aggregate')
624
+ expect(stream.resume_token).to eq(sample_resume_token)
625
+ end
626
+ end
627
+
628
+ context 'if resumeAfter was specified' do
629
+ let (:stream) do
630
+ authorized_collection.watch([], { resume_after: sample_resume_token })
631
+ end
632
+
633
+ it 'must return resumeAfter from the initial aggregate' do
634
+ sample_resume_token
635
+
636
+ expect(events.size).to eq(1)
637
+ expect(events.first.command_name).to eq('aggregate')
638
+ expect(stream.resume_token).to eq(sample_resume_token)
639
+ end
640
+ end
641
+
642
+ context 'if neither the startAfter nor resumeAfter options were specified' do
643
+ it 'must be empty' do
644
+ expect(events.size).to eq(1)
645
+ expect(events.first.command_name).to eq('aggregate')
646
+ expect(stream.resume_token).to be(nil)
647
+ end
648
+ end
649
+ end
650
+
651
+
652
+ context 'for non-empty, non-iterated batch directly after get_more' do
653
+ let(:next_doc) do
654
+ authorized_collection.insert_one(:a => 1)
655
+ stream.to_enum.next
656
+ end
657
+
658
+ let(:do_get_more) do
659
+ authorized_collection.insert_one(:a => 1)
660
+ stream.instance_variable_get('@cursor').get_more
661
+ end
662
+
663
+ context '4.2+' do
664
+ min_server_fcv '4.2'
665
+
666
+ let(:use_stream) do
667
+ stream
668
+ next_doc
669
+ do_get_more
670
+ end
671
+
672
+ it 'returns post batch resume token from previous command response' do
673
+ expect(events.size).to eq(3)
674
+
675
+ expect(events.last.command_name).to eq('getMore')
676
+
677
+ first_get_more = events[1].reply
678
+ second_get_more = events[2].reply
679
+ expect(first_get_more['cursor'].key?('postBatchResumeToken')).to eq(true)
680
+ expect(second_get_more['cursor'].key?('postBatchResumeToken')).to eq(true)
681
+
682
+ res_tok = stream.resume_token
683
+ expect(res_tok).to eq(first_get_more['cursor']['postBatchResumeToken'])
684
+ expect(res_tok).not_to eq(second_get_more['cursor']['postBatchResumeToken'])
685
+ end
686
+ end
687
+
688
+ context '4.0-' do
689
+ max_server_version '4.0'
690
+
691
+ context 'if a document was returned' do
692
+ let(:use_stream) do
693
+ stream
694
+ next_doc
695
+ do_get_more
696
+ end
697
+
698
+ it 'returns _id of previous document' do
699
+ expect(events.last.command_name).to eq('getMore')
700
+ expect(stream.resume_token).to eq(next_doc['_id'])
701
+ end
702
+ end
703
+
704
+ context 'if a document was not returned' do
705
+ let(:use_stream) do
706
+ stream
707
+ do_get_more
708
+ end
709
+
710
+ context 'when resumeAfter is specified' do
711
+ let (:stream) do
712
+ authorized_collection.watch([], { resume_after: sample_resume_token })
713
+ end
714
+
715
+ it 'must return resumeAfter from the initial aggregate if the option was specified' do
716
+ sample_resume_token
717
+
718
+ expect(events.last.command_name).to eq('getMore')
719
+ expect(stream.resume_token).to eq(sample_resume_token)
720
+ end
721
+ end
722
+
723
+ context 'if neither the startAfter nor resumeAfter options were specified' do
724
+ it 'must be empty' do
725
+ expect(events.last.command_name).to eq('getMore')
726
+ expect(stream.resume_token).to be(nil)
727
+ end
728
+ end
729
+ end
730
+ end
731
+ end
732
+ end
433
733
  end