mongo 2.9.2 → 2.10.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -28,6 +28,7 @@ module Mongo
28
28
  include Monitoring::Publishable
29
29
  include Event::Subscriber
30
30
  include Loggable
31
+ include ClusterTime::Consumer
31
32
 
32
33
  # The default number of legacy read retries.
33
34
  #
@@ -53,6 +54,7 @@ module Mongo
53
54
  # The cluster time key in responses from mongos servers.
54
55
  #
55
56
  # @since 2.5.0
57
+ # @deprecated
56
58
  CLUSTER_TIME = 'clusterTime'.freeze
57
59
 
58
60
  # Instantiate the new cluster.
@@ -74,18 +76,23 @@ module Mongo
74
76
  # @param [ Hash ] options Options. Client constructor forwards its
75
77
  # options to Cluster constructor, although Cluster recognizes
76
78
  # only a subset of the options recognized by Client.
77
- # @option options [ true, false ] :scan Whether to scan all seeds
79
+ # @option options [ true | false ] :scan Whether to scan all seeds
78
80
  # in constructor. The default in driver version 2.x is to do so;
79
81
  # driver version 3.x will not scan seeds in constructor. Opt in to the
80
82
  # new behavior by setting this option to false. *Note:* setting
81
83
  # this option to nil enables scanning seeds in constructor in driver
82
84
  # version 2.x. Driver version 3.x will recognize this option but
83
85
  # will ignore it and will never scan seeds in the constructor.
84
- # @option options [ true, false ] :monitoring_io For internal driver
86
+ # @option options [ true | false ] :monitoring_io For internal driver
85
87
  # use only. Set to false to prevent SDAM-related I/O from being
86
88
  # done by this cluster or servers under it. Note: setting this option
87
89
  # to false will make the cluster non-functional. It is intended for
88
90
  # use in tests which manually invoke SDAM state transitions.
91
+ # @option options [ true | false ] :cleanup For internal driver use only.
92
+ # Set to false to prevent endSessions command being sent to the server
93
+ # to clean up server sessions when the cluster is disconnected, and to
94
+ # to not start the periodic executor. If :monitoring_io is false,
95
+ # :cleanup automatically defaults to false as well.
89
96
  #
90
97
  # @since 2.0.0
91
98
  def initialize(seeds, monitoring, options = Options::Redacted.new)
@@ -93,6 +100,11 @@ module Mongo
93
100
  raise ArgumentError, 'Need server selection semaphore'
94
101
  end
95
102
 
103
+ if options[:monitoring_io] == false && !options.key?(:cleanup)
104
+ options = options.dup
105
+ options[:cleanup] = false
106
+ end
107
+
96
108
  @servers = []
97
109
  @monitoring = monitoring
98
110
  @event_listeners = Event::Listeners.new
@@ -148,12 +160,14 @@ module Mongo
148
160
  return
149
161
  end
150
162
 
151
- @cursor_reaper = CursorReaper.new
152
- @socket_reaper = SocketReaper.new(self)
153
- @periodic_executor = PeriodicExecutor.new(@cursor_reaper, @socket_reaper)
154
- @periodic_executor.run!
163
+ if options[:cleanup] != false
164
+ @cursor_reaper = CursorReaper.new
165
+ @socket_reaper = SocketReaper.new(self)
166
+ @periodic_executor = PeriodicExecutor.new(@cursor_reaper, @socket_reaper)
167
+ @periodic_executor.run!
155
168
 
156
- ObjectSpace.define_finalizer(self, self.class.finalize({}, @periodic_executor, @session_pool))
169
+ ObjectSpace.define_finalizer(self, self.class.finalize({}, @periodic_executor, @session_pool))
170
+ end
157
171
 
158
172
  @connecting = false
159
173
  @connected = true
@@ -225,11 +239,6 @@ module Mongo
225
239
  # @since 2.4.0
226
240
  attr_reader :app_metadata
227
241
 
228
- # @return [ BSON::Document ] The latest cluster time seen.
229
- #
230
- # @since 2.5.0
231
- attr_reader :cluster_time
232
-
233
242
  # @return [ Array<String> ] The addresses of seed servers. Contains
234
243
  # addresses that were given to Cluster when it was instantiated, not
235
244
  # current addresses that the cluster is using as a result of SDAM.
@@ -245,7 +254,14 @@ module Mongo
245
254
 
246
255
  def_delegators :topology, :replica_set?, :replica_set_name, :sharded?,
247
256
  :single?, :unknown?
248
- def_delegators :@cursor_reaper, :register_cursor, :schedule_kill_cursor, :unregister_cursor
257
+
258
+ [:register_cursor, :schedule_kill_cursor, :unregister_cursor].each do |m|
259
+ define_method(m) do |*args|
260
+ if options[:cleanup] != false
261
+ @cursor_reaper.send(m, *args)
262
+ end
263
+ end
264
+ end
249
265
 
250
266
  # Get the maximum number of times the client can retry a read operation
251
267
  # when using legacy read retries.
@@ -393,7 +409,12 @@ module Mongo
393
409
  unless @connecting || @connected
394
410
  return true
395
411
  end
396
- @periodic_executor.stop!
412
+ if options[:cleanup] != false
413
+ if wait
414
+ session_pool.end_sessions
415
+ end
416
+ @periodic_executor.stop!(wait)
417
+ end
397
418
  @servers.each do |server|
398
419
  if server.connected?
399
420
  server.disconnect!(wait)
@@ -519,16 +540,16 @@ module Mongo
519
540
  # @example Get the next primary server.
520
541
  # cluster.next_primary
521
542
  #
522
- # @param [ true, false ] ping Whether to ping the server before selection. Deprecated,
523
- # not necessary with the implementation of the Server Selection specification.
524
- #
543
+ # @param [ true, false ] ping Whether to ping the server before selection.
544
+ # Deprecated and ignored.
545
+ # @param [ Session | nil ] session Optional session to take into account
546
+ # for mongos pinning.
525
547
  #
526
548
  # @return [ Mongo::Server ] A primary server.
527
549
  #
528
550
  # @since 2.0.0
529
- def next_primary(ping = true)
530
- @primary_selector ||= ServerSelector.get(ServerSelector::PRIMARY)
531
- @primary_selector.select_server(self)
551
+ def next_primary(ping = nil, session = nil)
552
+ ServerSelector.primary.select_server(self, nil, session)
532
553
  end
533
554
 
534
555
  # Get the connection pool for the server.
@@ -559,11 +580,7 @@ module Mongo
559
580
  def update_cluster_time(result)
560
581
  if cluster_time_doc = result.cluster_time
561
582
  @cluster_time_lock.synchronize do
562
- if @cluster_time.nil?
563
- @cluster_time = cluster_time_doc
564
- elsif cluster_time_doc[CLUSTER_TIME] > @cluster_time[CLUSTER_TIME]
565
- @cluster_time = cluster_time_doc
566
- end
583
+ advance_cluster_time(cluster_time_doc)
567
584
  end
568
585
  end
569
586
  end
@@ -670,6 +687,11 @@ module Mongo
670
687
  # @note If the cluster has no data bearing servers, for example because
671
688
  # the deployment is in the middle of a failover, this method returns
672
689
  # false.
690
+ #
691
+ # @note This method returns as soon as the driver connects to any single
692
+ # server in the deployment. Whether deployment overall supports sessions
693
+ # can change depending on how many servers have been contacted, if
694
+ # the servers are configured differently.
673
695
  def sessions_supported?
674
696
  if topology.data_bearing_servers?
675
697
  return !!topology.logical_session_timeout
@@ -74,7 +74,7 @@ module Mongo
74
74
  #
75
75
  # @since 2.4.0
76
76
  def has_readable_server?(cluster, server_selector = nil)
77
- (server_selector || ServerSelector.get(mode: :primary)).candidates(cluster).any?
77
+ (server_selector || ServerSelector.primary).candidates(cluster).any?
78
78
  end
79
79
 
80
80
  # Determine if the topology would select a writable server for the
@@ -0,0 +1,139 @@
1
+ # Copyright (C) 2019 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ # ClusterTime encapsulates cluster time storage and operations.
17
+ #
18
+ # The primary operation performed on the cluster time is advancing it:
19
+ # given another cluster time, pick the newer of the two.
20
+ #
21
+ # This class provides comparison methods that are used to figure out which
22
+ # cluster time is newer, and provides diagnostics in lint mode when
23
+ # the actual time is missing from a cluster time document.
24
+ #
25
+ # @api private
26
+ class ClusterTime < BSON::Document
27
+ def initialize(elements = nil)
28
+ super
29
+
30
+ if Lint.enabled? && !self['clusterTime']
31
+ raise ArgumentError, 'Creating a cluster time without clusterTime field'
32
+ end
33
+ end
34
+
35
+ # Advances the cluster time in the receiver to the cluster time in +other+.
36
+ #
37
+ # +other+ can be nil or be behind the cluster time in the receiver; in
38
+ # these cases the receiver is returned unmodified. If receiver is advanced,
39
+ # a new ClusterTime object is returned.
40
+ #
41
+ # Return value is nil or a ClusterTime instance.
42
+ def advance(other)
43
+ if self['clusterTime'] && other['clusterTime'] &&
44
+ other['clusterTime'] > self['clusterTime']
45
+ then
46
+ ClusterTime[other]
47
+ else
48
+ self
49
+ end
50
+ end
51
+
52
+ # Compares two ClusterTime instances by comparing their timestamps.
53
+ def <=>(other)
54
+ if self['clusterTime'] && other['clusterTime']
55
+ self['clusterTime'] <=> other['clusterTime']
56
+ elsif !self['clusterTime']
57
+ raise ArgumentError, "Cannot compare cluster times when receiver is missing clusterTime key: #{inspect}"
58
+ else other['clusterTime']
59
+ raise ArgumentError, "Cannot compare cluster times when other is missing clusterTime key: #{other.inspect}"
60
+ end
61
+ end
62
+
63
+ # Older Rubies do not implement other logical operators through <=>.
64
+ # TODO revise whether these methods are needed when
65
+ # https://jira.mongodb.org/browse/RUBY-1622 is implemented.
66
+ def >=(other)
67
+ (self <=> other) != -1
68
+ end
69
+ def >(other)
70
+ (self <=> other) == 1
71
+ end
72
+ def <=(other)
73
+ (self <=> other) != 1
74
+ end
75
+ def <(other)
76
+ (self <=> other) == -1
77
+ end
78
+
79
+ # Compares two ClusterTime instances by comparing their timestamps.
80
+ def ==(other)
81
+ if self['clusterTime'] && other['clusterTime'] &&
82
+ self['clusterTime'] == other['clusterTime']
83
+ then
84
+ true
85
+ else
86
+ false
87
+ end
88
+ end
89
+
90
+ class << self
91
+ # Converts a BSON::Document to a ClusterTime.
92
+ #
93
+ # +doc+ can be nil, in which case nil is returned.
94
+ def [](doc)
95
+ if doc.nil? || doc.is_a?(ClusterTime)
96
+ doc
97
+ else
98
+ ClusterTime.new(doc)
99
+ end
100
+ end
101
+ end
102
+
103
+ # This module provides common cluster time tracking behavior.
104
+ #
105
+ # @note Although attributes and methods defined in this module are part of
106
+ # the public API for the classes including this module, the fact that
107
+ # the methods are defined on this module and not directly on the
108
+ # including classes is not part of the public API.
109
+ module Consumer
110
+
111
+ # The cluster time tracked by the object including this module.
112
+ #
113
+ # @return [ nil | ClusterTime ] The cluster time.
114
+ #
115
+ # Changed in version 2.9.0: This attribute became an instance of
116
+ # ClusterTime, which is a subclass of BSON::Document.
117
+ # Previously it was an instance of BSON::Document.
118
+ #
119
+ # @since 2.5.0
120
+ attr_reader :cluster_time
121
+
122
+ # Advance the tracked cluster time document for the object including
123
+ # this module.
124
+ #
125
+ # @param [ BSON::Document ] new_cluster_time The new cluster time document.
126
+ #
127
+ # @return [ ClusterTime ] The resulting cluster time.
128
+ #
129
+ # @since 2.5.0
130
+ def advance_cluster_time(new_cluster_time)
131
+ if @cluster_time
132
+ @cluster_time = @cluster_time.advance(new_cluster_time)
133
+ else
134
+ @cluster_time = ClusterTime[new_cluster_time]
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -53,7 +53,7 @@ module Mongo
53
53
  # Options that can be updated on a new Collection instance via the #with method.
54
54
  #
55
55
  # @since 2.1.0
56
- CHANGEABLE_OPTIONS = [ :read, :read_concern, :write ].freeze
56
+ CHANGEABLE_OPTIONS = [ :read, :read_concern, :write, :write_concern ].freeze
57
57
 
58
58
  # Check if a collection is equal to another object. Will check the name and
59
59
  # the database for equality.
@@ -80,12 +80,28 @@ module Mongo
80
80
  # @param [ String, Symbol ] name The collection name.
81
81
  # @param [ Hash ] options The collection options.
82
82
  #
83
+ # @option options [ Hash ] :write Deprecated. Equivalent to :write_concern
84
+ # option.
85
+ # @option options [ Hash ] :write_concern The write concern options.
86
+ # Can be :w => Integer|String, :fsync => Boolean, :j => Boolean.
87
+ #
83
88
  # @since 2.0.0
84
89
  def initialize(database, name, options = {})
85
90
  raise Error::InvalidCollectionName.new unless name
91
+ if options[:write] && options[:write_concern] && options[:write] != options[:write_concern]
92
+ raise ArgumentError, "If :write and :write_concern are both given, they must be identical: #{options.inspect}"
93
+ end
86
94
  @database = database
87
95
  @name = name.to_s.freeze
88
- @options = options.freeze
96
+ @options = options.dup
97
+ =begin WriteConcern object support
98
+ if @options[:write_concern].is_a?(WriteConcern::Base)
99
+ # Cache the instance so that we do not needlessly reconstruct it.
100
+ @write_concern = @options[:write_concern]
101
+ @options[:write_concern] = @write_concern.options
102
+ end
103
+ =end
104
+ @options.freeze
89
105
  end
90
106
 
91
107
  # Get the read concern for this collection instance.
@@ -133,17 +149,40 @@ module Mongo
133
149
  #
134
150
  # @since 2.0.0
135
151
  def write_concern
136
- @write_concern ||= WriteConcern.get(options[:write] || database.write_concern)
152
+ @write_concern ||= WriteConcern.get(
153
+ options[:write_concern] || options[:write] || database.write_concern)
154
+ end
155
+
156
+ # Get the write concern for the collection, given the session.
157
+ #
158
+ # If the session is in a transaction and the collection
159
+ # has an unacknowledged write concern, remove the write
160
+ # concern's :w option. Otherwise, return the unmodified
161
+ # write concern.
162
+ #
163
+ # @return [ Mongo::WriteConcern ] The write concern.
164
+ #
165
+ # @api private
166
+ def write_concern_with_session(session)
167
+ wc = write_concern
168
+ if session && session.in_transaction?
169
+ if wc && !wc.acknowledged?
170
+ opts = wc.options.dup
171
+ opts.delete(:w)
172
+ return WriteConcern.get(opts)
173
+ end
174
+ end
175
+ wc
137
176
  end
138
177
 
139
178
  # Provides a new collection with either a new read preference or new write concern
140
179
  # merged over the existing read preference / write concern.
141
180
  #
142
- # @example Get a collection with changed read preference.
143
- # collection.with(:read => { :mode => :primary_preferred })
181
+ # @example Get a collection with a changed read preference.
182
+ # collection.with(read: { mode: :primary_preferred })
144
183
  #
145
- # @example Get a collection with changed write concern.
146
- # collection.with(:write => { w: 3 })
184
+ # @example Get a collection with a changed write concern.
185
+ # collection.with(write_concern: { w: 3 })
147
186
 
148
187
  # @param [ Hash ] new_options The new options to use.
149
188
  #
@@ -154,7 +193,14 @@ module Mongo
154
193
  new_options.keys.each do |k|
155
194
  raise Error::UnchangeableCollectionOption.new(k) unless CHANGEABLE_OPTIONS.include?(k)
156
195
  end
157
- Collection.new(database, name, options.merge(new_options))
196
+ options = @options.dup
197
+ if options[:write] && new_options[:write_concern]
198
+ options.delete(:write)
199
+ end
200
+ if options[:write_concern] && new_options[:write]
201
+ options.delete(:write_concern)
202
+ end
203
+ Collection.new(database, name, options.update(new_options))
158
204
  end
159
205
 
160
206
  # Is the collection capped?
@@ -166,7 +212,7 @@ module Mongo
166
212
  #
167
213
  # @since 2.0.0
168
214
  def capped?
169
- database.read_command(:collstats => name).documents[0][CAPPED]
215
+ database.command(:collstats => name).documents[0][CAPPED]
170
216
  end
171
217
 
172
218
  # Force the collection to be created in the database.
@@ -182,13 +228,22 @@ module Mongo
182
228
  #
183
229
  # @since 2.0.0
184
230
  def create(opts = {})
231
+ # Passing read options to create command causes it to break.
232
+ # Filter the read options out.
233
+ # TODO put the list of read options in a class-level constant when
234
+ # we figure out what the full set of them is.
235
+ options = Hash[self.options.reject do |key, value|
236
+ %w(read read_preference).include?(key.to_s)
237
+ end]
185
238
  operation = { :create => name }.merge(options)
186
239
  operation.delete(:write)
187
- server = next_primary
188
- if (options[:collation] || options[Operation::COLLATION]) && !server.features.collation_enabled?
189
- raise Error::UnsupportedCollation.new
190
- end
240
+ operation.delete(:write_concern)
191
241
  client.send(:with_session, opts) do |session|
242
+ server = next_primary(nil, session)
243
+ if (options[:collation] || options[Operation::COLLATION]) && !server.features.collation_enabled?
244
+ raise Error::UnsupportedCollation
245
+ end
246
+
192
247
  Operation::Create.new({
193
248
  selector: operation,
194
249
  db_name: database.name,
@@ -220,7 +275,7 @@ module Mongo
220
275
  db_name: database.name,
221
276
  write_concern: write_concern,
222
277
  session: session
223
- }).execute(next_primary)
278
+ }).execute(next_primary(nil, session))
224
279
  end
225
280
  rescue Error::OperationFailure => ex
226
281
  raise ex unless ex.message =~ /ns not found/
@@ -277,18 +332,21 @@ module Mongo
277
332
  # @param [ Array<Hash> ] pipeline The aggregation pipeline.
278
333
  # @param [ Hash ] options The aggregation options.
279
334
  #
280
- # @option options [ true, false ] :allow_disk_use Set to true if disk usage is allowed during
281
- # the aggregation.
282
- # @option options [ Integer ] :batch_size The number of documents to return per batch.
283
- # @option options [ Integer ] :max_time_ms The maximum amount of time in milliseconds to allow the
284
- # aggregation to run.
285
- # @option options [ true, false ] :use_cursor Indicates whether the command will request that the server
286
- # provide results using a cursor. Note that as of server version 3.6, aggregations always provide results
287
- # using a cursor and this option is therefore not valid.
335
+ # @option options [ true, false ] :allow_disk_use Set to true if disk
336
+ # usage is allowed during the aggregation.
337
+ # @option options [ Integer ] :batch_size The number of documents to return
338
+ # per batch.
288
339
  # @option options [ true, false ] :bypass_document_validation Whether or
289
340
  # not to skip document level validation.
290
341
  # @option options [ Hash ] :collation The collation to use.
291
342
  # @option options [ String ] :comment Associate a comment with the aggregation.
343
+ # @option options [ String ] :hint The index to use for the aggregation.
344
+ # @option options [ Integer ] :max_time_ms The maximum amount of time in
345
+ # milliseconds to allow the aggregation to run.
346
+ # @option options [ true, false ] :use_cursor Indicates whether the command
347
+ # will request that the server provide results using a cursor. Note that
348
+ # as of server version 3.6, aggregations always provide results using a
349
+ # cursor and this option is therefore not valid.
292
350
  # @option options [ Session ] :session The session to use.
293
351
  #
294
352
  # @return [ Aggregation ] The aggregation object.
@@ -475,6 +533,7 @@ module Mongo
475
533
  # @since 2.0.0
476
534
  def insert_one(document, opts = {})
477
535
  client.send(:with_session, opts) do |session|
536
+ write_concern = write_concern_with_session(session)
478
537
  write_with_retry(session, write_concern) do |server, txn_num|
479
538
  Operation::Insert.new(
480
539
  :documents => [ document ],
@@ -620,7 +679,7 @@ module Mongo
620
679
  # collection.update_many({ name: 'test'}, '$set' => { name: 'test1' })
621
680
  #
622
681
  # @param [ Hash ] filter The filter to use.
623
- # @param [ Hash ] update The update statement.
682
+ # @param [ Hash | Array<Hash> ] update The update document or pipeline.
624
683
  # @param [ Hash ] options The options.
625
684
  #
626
685
  # @option options [ true, false ] :upsert Whether to upsert if the
@@ -645,7 +704,7 @@ module Mongo
645
704
  # collection.update_one({ name: 'test'}, '$set' => { name: 'test1'})
646
705
  #
647
706
  # @param [ Hash ] filter The filter to use.
648
- # @param [ Hash ] update The update statement.
707
+ # @param [ Hash | Array<Hash> ] update The update document or pipeline.
649
708
  # @param [ Hash ] options The options.
650
709
  #
651
710
  # @option options [ true, false ] :upsert Whether to upsert if the
@@ -700,7 +759,7 @@ module Mongo
700
759
  # collection.find_one_and_update({ name: 'test' }, { "$set" => { name: 'test1' }}, :return_document => :after)
701
760
  #
702
761
  # @param [ Hash ] filter The filter to use.
703
- # @param [ BSON::Document ] update The update statement.
762
+ # @param [ Hash | Array<Hash> ] update The update document or pipeline.
704
763
  # @param [ Hash ] options The options.
705
764
  #
706
765
  # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command