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
@@ -96,7 +96,7 @@ module Mongo
96
96
  buffer = serialize(message)
97
97
  ensure_connected do |socket|
98
98
  operation_id = Monitoring.next_operation_id
99
- command_started(address, operation_id, message.payload)
99
+ command_started(address, operation_id, message.payload, socket.object_id)
100
100
  start = Time.now
101
101
  result = nil
102
102
  begin
@@ -12,73 +12,346 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'mongo/server/connection_pool/queue'
16
-
17
15
  module Mongo
18
16
  class Server
19
17
 
20
18
  # Represents a connection pool for server connections.
21
19
  #
22
- # @since 2.0.0
20
+ # @since 2.0.0, largely rewritten in 2.9.0
23
21
  class ConnectionPool
24
22
  include Loggable
23
+ include Monitoring::Publishable
25
24
  extend Forwardable
26
25
 
27
- # Create the new connection pool.
26
+ # The default max size for the connection pool.
28
27
  #
29
- # @example Create the new connection pool.
30
- # Pool.new(wait_queue_timeout: 0.5) do
31
- # Connection.new
32
- # end
28
+ # @since 2.9.0
29
+ DEFAULT_MAX_SIZE = 5.freeze
30
+
31
+ # The default min size for the connection pool.
33
32
  #
34
- # @note A block must be passed to set up the connections on initialization.
33
+ # @since 2.9.0
34
+ DEFAULT_MIN_SIZE = 0.freeze
35
+
36
+ # The default timeout, in seconds, to wait for a connection.
37
+ #
38
+ # @since 2.9.0
39
+ DEFAULT_WAIT_TIMEOUT = 1.freeze
40
+
41
+ # Create the new connection pool.
35
42
  #
43
+ # @param [ Server ] server The server which this connection pool is for.
36
44
  # @param [ Hash ] options The connection pool options.
37
45
  #
38
- # @option options [ Integer ] :max_pool_size The maximum pool size.
39
- # @option options [ Integer ] :min_pool_size The minimum pool size.
40
- # @option options [ Float ] :wait_queue_timeout The time to wait, in
46
+ # @option options [ Integer ] :max_size The maximum pool size.
47
+ # @option options [ Integer ] :max_pool_size Deprecated.
48
+ # The maximum pool size. If max_size is also given, max_size and
49
+ # max_pool_size must be identical.
50
+ # @option options [ Integer ] :min_size The minimum pool size.
51
+ # @option options [ Integer ] :min_pool_size Deprecated.
52
+ # The minimum pool size. If min_size is also given, min_size and
53
+ # min_pool_size must be identical.
54
+ # @option options [ Float ] :wait_timeout The time to wait, in
41
55
  # seconds, for a free connection.
56
+ # @option options [ Float ] :wait_queue_timeout Deprecated.
57
+ # Alias for :wait_timeout. If both wait_timeout and wait_queue_timeout
58
+ # are given, their values must be identical.
59
+ # @option options [ Float ] :max_idle_time The time, in seconds,
60
+ # after which idle connections should be closed by the pool.
42
61
  #
43
- # @since 2.0.0
44
- def initialize(options = {}, &block)
45
- @options = options.dup.freeze
46
- @queue = queue = Queue.new(@options, &block)
62
+ # @since 2.0.0, API changed in 2.9.0
63
+ def initialize(server, options = {})
64
+ unless server.is_a?(Server)
65
+ raise ArgumentError, 'First argument must be a Server instance'
66
+ end
67
+ options = options.dup
68
+ if options[:min_size] && options[:min_pool_size] && options[:min_size] != options[:min_pool_size]
69
+ raise ArgumentError, "Min size #{options[:min_size]} is not identical to min pool size #{options[:min_pool_size]}"
70
+ end
71
+ if options[:max_size] && options[:max_pool_size] && options[:max_size] != options[:max_pool_size]
72
+ raise ArgumentError, "Max size #{options[:max_size]} is not identical to max pool size #{options[:max_pool_size]}"
73
+ end
74
+ if options[:wait_timeout] && options[:wait_queue_timeout] && options[:wait_timeout] != options[:wait_queue_timeout]
75
+ raise ArgumentError, "Wait timeout #{options[:wait_timeout]} is not identical to wait queue timeout #{options[:wait_queue_timeout]}"
76
+ end
77
+ options[:min_size] ||= options[:min_pool_size]
78
+ options.delete(:min_pool_size)
79
+ options[:max_size] ||= options[:max_pool_size]
80
+ options.delete(:max_pool_size)
81
+ if options[:min_size] && options[:max_size] &&
82
+ options[:min_size] > options[:max_size]
83
+ then
84
+ raise ArgumentError, "Cannot have min size #{options[:min_size]} exceed max size #{options[:max_size]}"
85
+ end
86
+ if options[:wait_queue_timeout]
87
+ options[:wait_timeout] ||= options[:wait_queue_timeout]
88
+ end
89
+ options.delete(:wait_queue_timeout)
90
+
91
+ @server = server
92
+ @options = options.freeze
93
+
94
+ @generation = 1
95
+ @closed = false
96
+
97
+ # A connection owned by this pool should be either in the
98
+ # available connections array (which is used as a stack)
99
+ # or in the checked out connections set.
100
+ @available_connections = available_connections = []
101
+ @checked_out_connections = Set.new
102
+
103
+ # Mutex used for synchronizing access to @available_connections and
104
+ # @checked_out_connections. The pool object is thread-safe, thus
105
+ # all methods that retrieve or modify instance variables generally
106
+ # must do so under this lock.
107
+ @lock = Mutex.new
108
+
109
+ # Condition variable broadcast when a connection is added to
110
+ # @available_connections, to wake up any threads waiting for an
111
+ # available connection when pool is at max size
112
+ @available_semaphore = Semaphore.new
47
113
 
48
114
  finalizer = proc do
49
- queue.disconnect!
115
+ available_connections.each do |connection|
116
+ connection.disconnect!(reason: :pool_closed)
117
+ end
118
+ available_connections.clear
119
+ # Finalizer does not close checked out connections.
120
+ # Those would have to be garbage collected on their own
121
+ # and that should close them.
50
122
  end
51
123
  ObjectSpace.define_finalizer(self, finalizer)
124
+
125
+ publish_cmap_event(
126
+ Monitoring::Event::Cmap::PoolCreated.new(@server.address, options)
127
+ )
52
128
  end
53
129
 
54
130
  # @return [ Hash ] options The pool options.
55
131
  attr_reader :options
56
132
 
57
- def_delegators :queue, :close_stale_sockets!
133
+ # Get the maximum size of the connection pool.
134
+ #
135
+ # @return [ Integer ] The maximum size of the connection pool.
136
+ #
137
+ # @since 2.9.0
138
+ def max_size
139
+ @max_size ||= options[:max_size] || [DEFAULT_MAX_SIZE, min_size].max
140
+ end
141
+
142
+ # Get the minimum size of the connection pool.
143
+ #
144
+ # @return [ Integer ] The minimum size of the connection pool.
145
+ #
146
+ # @since 2.9.0
147
+ def min_size
148
+ @min_size ||= options[:min_size] || DEFAULT_MIN_SIZE
149
+ end
58
150
 
59
- # Check a connection back into the pool. Will pull the connection from a
60
- # thread local stack that should contain it after it was checked out.
151
+ # The time to wait, in seconds, for a connection to become available.
61
152
  #
62
- # @example Checkin the thread's connection to the pool.
63
- # pool.checkin
153
+ # @return [ Float ] The queue wait timeout.
64
154
  #
65
- # @since 2.0.0
66
- def checkin(connection)
67
- queue.enqueue(connection)
155
+ # @since 2.9.0
156
+ def wait_timeout
157
+ @wait_timeout ||= options[:wait_timeout] || DEFAULT_WAIT_TIMEOUT
68
158
  end
69
159
 
70
- # Check a connection out from the pool. If a connection exists on the same
71
- # thread it will get that connection, otherwise it will dequeue a
72
- # connection from the queue and pin it to this thread.
160
+ # The maximum seconds a socket can remain idle since it has been
161
+ # checked in to the pool, if set.
73
162
  #
74
- # @example Check a connection out from the pool.
75
- # pool.checkout
163
+ # @return [ Float | nil ] The max socket idle time in seconds.
164
+ #
165
+ # @since 2.9.0
166
+ def max_idle_time
167
+ @max_idle_time ||= options[:max_idle_time]
168
+ end
169
+
170
+ # @return [ Integer ] generation Generation of connections currently
171
+ # being used by the queue.
172
+ #
173
+ # @since 2.9.0
174
+ # @api private
175
+ attr_reader :generation
176
+
177
+ # Size of the connection pool.
178
+ #
179
+ # Includes available and checked out connections.
180
+ #
181
+ # @return [ Integer ] Size of the connection pool.
182
+ #
183
+ # @since 2.9.0
184
+ def size
185
+ raise_if_closed!
186
+
187
+ @lock.synchronize do
188
+ unsynchronized_size
189
+ end
190
+ end
191
+
192
+ # Returns the size of the connection pool without acquiring the lock.
193
+ # This method should only be used by other pool methods when they are
194
+ # already holding the lock as Ruby does not allow a thread holding a
195
+ # lock to acquire this lock again.
196
+ def unsynchronized_size
197
+ @available_connections.length + @checked_out_connections.size
198
+ end
199
+ private :unsynchronized_size
200
+
201
+ # Number of available connections in the pool.
202
+ #
203
+ # @return [ Integer ] Number of available connections.
204
+ #
205
+ # @since 2.9.0
206
+ def available_count
207
+ raise_if_closed!
208
+
209
+ @lock.synchronize do
210
+ @available_connections.length
211
+ end
212
+ end
213
+
214
+ # Whether the pool has been closed.
215
+ #
216
+ # @return [ true | false ] Whether the pool is closed.
217
+ #
218
+ # @since 2.9.0
219
+ def closed?
220
+ !!@closed
221
+ end
222
+
223
+ # @since 2.9.0
224
+ def_delegators :@server, :monitoring
225
+
226
+ # Checks a connection out of the pool.
227
+ #
228
+ # If there are active connections in the pool, the most recently used
229
+ # connection is returned. Otherwise if the connection pool size is less
230
+ # than the max size, creates a new connection and returns it. Otherwise
231
+ # waits up to the wait timeout and raises Timeout::Error if there are
232
+ # still no active connections and the pool is at max size.
233
+ #
234
+ # The returned connection counts toward the pool's max size. When the
235
+ # caller is finished using the connection, the connection should be
236
+ # checked back in via the check_in method.
76
237
  #
77
238
  # @return [ Mongo::Server::Connection ] The checked out connection.
239
+ # @raise [ Timeout::Error ] If the connection pool is at maximum size
240
+ # and remains so for longer than the wait timeout.
78
241
  #
79
- # @since 2.0.0
80
- def checkout
81
- queue.dequeue
242
+ # @since 2.9.0
243
+ def check_out
244
+ raise_if_closed!
245
+
246
+ publish_cmap_event(
247
+ Monitoring::Event::Cmap::ConnectionCheckOutStarted.new(@server.address)
248
+ )
249
+
250
+ deadline = Time.now + wait_timeout
251
+ connection = nil
252
+ # It seems that synchronize sets up its own loop, thus a simple break
253
+ # is insufficient to break the outer loop
254
+ catch(:done) do
255
+ loop do
256
+ # Lock must be taken on each iteration, rather for the method
257
+ # overall, otherwise other threads will not be able to check in
258
+ # a connection while this thread is waiting for one.
259
+ @lock.synchronize do
260
+ until @available_connections.empty?
261
+ connection = @available_connections.pop
262
+
263
+ if connection.generation != generation
264
+ # Stale connections should be disconnected in the clear
265
+ # method, but if any don't, check again here
266
+ connection.disconnect!(reason: :stale)
267
+ next
268
+ end
269
+
270
+ if max_idle_time && connection.last_checkin &&
271
+ Time.now - connection.last_checkin > max_idle_time
272
+ then
273
+ connection.disconnect!(reason: :idle)
274
+ next
275
+ end
276
+
277
+ throw(:done)
278
+ end
279
+
280
+ # Ruby does not allow a thread to lock a mutex which it already
281
+ # holds.
282
+ if unsynchronized_size < max_size
283
+ # This does not currently connect the socket and handshake,
284
+ # but if it did, it would be performing i/o under our lock,
285
+ # which is bad. Fix in the future.
286
+ connection = create_connection
287
+ throw(:done)
288
+ end
289
+ end
290
+
291
+ wait = deadline - Time.now
292
+ if wait <= 0
293
+ publish_cmap_event(
294
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
295
+ @server.address,
296
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::TIMEOUT,
297
+ ),
298
+ )
299
+ raise Error::ConnectionCheckOutTimeout.new(@server.address, wait_timeout)
300
+ end
301
+ @available_semaphore.wait(wait)
302
+ end
303
+ end
304
+
305
+ @checked_out_connections << connection
306
+ publish_cmap_event(
307
+ Monitoring::Event::Cmap::ConnectionCheckedOut.new(@server.address, connection.id),
308
+ )
309
+ connection
310
+ end
311
+
312
+ # Check a connection back into the pool.
313
+ #
314
+ # The connection must have been previously created by this pool.
315
+ #
316
+ # @param [ Mongo::Server::Connection ] connection The connection.
317
+ #
318
+ # @since 2.9.0
319
+ def check_in(connection)
320
+ @lock.synchronize do
321
+ unless @checked_out_connections.include?(connection)
322
+ raise ArgumentError, "Trying to check in a connection which is not currently checked out by this pool: #{connection}"
323
+ end
324
+
325
+ @checked_out_connections.delete(connection)
326
+
327
+ # Note: if an event handler raises, resource will not be signaled.
328
+ # This means threads waiting for a connection to free up when
329
+ # the pool is at max size may time out.
330
+ # Threads that begin waiting after this method completes (with
331
+ # the exception) should be fine.
332
+ publish_cmap_event(
333
+ Monitoring::Event::Cmap::ConnectionCheckedIn.new(@server.address, connection.id)
334
+ )
335
+
336
+ if closed?
337
+ connection.disconnect!(reason: :pool_closed)
338
+ return
339
+ end
340
+
341
+ if connection.closed?
342
+ # Connection was closed - for example, because it experienced
343
+ # a network error. Nothing else needs to be done here.
344
+ elsif connection.generation != @generation
345
+ connection.disconnect!(reason: :stale)
346
+ else
347
+ connection.record_checkin!
348
+ @available_connections << connection
349
+
350
+ # Wake up only one thread waiting for an available connection,
351
+ # since only one connection was checked in.
352
+ @available_semaphore.signal
353
+ end
354
+ end
82
355
  end
83
356
 
84
357
  # Closes all idle connections in the pool and schedules currently checked
@@ -86,14 +359,75 @@ module Mongo
86
359
  # The pool remains operational and can create new connections when
87
360
  # requested.
88
361
  #
89
- # @example Disconnect the connection pool.
90
- # pool.disconnect!
362
+ # @option options [ true | false ] :lazy If true, do not close any of
363
+ # the idle connections and instead let them be closed during a
364
+ # subsequent check out operation.
91
365
  #
92
366
  # @return [ true ] true.
93
367
  #
94
368
  # @since 2.1.0
95
- def disconnect!
96
- queue.disconnect!
369
+ def clear(options = nil)
370
+ raise_if_closed!
371
+
372
+ @lock.synchronize do
373
+ @generation += 1
374
+
375
+ publish_cmap_event(
376
+ Monitoring::Event::Cmap::PoolCleared.new(@server.address)
377
+ )
378
+
379
+ unless options && options[:lazy]
380
+ until @available_connections.empty?
381
+ connection = @available_connections.pop
382
+ connection.disconnect!(reason: :stale)
383
+ end
384
+ end
385
+ end
386
+
387
+ true
388
+ end
389
+
390
+ # @since 2.1.0
391
+ # @deprecated
392
+ alias :disconnect! :clear
393
+
394
+ # Marks the pool closed, closes all idle connections in the pool and
395
+ # schedules currently checked out connections to be closed when they are
396
+ # checked back into the pool. If force option is true, checked out
397
+ # connections are also closed. Attempts to use the pool after it is closed
398
+ # will raise Error::PoolClosedError.
399
+ #
400
+ # @option options [ true | false ] :force Also close all checked out
401
+ # connections.
402
+ #
403
+ # @return [ true ] true.
404
+ #
405
+ # @since 2.9.0
406
+ def close(options = nil)
407
+ return if closed?
408
+
409
+ @lock.synchronize do
410
+ until @available_connections.empty?
411
+ connection = @available_connections.pop
412
+ connection.disconnect!(reason: :pool_closed)
413
+ end
414
+
415
+ if options && options[:force]
416
+ until @checked_out_connections.empty?
417
+ connection = @checked_out_connections.take(1).first
418
+ connection.disconnect!(reason: :pool_closed)
419
+ @checked_out_connections.delete(connection)
420
+ end
421
+ end
422
+ end
423
+
424
+ @closed = true
425
+
426
+ publish_cmap_event(
427
+ Monitoring::Event::Cmap::PoolClosed.new(@server.address)
428
+ )
429
+
430
+ true
97
431
  end
98
432
 
99
433
  # Get a pretty printed string inspection for the pool.
@@ -105,10 +439,16 @@ module Mongo
105
439
  #
106
440
  # @since 2.0.0
107
441
  def inspect
108
- "#<Mongo::Server::ConnectionPool:0x#{object_id} queue=#{queue.inspect}>"
442
+ if closed?
443
+ "#<Mongo::Server::ConnectionPool:0x#{object_id} min_size=#{min_size} max_size=#{max_size} " +
444
+ "wait_timeout=#{wait_timeout} closed>"
445
+ else
446
+ "#<Mongo::Server::ConnectionPool:0x#{object_id} min_size=#{min_size} max_size=#{max_size} " +
447
+ "wait_timeout=#{wait_timeout} current_size=#{size} available=#{available_count}>"
448
+ end
109
449
  end
110
450
 
111
- # Yield the block to a connection, while handling checkin/checkout logic.
451
+ # Yield the block to a connection, while handling check in/check out logic.
112
452
  #
113
453
  # @example Execute with a connection.
114
454
  # pool.with_connection do |connection|
@@ -119,15 +459,71 @@ module Mongo
119
459
  #
120
460
  # @since 2.0.0
121
461
  def with_connection
122
- connection = checkout
462
+ raise_if_closed!
463
+
464
+ connection = check_out
123
465
  yield(connection)
124
466
  ensure
125
- checkin(connection) if connection
467
+ if connection
468
+ check_in(connection)
469
+ end
470
+ end
471
+
472
+ # Close sockets that have been open for longer than the max idle time,
473
+ # if the option is set.
474
+ #
475
+ # @since 2.5.0
476
+ def close_idle_sockets
477
+ return if closed?
478
+ return unless max_idle_time
479
+
480
+ @lock.synchronize do
481
+ i = 0
482
+ while i < @available_connections.length
483
+ connection = @available_connections[i]
484
+ if last_checkin = connection.last_checkin
485
+ if (Time.now - last_checkin) > max_idle_time
486
+ connection.disconnect!(reason: :idle)
487
+ @available_connections.delete_at(i)
488
+ next
489
+ end
490
+ end
491
+ i += 1
492
+ end
493
+ end
494
+ end
495
+
496
+ # Creates up to the min size connections.
497
+ #
498
+ # Used by the spec test runner.
499
+ #
500
+ # @api private
501
+ def populate
502
+ while size < min_size
503
+ @available_connections << create_connection
504
+ end
126
505
  end
127
506
 
128
- protected
507
+ private
508
+
509
+ def create_connection
510
+ connection = Connection.new(@server, options.merge(generation: generation))
511
+ # CMAP spec requires connections to be returned from the pool
512
+ # fully established.
513
+ #connection.connect!
514
+ connection
515
+ end
129
516
 
130
- attr_reader :queue
517
+ # Asserts that the pool has not been closed.
518
+ #
519
+ # @raise [ Error::PoolClosedError ] If the pool has been closed.
520
+ #
521
+ # @since 2.9.0
522
+ def raise_if_closed!
523
+ if closed?
524
+ raise Error::PoolClosedError.new(@server.address)
525
+ end
526
+ end
131
527
  end
132
528
  end
133
529
  end