mongo 2.8.0 → 2.9.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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