mongo 2.8.0 → 2.9.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 (276) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +12 -0
  5. data/lib/mongo.rb +15 -1
  6. data/lib/mongo/address/ipv6.rb +0 -2
  7. data/lib/mongo/auth/scram/conversation.rb +0 -3
  8. data/lib/mongo/bulk_write/result_combiner.rb +12 -2
  9. data/lib/mongo/client.rb +59 -6
  10. data/lib/mongo/cluster.rb +19 -8
  11. data/lib/mongo/cluster/reapers/cursor_reaper.rb +0 -2
  12. data/lib/mongo/cluster/reapers/socket_reaper.rb +12 -9
  13. data/lib/mongo/collection.rb +1 -1
  14. data/lib/mongo/collection/view/aggregation.rb +5 -1
  15. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  16. data/lib/mongo/collection/view/change_stream.rb +30 -10
  17. data/lib/mongo/collection/view/iterable.rb +13 -6
  18. data/lib/mongo/collection/view/map_reduce.rb +12 -10
  19. data/lib/mongo/collection/view/readable.rb +19 -14
  20. data/lib/mongo/cursor.rb +12 -8
  21. data/lib/mongo/database.rb +10 -7
  22. data/lib/mongo/database/view.rb +18 -11
  23. data/lib/mongo/error.rb +2 -2
  24. data/lib/mongo/error/connection_check_out_timeout.rb +49 -0
  25. data/lib/mongo/error/operation_failure.rb +9 -9
  26. data/lib/mongo/error/parser.rb +25 -3
  27. data/lib/mongo/error/pool_closed_error.rb +43 -0
  28. data/lib/mongo/error/sdam_error_detection.rb +18 -0
  29. data/lib/mongo/grid/file/chunk.rb +0 -2
  30. data/lib/mongo/grid/fs_bucket.rb +26 -12
  31. data/lib/mongo/grid/stream/read.rb +36 -21
  32. data/lib/mongo/index/view.rb +11 -7
  33. data/lib/mongo/logger.rb +0 -2
  34. data/lib/mongo/monitoring.rb +31 -0
  35. data/lib/mongo/monitoring/cmap_log_subscriber.rb +53 -0
  36. data/lib/mongo/monitoring/event.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap.rb +25 -0
  38. data/lib/mongo/monitoring/event/cmap/base.rb +28 -0
  39. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +78 -0
  40. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +56 -0
  41. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +63 -0
  42. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +64 -0
  43. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +103 -0
  44. data/lib/mongo/monitoring/event/cmap/connection_created.rb +64 -0
  45. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +64 -0
  46. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +57 -0
  47. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +57 -0
  48. data/lib/mongo/monitoring/event/cmap/pool_created.rb +63 -0
  49. data/lib/mongo/monitoring/event/command_started.rb +12 -3
  50. data/lib/mongo/monitoring/publishable.rb +10 -2
  51. data/lib/mongo/operation.rb +0 -1
  52. data/lib/mongo/operation/find/legacy/result.rb +1 -0
  53. data/lib/mongo/operation/list_collections/result.rb +7 -1
  54. data/lib/mongo/operation/result.rb +10 -1
  55. data/lib/mongo/operation/shared/executable.rb +15 -0
  56. data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +29 -0
  57. data/lib/mongo/operation/shared/specifiable.rb +0 -16
  58. data/lib/mongo/operation/update/legacy/result.rb +1 -0
  59. data/lib/mongo/protocol/compressed.rb +0 -2
  60. data/lib/mongo/protocol/msg.rb +25 -2
  61. data/lib/mongo/retryable.rb +171 -33
  62. data/lib/mongo/server.rb +26 -7
  63. data/lib/mongo/server/app_metadata.rb +0 -2
  64. data/lib/mongo/server/connectable.rb +8 -2
  65. data/lib/mongo/server/connection.rb +83 -13
  66. data/lib/mongo/server/connection_base.rb +1 -1
  67. data/lib/mongo/server/connection_pool.rb +439 -43
  68. data/lib/mongo/server/monitor/connection.rb +4 -1
  69. data/lib/mongo/session.rb +37 -5
  70. data/lib/mongo/session/session_pool.rb +2 -2
  71. data/lib/mongo/socket.rb +0 -2
  72. data/lib/mongo/socket/ssl.rb +0 -2
  73. data/lib/mongo/uri.rb +127 -66
  74. data/lib/mongo/uri/srv_protocol.rb +35 -13
  75. data/lib/mongo/version.rb +1 -1
  76. data/spec/README.md +190 -63
  77. data/spec/integration/change_stream_spec.rb +64 -0
  78. data/spec/integration/command_spec.rb +0 -7
  79. data/spec/integration/error_detection_spec.rb +39 -0
  80. data/spec/integration/read_concern.rb +83 -0
  81. data/spec/integration/retryable_writes_spec.rb +6 -50
  82. data/spec/integration/sdam_error_handling_spec.rb +60 -7
  83. data/spec/integration/ssl_uri_options_spec.rb +24 -0
  84. data/spec/integration/step_down_spec.rb +197 -0
  85. data/spec/lite_spec_helper.rb +4 -0
  86. data/spec/mongo/client_construction_spec.rb +42 -17
  87. data/spec/mongo/client_spec.rb +32 -1
  88. data/spec/mongo/cluster/socket_reaper_spec.rb +2 -2
  89. data/spec/mongo/cluster_spec.rb +36 -2
  90. data/spec/mongo/collection/view/aggregation_spec.rb +2 -0
  91. data/spec/mongo/collection/view/change_stream_spec.rb +28 -28
  92. data/spec/mongo/collection/view/readable_spec.rb +1 -1
  93. data/spec/mongo/collection/view_spec.rb +3 -1
  94. data/spec/mongo/cursor_spec.rb +5 -5
  95. data/spec/mongo/error/parser_spec.rb +61 -1
  96. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  97. data/spec/mongo/monitoring/event/cmap/connection_check_out_failed_spec.rb +23 -0
  98. data/spec/mongo/monitoring/event/cmap/connection_check_out_started_spec.rb +19 -0
  99. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +23 -0
  100. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +23 -0
  101. data/spec/mongo/monitoring/event/cmap/connection_closed_spec.rb +27 -0
  102. data/spec/mongo/monitoring/event/cmap/connection_created_spec.rb +24 -0
  103. data/spec/mongo/monitoring/event/cmap/connection_ready_spec.rb +24 -0
  104. data/spec/mongo/monitoring/event/cmap/pool_cleared_spec.rb +19 -0
  105. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +19 -0
  106. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +26 -0
  107. data/spec/mongo/operation/delete/bulk_spec.rb +1 -6
  108. data/spec/mongo/operation/delete/command_spec.rb +1 -1
  109. data/spec/mongo/operation/delete/op_msg_spec.rb +1 -1
  110. data/spec/mongo/operation/delete_spec.rb +4 -4
  111. data/spec/mongo/operation/insert/bulk_spec.rb +1 -1
  112. data/spec/mongo/operation/insert/command_spec.rb +1 -1
  113. data/spec/mongo/operation/insert/op_msg_spec.rb +1 -1
  114. data/spec/mongo/operation/update/bulk_spec.rb +1 -1
  115. data/spec/mongo/operation/update/command_spec.rb +2 -2
  116. data/spec/mongo/operation/update/op_msg_spec.rb +2 -2
  117. data/spec/mongo/protocol/msg_spec.rb +11 -0
  118. data/spec/mongo/retryable_spec.rb +78 -25
  119. data/spec/mongo/server/connection_pool_spec.rb +661 -126
  120. data/spec/mongo/server/connection_spec.rb +55 -7
  121. data/spec/mongo/server_spec.rb +5 -0
  122. data/spec/mongo/uri/srv_protocol_spec.rb +135 -2
  123. data/spec/mongo/uri_option_parsing_spec.rb +511 -0
  124. data/spec/mongo/uri_spec.rb +42 -6
  125. data/spec/spec_helper.rb +1 -84
  126. data/spec/spec_tests/cmap_spec.rb +50 -0
  127. data/spec/spec_tests/command_monitoring_spec.rb +7 -18
  128. data/spec/spec_tests/crud_spec.rb +3 -49
  129. data/spec/spec_tests/data/cmap/connection-must-have-id.yml +21 -0
  130. data/spec/spec_tests/data/cmap/connection-must-order-ids.yml +21 -0
  131. data/spec/spec_tests/data/cmap/pool-checkin-destroy-closed.yml +24 -0
  132. data/spec/spec_tests/data/cmap/pool-checkin-destroy-stale.yml +24 -0
  133. data/spec/spec_tests/data/cmap/pool-checkin-make-available.yml +21 -0
  134. data/spec/spec_tests/data/cmap/pool-checkin.yml +18 -0
  135. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +13 -0
  136. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +28 -0
  137. data/spec/spec_tests/data/cmap/pool-checkout-multiple.yml +34 -0
  138. data/spec/spec_tests/data/cmap/pool-checkout-no-idle.yml +31 -0
  139. data/spec/spec_tests/data/cmap/pool-checkout-no-stale.yml +29 -0
  140. data/spec/spec_tests/data/cmap/pool-close-destroy-conns.yml +26 -0
  141. data/spec/spec_tests/data/cmap/pool-close.yml +11 -0
  142. data/spec/spec_tests/data/cmap/pool-create-max-size.yml +56 -0
  143. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +27 -0
  144. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +20 -0
  145. data/spec/spec_tests/data/cmap/pool-create.yml +12 -0
  146. data/spec/spec_tests/data/cmap/wait-queue-fairness.yml +94 -0
  147. data/spec/spec_tests/data/cmap/wait-queue-timeout.yml +41 -0
  148. data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +157 -0
  149. data/spec/spec_tests/data/retryable_reads/aggregate.yml +87 -0
  150. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +149 -0
  151. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +61 -0
  152. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +149 -0
  153. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +65 -0
  154. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +153 -0
  155. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +61 -0
  156. data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +150 -0
  157. data/spec/spec_tests/data/retryable_reads/count.yml +64 -0
  158. data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +150 -0
  159. data/spec/spec_tests/data/retryable_reads/countDocuments.yml +64 -0
  160. data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +156 -0
  161. data/spec/spec_tests/data/retryable_reads/distinct.yml +71 -0
  162. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors.yml +148 -0
  163. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount.yml +62 -0
  164. data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +160 -0
  165. data/spec/spec_tests/data/retryable_reads/find.yml +86 -0
  166. data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +154 -0
  167. data/spec/spec_tests/data/retryable_reads/findOne.yml +68 -0
  168. data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +173 -0
  169. data/spec/spec_tests/data/retryable_reads/gridfs-download.yml +79 -0
  170. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +174 -0
  171. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName.yml +79 -0
  172. data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +143 -0
  173. data/spec/spec_tests/data/retryable_reads/listCollectionNames.yml +59 -0
  174. data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +144 -0
  175. data/spec/spec_tests/data/retryable_reads/listCollectionObjects.yml +59 -0
  176. data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +143 -0
  177. data/spec/spec_tests/data/retryable_reads/listCollections.yml +59 -0
  178. data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +143 -0
  179. data/spec/spec_tests/data/retryable_reads/listDatabaseNames.yml +59 -0
  180. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +144 -0
  181. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects.yml +59 -0
  182. data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +144 -0
  183. data/spec/spec_tests/data/retryable_reads/listDatabases.yml +59 -0
  184. data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +144 -0
  185. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +60 -0
  186. data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +145 -0
  187. data/spec/spec_tests/data/retryable_reads/listIndexes.yml +60 -0
  188. data/spec/spec_tests/data/retryable_reads/mapReduce.yml +60 -0
  189. data/spec/spec_tests/data/retryable_writes/bulkWrite-serverErrors.yml +10 -7
  190. data/spec/spec_tests/data/retryable_writes/bulkWrite.yml +15 -22
  191. data/spec/spec_tests/data/retryable_writes/deleteMany.yml +22 -0
  192. data/spec/spec_tests/data/retryable_writes/deleteOne-serverErrors.yml +8 -7
  193. data/spec/spec_tests/data/retryable_writes/deleteOne.yml +5 -8
  194. data/spec/spec_tests/data/retryable_writes/findOneAndDelete-serverErrors.yml +8 -7
  195. data/spec/spec_tests/data/retryable_writes/findOneAndDelete.yml +5 -8
  196. data/spec/spec_tests/data/retryable_writes/findOneAndReplace-serverErrors.yml +8 -7
  197. data/spec/spec_tests/data/retryable_writes/findOneAndReplace.yml +5 -8
  198. data/spec/spec_tests/data/retryable_writes/findOneAndUpdate-serverErrors.yml +8 -7
  199. data/spec/spec_tests/data/retryable_writes/findOneAndUpdate.yml +5 -8
  200. data/spec/spec_tests/data/retryable_writes/insertMany-serverErrors.yml +8 -7
  201. data/spec/spec_tests/data/retryable_writes/insertMany.yml +5 -8
  202. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +10 -45
  203. data/spec/spec_tests/data/retryable_writes/insertOne.yml +5 -8
  204. data/spec/spec_tests/data/retryable_writes/replaceOne-serverErrors.yml +8 -7
  205. data/spec/spec_tests/data/retryable_writes/replaceOne.yml +5 -8
  206. data/spec/spec_tests/data/retryable_writes/updateMany.yml +27 -0
  207. data/spec/spec_tests/data/retryable_writes/updateOne-serverErrors.yml +8 -7
  208. data/spec/spec_tests/data/retryable_writes/updateOne.yml +5 -14
  209. data/spec/spec_tests/data/transactions/abort.yml +7 -2
  210. data/spec/spec_tests/data/transactions/bulk.yml +7 -2
  211. data/spec/spec_tests/data/transactions/causal-consistency.yml +11 -4
  212. data/spec/spec_tests/data/transactions/commit.yml +11 -4
  213. data/spec/spec_tests/data/transactions/count.yml +64 -0
  214. data/spec/spec_tests/data/transactions/delete.yml +7 -2
  215. data/spec/spec_tests/data/transactions/error-labels.yml +8 -2
  216. data/spec/spec_tests/data/transactions/errors.yml +7 -2
  217. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +7 -2
  218. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +7 -2
  219. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +7 -2
  220. data/spec/spec_tests/data/transactions/insert.yml +9 -2
  221. data/spec/spec_tests/data/transactions/isolation.yml +7 -2
  222. data/spec/spec_tests/data/transactions/read-concern.yml +15 -6
  223. data/spec/spec_tests/data/transactions/read-pref.yml +7 -2
  224. data/spec/spec_tests/data/transactions/reads.yml +8 -48
  225. data/spec/spec_tests/data/transactions/retryable-abort.yml +7 -2
  226. data/spec/spec_tests/data/transactions/retryable-commit.yml +7 -2
  227. data/spec/spec_tests/data/transactions/retryable-writes.yml +7 -2
  228. data/spec/spec_tests/data/transactions/run-command.yml +7 -2
  229. data/spec/spec_tests/data/transactions/transaction-options.yml +7 -2
  230. data/spec/spec_tests/data/transactions/update.yml +7 -2
  231. data/spec/spec_tests/data/transactions/write-concern.yml +7 -2
  232. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -1
  233. data/spec/spec_tests/data/transactions_api/callback-commits.yml +6 -1
  234. data/spec/spec_tests/data/transactions_api/callback-retry.yml +6 -1
  235. data/spec/spec_tests/data/transactions_api/commit-retry.yml +6 -1
  236. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +6 -3
  237. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +6 -1
  238. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +6 -1
  239. data/spec/spec_tests/data/transactions_api/commit.yml +6 -1
  240. data/spec/spec_tests/data/transactions_api/transaction-options.yml +6 -1
  241. data/spec/spec_tests/retryable_reads_spec.rb +11 -0
  242. data/spec/spec_tests/retryable_writes_spec.rb +4 -69
  243. data/spec/spec_tests/transactions_api_spec.rb +42 -37
  244. data/spec/spec_tests/transactions_spec.rb +42 -33
  245. data/spec/support/authorization.rb +12 -0
  246. data/spec/support/change_streams/operation.rb +1 -1
  247. data/spec/support/client_registry.rb +20 -0
  248. data/spec/support/cluster_config.rb +16 -15
  249. data/spec/support/cluster_tools.rb +346 -0
  250. data/spec/support/cmap.rb +367 -0
  251. data/spec/support/cmap/verifier.rb +46 -0
  252. data/spec/support/command_monitoring.rb +4 -6
  253. data/spec/support/common_shortcuts.rb +6 -0
  254. data/spec/support/connection_string.rb +2 -2
  255. data/spec/support/crud.rb +171 -184
  256. data/spec/support/crud/operation.rb +43 -0
  257. data/spec/support/crud/outcome.rb +53 -0
  258. data/spec/support/crud/read.rb +102 -12
  259. data/spec/support/crud/requirement.rb +69 -0
  260. data/spec/support/crud/spec.rb +68 -0
  261. data/spec/support/crud/test.rb +141 -0
  262. data/spec/support/crud/verifier.rb +96 -18
  263. data/spec/support/crud/write.rb +18 -3
  264. data/spec/support/event_subscriber.rb +15 -0
  265. data/spec/support/primary_socket.rb +2 -2
  266. data/spec/support/spec_config.rb +89 -20
  267. data/spec/support/transactions.rb +2 -306
  268. data/spec/support/transactions/operation.rb +7 -7
  269. data/spec/support/transactions/spec.rb +28 -0
  270. data/spec/support/transactions/test.rb +191 -0
  271. data/spec/support/utils.rb +123 -0
  272. metadata +202 -9
  273. metadata.gz.sig +0 -0
  274. data/lib/mongo/server/connection_pool/queue.rb +0 -359
  275. data/spec/mongo/server/connection_pool/queue_spec.rb +0 -353
  276. data/spec/support/transactions/verifier.rb +0 -97
@@ -22,6 +22,7 @@ module Mongo
22
22
  #
23
23
  # @since 2.0.0
24
24
  class Result < Operation::Result
25
+ include Operation::Result::UseLegacyErrorParser
25
26
 
26
27
  # Whether an existing document was updated.
27
28
  #
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'zlib'
16
-
17
15
  module Mongo
18
16
  module Protocol
19
17
 
@@ -31,6 +31,12 @@ module Mongo
31
31
  # @since 2.5.0
32
32
  DATABASE_IDENTIFIER = '$db'.freeze
33
33
 
34
+ # Keys that the driver adds to commands. These are going to be
35
+ # moved to the end of the hash for better logging.
36
+ #
37
+ # @api private
38
+ INTERNAL_KEYS = Set.new(%w($clusterTime lsid signature txnNumber)).freeze
39
+
34
40
  # Creates a new OP_MSG protocol message
35
41
  #
36
42
  # @example Create a OP_MSG wire protocol message
@@ -82,10 +88,27 @@ module Mongo
82
88
  #
83
89
  # @since 2.5.0
84
90
  def payload
91
+ # Reorder keys in global_args for better logging - see
92
+ # https://jira.mongodb.org/browse/RUBY-1591.
93
+ # Note that even without the reordering, the payload is not an exact
94
+ # match to what is sent over the wire because the command as used in
95
+ # the published eent combines keys from multiple sections of the
96
+ # payload sent over the wire.
97
+ ordered_command = {}
98
+ skipped_command = {}
99
+ command.each do |k, v|
100
+ if INTERNAL_KEYS.member?(k.to_s)
101
+ skipped_command[k] = v
102
+ else
103
+ ordered_command[k] = v
104
+ end
105
+ end
106
+ ordered_command.update(skipped_command)
107
+
85
108
  BSON::Document.new(
86
- command_name: command.keys.first.to_s,
109
+ command_name: ordered_command.keys.first.to_s,
87
110
  database_name: global_args[DATABASE_IDENTIFIER],
88
- command: command,
111
+ command: ordered_command,
89
112
  request_id: request_id,
90
113
  reply: sections[0]
91
114
  )
@@ -19,50 +19,115 @@ module Mongo
19
19
  # @since 2.1.0
20
20
  module Retryable
21
21
 
22
- # Execute a read operation with a retry.
22
+ # Execute a read operation returning a cursor with retrying.
23
+ #
24
+ # This method performs server selection for the specified server selector
25
+ # and yields to the provided block, which should execute the initial
26
+ # query operation and return its result. The block will be passed the
27
+ # server selected for the operation. If the block raises an exception,
28
+ # and this exception corresponds to a read retryable error, and read
29
+ # retries are enabled for the client, this method will perform server
30
+ # selection again and yield to the block again (with potentially a
31
+ # different server). If the block returns successfully, the result
32
+ # of the block (which should be a Mongo::Operation::Result) is used to
33
+ # construct a Mongo::Cursor object for the result set. The cursor
34
+ # is then returned.
35
+ #
36
+ # If modern retry reads are on (which is the default), the initial read
37
+ # operation will be retried once. If legacy retry reads are on, the
38
+ # initial read operation will be retried zero or more times depending
39
+ # on the :max_read_retries client setting, the default for which is 1.
40
+ # To disable read retries, turn off modern read retries by setting
41
+ # retry_reads: false and set :max_read_retries to 0 on the client.
23
42
  #
24
43
  # @api private
25
44
  #
26
- # @example Execute the read.
27
- # read_with_retry do
45
+ # @example Execute a read returning a cursor.
46
+ # cursor = read_with_retry_cursor(session, server_selector, view) do |server|
47
+ # # return a Mongo::Operation::Result
28
48
  # ...
29
49
  # end
30
50
  #
31
- # @note This only retries read operations on socket errors.
51
+ # @param [ Mongo::Session ] session The session that the operation is being
52
+ # run on.
53
+ # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
54
+ # selector for the operation.
55
+ # @param [ CollectionView ] view The +CollectionView+ defining the query.
56
+ # @param [ Proc ] block The block to execute.
57
+ #
58
+ # @return [ Cursor ] The cursor for the result set.
59
+ def read_with_retry_cursor(session, server_selector, view, &block)
60
+ read_with_retry(session, server_selector) do |server|
61
+ result = yield server
62
+ Cursor.new(view, result, server, session: session)
63
+ end
64
+ end
65
+
66
+ # Execute a read operation with retrying.
67
+ #
68
+ # This method performs server selection for the specified server selector
69
+ # and yields to the provided block, which should execute the initial
70
+ # query operation and return its result. The block will be passed the
71
+ # server selected for the operation. If the block raises an exception,
72
+ # and this exception corresponds to a read retryable error, and read
73
+ # retries are enabled for the client, this method will perform server
74
+ # selection again and yield to the block again (with potentially a
75
+ # different server). If the block returns successfully, the result
76
+ # of the block is returned.
32
77
  #
33
- # @param [ Mongo::Session ] session The session that the operation is being run on.
78
+ # If modern retry reads are on (which is the default), the initial read
79
+ # operation will be retried once. If legacy retry reads are on, the
80
+ # initial read operation will be retried zero or more times depending
81
+ # on the :max_read_retries client setting, the default for which is 1.
82
+ # To disable read retries, turn off modern read retries by setting
83
+ # retry_reads: false and set :max_read_retries to 0 on the client.
84
+ #
85
+ # @api private
86
+ #
87
+ # @example Execute the read.
88
+ # read_with_retry(session, server_selector) do |server|
89
+ # ...
90
+ # end
91
+ #
92
+ # @param [ Mongo::Session ] session The session that the operation is being
93
+ # run on.
94
+ # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
95
+ # selector for the operation.
34
96
  # @param [ Proc ] block The block to execute.
35
97
  #
36
98
  # @return [ Result ] The result of the operation.
37
- #
38
- # @since 2.1.0
39
- def read_with_retry(session = nil)
40
- attempt = 0
41
- begin
42
- attempt += 1
43
- yield
44
- rescue Error::SocketError, Error::SocketTimeoutError => e
45
- if attempt > cluster.max_read_retries || (session && session.in_transaction?)
46
- raise
47
- end
48
- log_retry(e)
49
- cluster.scan!(false)
50
- retry
51
- rescue Error::OperationFailure => e
52
- if cluster.sharded? && e.retryable? && !(session && session.in_transaction?)
53
- if attempt > cluster.max_read_retries
54
- raise
55
- end
56
- log_retry(e)
57
- sleep(cluster.read_retry_interval)
58
- retry
59
- else
60
- raise
99
+ def read_with_retry(session = nil, server_selector = nil, &block)
100
+ if session.nil? && server_selector.nil?
101
+ # Older versions of Mongoid call read_with_retry without arguments.
102
+ # This is already not correct in a MongoDB 3.6+ environment with
103
+ # sessions. For compatibility we emulate the legacy driver behavior
104
+ # here but upgrading Mongoid is strongly recommended.
105
+ unless $_mongo_read_with_retry_warned
106
+ $_mongo_read_with_retry_warned = true
107
+ Logger.logger.warn("Legacy read_with_retry invocation - please update the application and/or its dependencies")
61
108
  end
109
+ # Since we don't have a session, we cannot use the modern read retries.
110
+ # And we need to select a server but we don't have a server selector.
111
+ # Use PrimaryPreferred which will work as long as there is a data
112
+ # bearing node in the cluster; the block may select a different server
113
+ # which is fine.
114
+ server_selector = ServerSelector.get(mode: :primary_preferred)
115
+ legacy_read_with_retry(nil, server_selector, &block)
116
+ elsif session && session.retry_reads?
117
+ modern_read_with_retry(session, server_selector, &block)
118
+ elsif client.max_read_retries > 0
119
+ legacy_read_with_retry(session, server_selector, &block)
120
+ else
121
+ server = select_server(cluster, server_selector)
122
+ yield server
62
123
  end
63
124
  end
64
125
 
65
- # Execute a read operation with a single retry.
126
+ # Execute a read operation with a single retry on network errors.
127
+ #
128
+ # This method is used by the driver for some of the internal housekeeping
129
+ # operations. Application-requested reads should use read_with_retry
130
+ # rather than this method.
66
131
  #
67
132
  # @api private
68
133
  #
@@ -159,6 +224,52 @@ module Mongo
159
224
 
160
225
  private
161
226
 
227
+ def modern_read_with_retry(session, server_selector, &block)
228
+ attempt = 0
229
+ server = select_server(cluster, server_selector)
230
+ begin
231
+ yield server
232
+ rescue Error::SocketError, Error::SocketTimeoutError => e
233
+ if session.in_transaction?
234
+ raise
235
+ end
236
+ retry_read(e, server_selector, &block)
237
+ rescue Error::OperationFailure => e
238
+ if session.in_transaction? || !e.write_retryable?
239
+ raise
240
+ end
241
+ retry_read(e, server_selector, &block)
242
+ end
243
+ end
244
+
245
+ def legacy_read_with_retry(session, server_selector)
246
+ attempt = 0
247
+ server = select_server(cluster, server_selector)
248
+ begin
249
+ attempt += 1
250
+ yield server
251
+ rescue Error::SocketError, Error::SocketTimeoutError => e
252
+ if attempt > client.max_read_retries || (session && session.in_transaction?)
253
+ raise
254
+ end
255
+ log_retry(e, message: 'Legacy read retry')
256
+ server = select_server(cluster, server_selector)
257
+ retry
258
+ rescue Error::OperationFailure => e
259
+ if cluster.sharded? && e.retryable? && !(session && session.in_transaction?)
260
+ if attempt > client.max_read_retries
261
+ raise
262
+ end
263
+ log_retry(e, message: 'Legacy read retry')
264
+ sleep(client.read_retry_interval)
265
+ server = select_server(cluster, server_selector)
266
+ retry
267
+ else
268
+ raise
269
+ end
270
+ end
271
+ end
272
+
162
273
  def retry_write_allowed?(session, write_concern)
163
274
  unless session && session.retry_writes?
164
275
  return false
@@ -174,6 +285,27 @@ module Mongo
174
285
  end
175
286
  end
176
287
 
288
+ def retry_read(original_error, server_selector, &block)
289
+ begin
290
+ server = select_server(cluster, server_selector)
291
+ rescue
292
+ raise original_error
293
+ end
294
+
295
+ log_retry(original_error, message: 'Read retry')
296
+
297
+ begin
298
+ yield server, true
299
+ rescue Error::SocketError, Error::SocketTimeoutError => e
300
+ raise e
301
+ rescue Error::OperationFailure => e
302
+ raise original_error unless e.write_retryable?
303
+ raise e
304
+ rescue
305
+ raise original_error
306
+ end
307
+ end
308
+
177
309
  def retry_write(original_error, txn_num, &block)
178
310
  # We do not request a scan of the cluster here, because error handling
179
311
  # for the error which triggered the retry should have updated the
@@ -182,7 +314,7 @@ module Mongo
182
314
  # server unknown). Here we just need to wait for server selection.
183
315
  server = cluster.next_primary
184
316
  raise original_error unless (server.retry_writes? && txn_num)
185
- log_retry(original_error)
317
+ log_retry(original_error, message: 'Write retry')
186
318
  yield(server, txn_num, true)
187
319
  rescue Error::SocketError, Error::SocketTimeoutError => e
188
320
  raise e
@@ -203,11 +335,11 @@ module Mongo
203
335
  yield(server || cluster.next_primary)
204
336
  rescue Error::OperationFailure => e
205
337
  server = nil
206
- if attempt > Cluster::MAX_WRITE_RETRIES
338
+ if attempt > client.max_write_retries
207
339
  raise
208
340
  end
209
341
  if e.write_retryable? && !(session && session.in_transaction?)
210
- log_retry(e)
342
+ log_retry(e, message: 'Legacy write retry')
211
343
  cluster.scan!(false)
212
344
  retry
213
345
  else
@@ -216,6 +348,12 @@ module Mongo
216
348
  end
217
349
  end
218
350
 
351
+ # This is a separate method to make it possible for the test suite to
352
+ # assert that server selection is performed during retry attempts.
353
+ def select_server(cluster, server_selector)
354
+ server_selector.select_server(cluster)
355
+ end
356
+
219
357
  # Log a warning so that any application slow down is immediately obvious.
220
358
  def log_retry(e, options = nil)
221
359
  message = if options && options[:message]
@@ -61,6 +61,9 @@ module Mongo
61
61
  monitor = options.delete(:monitor)
62
62
  @options = options.freeze
63
63
  @event_listeners = event_listeners
64
+ @connection_id_gen = Class.new do
65
+ include Id
66
+ end
64
67
  @monitor = Monitor.new(address, event_listeners, monitoring,
65
68
  options.merge(app_metadata: Monitor::AppMetadata.new(cluster.options)))
66
69
  unless monitor == false
@@ -178,7 +181,13 @@ module Mongo
178
181
  #
179
182
  # @since 2.0.0
180
183
  def disconnect!(wait=false)
181
- pool.disconnect!
184
+ begin
185
+ # For backwards compatibility we disconnect/clear the pool rather
186
+ # than close it here.
187
+ pool.disconnect!
188
+ rescue Error::PoolClosedError
189
+ # If the pool was already closed, we don't need to do anything here.
190
+ end
182
191
  monitor.stop!(wait)
183
192
  @connected = false
184
193
  true
@@ -282,11 +291,7 @@ module Mongo
282
291
  # @since 2.0.0
283
292
  def pool
284
293
  @pool_lock.synchronize do
285
- @pool ||= begin
286
- ConnectionPool.new(options) do |generation|
287
- Connection.new(self, options.merge(generation: generation))
288
- end
289
- end
294
+ @pool ||= ConnectionPool.new(self, options)
290
295
  end
291
296
  end
292
297
 
@@ -315,7 +320,9 @@ module Mongo
315
320
  #
316
321
  # @since 2.1.0
317
322
  def reconnect!
318
- monitor.restart!
323
+ if options[:monitoring_io] != false
324
+ monitor.restart!
325
+ end
319
326
  @connected = true
320
327
  end
321
328
 
@@ -373,6 +380,13 @@ module Mongo
373
380
  raise
374
381
  end
375
382
 
383
+ # Whether the server supports modern read retries.
384
+ #
385
+ # @api private
386
+ def retry_reads?
387
+ !!(features.sessions_enabled? && logical_session_timeout)
388
+ end
389
+
376
390
  # Will writes sent to this server be retried.
377
391
  #
378
392
  # @example Will writes be retried.
@@ -403,6 +417,11 @@ module Mongo
403
417
  def update_description(description)
404
418
  monitor.instance_variable_set('@description', description)
405
419
  end
420
+
421
+ # @api private
422
+ def next_connection_id
423
+ @connection_id_gen.next_id
424
+ end
406
425
  end
407
426
  end
408
427
 
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'rbconfig'
16
-
17
15
  module Mongo
18
16
  class Server
19
17
  # Application metadata that is sent to the server in an ismaster command,
@@ -81,15 +81,21 @@ module Mongo
81
81
  result
82
82
  ensure
83
83
  unless success
84
- disconnect!
84
+ disconnect!(reason: :error)
85
85
  end
86
86
  end
87
87
  end
88
88
 
89
89
  def ensure_same_process!
90
90
  if pid != Process.pid
91
- disconnect!
91
+ # When we reconnect here, CMAP events won't be correctly sent
92
+ # since the CMAP spec does not permit a connection to be disconnected
93
+ # and then reconnected
94
+ log_warn("Detected PID change - Mongo client should have been reconnected (old pid #{pid}, new pid #{Process.pid}")
95
+ disconnect!(reason: :stale)
96
+ @closed = false
92
97
  @pid = Process.pid
98
+ connect!
93
99
  end
94
100
  end
95
101
  end
@@ -89,6 +89,7 @@ module Mongo
89
89
  #
90
90
  # @since 2.0.0
91
91
  def initialize(server, options = {})
92
+ @id = server.next_connection_id
92
93
  @monitoring = server.monitoring
93
94
  @options = options.freeze
94
95
  @server = server
@@ -97,13 +98,23 @@ module Mongo
97
98
  @last_checkin = nil
98
99
  @auth_mechanism = nil
99
100
  @pid = Process.pid
101
+
102
+ publish_cmap_event(
103
+ Monitoring::Event::Cmap::ConnectionCreated.new(address, id)
104
+ )
100
105
  end
101
106
 
102
- # The last time the connection was checked back into a pool.
107
+ # @return [ Time ] The last time the connection was checked back into a pool.
103
108
  #
104
109
  # @since 2.5.0
105
110
  attr_reader :last_checkin
106
111
 
112
+ # @return [ Integer ] The ID for the connection. This will be unique
113
+ # across connections to the same server object.
114
+ #
115
+ # @since 2.9.0
116
+ attr_reader :id
117
+
107
118
  # Connection pool generation from which this connection was created.
108
119
  # May be nil.
109
120
  #
@@ -113,6 +124,18 @@ module Mongo
113
124
  options[:generation]
114
125
  end
115
126
 
127
+ # Whether the connection was closed.
128
+ #
129
+ # Closed connections should no longer be used. Instead obtain a new
130
+ # connection from the connection pool.
131
+ #
132
+ # @return [ true | false ] Whether connection was closed.
133
+ #
134
+ # @since 2.9.0
135
+ def closed?
136
+ !!@closed
137
+ end
138
+
116
139
  # Establishes a network connection to the target address.
117
140
  #
118
141
  # If the connection is already established, this method does nothing.
@@ -120,44 +143,91 @@ module Mongo
120
143
  # @example Connect to the host.
121
144
  # connection.connect!
122
145
  #
123
- # @note This method mutates the connection class by setting a socket if
146
+ # @note This method mutates the connection object by setting a socket if
124
147
  # one previously did not exist.
125
148
  #
126
149
  # @return [ true ] If the connection succeeded.
127
150
  #
128
151
  # @since 2.0.0
129
152
  def connect!
153
+ if closed?
154
+ if Lint.enabled?
155
+ raise Error::LintError, "Reconnecting closed connections is no longer supported"
156
+ else
157
+ log_warn("Reconnecting closed connections is deprecated (for #{address})")
158
+ end
159
+ end
160
+
130
161
  unless @socket
131
- socket = address.socket(socket_timeout, ssl_options,
132
- connect_timeout: address.connect_timeout)
133
- handshake!(socket)
134
- pending_connection = PendingConnection.new(socket, @server, monitoring, options)
135
- authenticate!(pending_connection)
136
162
  # When @socket is assigned, the socket should have handshaken and
137
163
  # authenticated and be usable.
138
- @socket = socket
164
+ @socket = do_connect
165
+
166
+ publish_cmap_event(
167
+ Monitoring::Event::Cmap::ConnectionReady.new(address, id)
168
+ )
169
+
170
+ @close_event_published = false
139
171
  end
140
172
  true
141
173
  end
142
174
 
175
+ # Separate method to permit easier mocking in the test suite.
176
+ def do_connect
177
+ socket = address.socket(socket_timeout, ssl_options,
178
+ connect_timeout: address.connect_timeout)
179
+ handshake!(socket)
180
+ pending_connection = PendingConnection.new(socket, @server, monitoring, options)
181
+ authenticate!(pending_connection)
182
+ socket
183
+ end
184
+ private :do_connect
185
+
143
186
  # Disconnect the connection.
144
187
  #
145
- # @example Disconnect from the host.
146
- # connection.disconnect!
188
+ # @note Once a connection is disconnected, it should no longer be used.
189
+ # A new connection should be obtained from the connection pool which
190
+ # will either return a ready connection or create a new connection.
191
+ # If linting is enabled, reusing a disconnected connection will raise
192
+ # Error::LintError. If linting is not enabled, a warning will be logged.
193
+ #
194
+ # @note This method mutates the connection object by setting the socket
195
+ # to nil if the closing succeeded.
147
196
  #
148
- # @note This method mutates the connection by setting the socket to nil
149
- # if the closing succeeded.
197
+ # @option options [ Symbol ] :reason The reason why the connection is
198
+ # being closed.
150
199
  #
151
200
  # @return [ true ] If the disconnect succeeded.
152
201
  #
153
202
  # @since 2.0.0
154
- def disconnect!
203
+ def disconnect!(options = nil)
204
+ # Note: @closed may be true here but we also may have a socket.
205
+ # Check the socket and not @closed flag.
155
206
  @auth_mechanism = nil
156
207
  @last_checkin = nil
157
208
  if socket
158
209
  socket.close
159
210
  @socket = nil
160
211
  end
212
+ @closed = true
213
+
214
+ # To satisfy CMAP spec tests, publish close events even if the
215
+ # socket was never connected (and thus the ready event was never
216
+ # published). But track whether we published close event and do not
217
+ # publish it multiple times, unless the socket was reconnected -
218
+ # in that case publish the close event once per socket close.
219
+ unless @close_event_published
220
+ reason = options && options[:reason]
221
+ publish_cmap_event(
222
+ Monitoring::Event::Cmap::ConnectionClosed.new(
223
+ address,
224
+ id,
225
+ reason,
226
+ ),
227
+ )
228
+ @close_event_published = true
229
+ end
230
+
161
231
  true
162
232
  end
163
233