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
@@ -17,7 +17,7 @@ describe Mongo::Operation::Insert do
17
17
  end
18
18
 
19
19
  let(:write_concern) do
20
- Mongo::WriteConcern.get(SpecConfig.instance.write_concern)
20
+ Mongo::WriteConcern.get(w: :majority)
21
21
  end
22
22
 
23
23
  let(:spec) do
@@ -15,7 +15,7 @@ describe Mongo::Operation::Insert::Command do
15
15
  end
16
16
 
17
17
  let(:write_concern) do
18
- Mongo::WriteConcern.get(SpecConfig.instance.write_concern)
18
+ Mongo::WriteConcern.get(w: :majority)
19
19
  end
20
20
 
21
21
  let(:op) { described_class.new(spec) }
@@ -15,7 +15,7 @@ describe Mongo::Operation::Insert::OpMsg do
15
15
  end
16
16
 
17
17
  let(:write_concern) do
18
- Mongo::WriteConcern.get(SpecConfig.instance.write_concern)
18
+ Mongo::WriteConcern.get(w: :majority)
19
19
  end
20
20
 
21
21
  let(:op) { described_class.new(spec) }
@@ -19,7 +19,7 @@ describe Mongo::Operation::Update do
19
19
  end
20
20
 
21
21
  let(:write_concern) do
22
- Mongo::WriteConcern.get(SpecConfig.instance.write_concern)
22
+ Mongo::WriteConcern.get(w: :majority)
23
23
  end
24
24
 
25
25
  let(:op) do
@@ -8,7 +8,7 @@ describe Mongo::Operation::Update::Command do
8
8
  :upsert => false }] }
9
9
 
10
10
  let(:write_concern) do
11
- Mongo::WriteConcern.get(SpecConfig.instance.write_concern)
11
+ Mongo::WriteConcern.get(w: :majority)
12
12
  end
13
13
  let(:session) { nil }
14
14
  let(:spec) do
@@ -54,7 +54,7 @@ describe Mongo::Operation::Update::Command do
54
54
  { :updates => other_updates,
55
55
  :db_name => SpecConfig.instance.test_db,
56
56
  :coll_name => TEST_COLL,
57
- :write_concern => Mongo::WriteConcern.get(SpecConfig.instance.write_concern),
57
+ :write_concern => Mongo::WriteConcern.get(w: :majority),
58
58
  :ordered => true
59
59
  }
60
60
  end
@@ -8,7 +8,7 @@ describe Mongo::Operation::Update::OpMsg do
8
8
  :upsert => false }] }
9
9
 
10
10
  let(:write_concern) do
11
- Mongo::WriteConcern.get(SpecConfig.instance.write_concern)
11
+ Mongo::WriteConcern.get(w: :majority)
12
12
  end
13
13
  let(:session) { nil }
14
14
  let(:spec) do
@@ -54,7 +54,7 @@ describe Mongo::Operation::Update::OpMsg do
54
54
  { :updates => other_updates,
55
55
  :db_name => SpecConfig.instance.test_db,
56
56
  :coll_name => TEST_COLL,
57
- :write_concern => Mongo::WriteConcern.get(SpecConfig.instance.write_concern),
57
+ :write_concern => Mongo::WriteConcern.get(w: :majority),
58
58
  :ordered => true
59
59
  }
60
60
  end
@@ -58,6 +58,17 @@ describe Mongo::Protocol::Msg do
58
58
  end
59
59
  end
60
60
  end
61
+
62
+ context 'with user-provided and driver-generated keys in global_args' do
63
+ let(:global_args) do
64
+ { 'ping' => 1, 'lsid' => '__lsid__', 'a' => 'b', '$clusterTime' => '__ct__',
65
+ 'signature' => '__signature__', 'd' => 'f'}
66
+ end
67
+
68
+ it 'reorders global_args for better logging' do
69
+ expect(message.payload[:command].keys).to eq(%w(ping a d lsid $clusterTime signature))
70
+ end
71
+ end
61
72
  end
62
73
 
63
74
  describe '#==' do
@@ -3,23 +3,31 @@ require 'spec_helper'
3
3
  class RetryableTestConsumer
4
4
  include Mongo::Retryable
5
5
 
6
+ attr_reader :client
6
7
  attr_reader :cluster
7
8
  attr_reader :operation
8
9
 
9
- def initialize(operation, cluster)
10
+ def initialize(operation, cluster, client)
10
11
  @operation = operation
11
12
  @cluster = cluster
13
+ @client = client
12
14
  end
13
15
 
14
16
  def max_read_retries
15
- cluster.max_read_retries
17
+ client.max_read_retries
16
18
  end
17
19
 
18
20
  def read_retry_interval
19
- cluster.read_retry_interval
21
+ client.read_retry_interval
20
22
  end
21
23
 
22
24
  def read
25
+ read_with_retry(nil, Mongo::ServerSelector.get(mode: :primary)) do
26
+ operation.execute
27
+ end
28
+ end
29
+
30
+ def read_legacy
23
31
  read_with_retry do
24
32
  operation.execute
25
33
  end
@@ -81,8 +89,22 @@ describe Mongo::Retryable do
81
89
 
82
90
  let(:server) { double('server') }
83
91
 
92
+ let(:max_read_retries) { 1 }
93
+ let(:max_write_retries) { 1 }
94
+
84
95
  let(:cluster) do
85
- double('cluster', next_primary: server)
96
+ double('cluster', next_primary: server).tap do |cluster|
97
+ allow(cluster).to receive(:replica_set?).and_return(true)
98
+ allow(cluster).to receive(:addresses).and_return(['x'])
99
+ end
100
+ end
101
+
102
+ let(:client) do
103
+ double('client').tap do |client|
104
+ allow(client).to receive(:cluster).and_return(cluster)
105
+ allow(client).to receive(:max_read_retries).and_return(max_read_retries)
106
+ allow(client).to receive(:max_write_retries).and_return(max_write_retries)
107
+ end
86
108
  end
87
109
 
88
110
  let(:server_selector) do
@@ -90,10 +112,15 @@ describe Mongo::Retryable do
90
112
  end
91
113
 
92
114
  let(:retryable) do
93
- LegacyRetryableTestConsumer.new(operation, cluster)
115
+ LegacyRetryableTestConsumer.new(operation, cluster, client)
94
116
  end
95
117
 
96
- describe '#read_with_retry' do
118
+ before do
119
+ # Retryable reads perform server selection
120
+ allow_any_instance_of(Mongo::ServerSelector::Primary).to receive(:select_server).and_return(server)
121
+ end
122
+
123
+ shared_examples_for 'reads with retries' do
97
124
 
98
125
  context 'when no exception occurs' do
99
126
 
@@ -102,12 +129,12 @@ describe Mongo::Retryable do
102
129
  end
103
130
 
104
131
  it 'executes the operation once' do
105
- expect(retryable.read).to be true
132
+ expect(read_operation).to be true
106
133
  end
107
134
  end
108
135
 
109
136
  context 'when ending_transaction is true' do
110
- let(:retryable) { RetryableTestConsumer.new(operation, cluster) }
137
+ let(:retryable) { RetryableTestConsumer.new(operation, cluster, client) }
111
138
 
112
139
  it 'raises ArgumentError' do
113
140
  expect do
@@ -119,28 +146,28 @@ describe Mongo::Retryable do
119
146
  context 'when a socket error occurs' do
120
147
 
121
148
  before do
149
+ expect(retryable).to receive(:select_server).ordered
122
150
  expect(operation).to receive(:execute).and_raise(Mongo::Error::SocketError).ordered
123
- expect(cluster).to receive(:max_read_retries).and_return(1).ordered
124
- expect(cluster).to receive(:scan!).and_return(true).ordered
151
+ expect(retryable).to receive(:select_server).ordered
125
152
  expect(operation).to receive(:execute).and_return(true).ordered
126
153
  end
127
154
 
128
155
  it 'executes the operation twice' do
129
- expect(retryable.read).to be true
156
+ expect(read_operation).to be true
130
157
  end
131
158
  end
132
159
 
133
160
  context 'when a socket timeout error occurs' do
134
161
 
135
162
  before do
163
+ expect(retryable).to receive(:select_server).ordered
136
164
  expect(operation).to receive(:execute).and_raise(Mongo::Error::SocketTimeoutError).ordered
137
- expect(cluster).to receive(:max_read_retries).and_return(1).ordered
138
- expect(cluster).to receive(:scan!).and_return(true).ordered
165
+ expect(retryable).to receive(:select_server).ordered
139
166
  expect(operation).to receive(:execute).and_return(true).ordered
140
167
  end
141
168
 
142
169
  it 'executes the operation twice' do
143
- expect(retryable.read).to be true
170
+ expect(read_operation).to be true
144
171
  end
145
172
  end
146
173
 
@@ -155,7 +182,7 @@ describe Mongo::Retryable do
155
182
 
156
183
  it 'raises an exception' do
157
184
  expect {
158
- retryable.read
185
+ read_operation
159
186
  }.to raise_error(Mongo::Error::OperationFailure)
160
187
  end
161
188
  end
@@ -175,7 +202,7 @@ describe Mongo::Retryable do
175
202
 
176
203
  it 'raises the exception' do
177
204
  expect {
178
- retryable.read
205
+ read_operation
179
206
  }.to raise_error(Mongo::Error::OperationFailure)
180
207
  end
181
208
  end
@@ -189,34 +216,39 @@ describe Mongo::Retryable do
189
216
  context 'when the retry succeeds' do
190
217
 
191
218
  before do
219
+ expect(retryable).to receive(:select_server).ordered
192
220
  expect(operation).to receive(:execute).and_raise(error).ordered
193
221
  expect(cluster).to receive(:sharded?).and_return(true)
194
- expect(cluster).to receive(:max_read_retries).and_return(1).ordered
195
- expect(cluster).to receive(:read_retry_interval).and_return(0.1).ordered
222
+ expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
223
+ expect(retryable).to receive(:select_server).ordered
196
224
  expect(operation).to receive(:execute).and_return(true).ordered
197
225
  end
198
226
 
199
227
  it 'returns the result' do
200
- expect(retryable.read).to be true
228
+ expect(read_operation).to be true
201
229
  end
202
230
  end
203
231
 
204
232
  context 'when the retry fails once and then succeeds' do
233
+ let(:max_read_retries) { 2 }
205
234
 
206
235
  before do
236
+ expect(retryable).to receive(:select_server).ordered
207
237
  expect(operation).to receive(:execute).and_raise(error).ordered
238
+
208
239
  expect(cluster).to receive(:sharded?).and_return(true)
209
- expect(cluster).to receive(:max_read_retries).and_return(2).ordered
210
- expect(cluster).to receive(:read_retry_interval).and_return(0.1).ordered
240
+ expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
241
+ expect(retryable).to receive(:select_server).ordered
211
242
  expect(operation).to receive(:execute).and_raise(error).ordered
243
+
212
244
  expect(cluster).to receive(:sharded?).and_return(true)
213
- expect(cluster).to receive(:max_read_retries).and_return(2).ordered
214
- expect(cluster).to receive(:read_retry_interval).and_return(0.1).ordered
245
+ expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
246
+ expect(retryable).to receive(:select_server).ordered
215
247
  expect(operation).to receive(:execute).and_return(true).ordered
216
248
  end
217
249
 
218
250
  it 'returns the result' do
219
- expect(retryable.read).to be true
251
+ expect(read_operation).to be true
220
252
  end
221
253
  end
222
254
  end
@@ -224,6 +256,27 @@ describe Mongo::Retryable do
224
256
  end
225
257
  end
226
258
 
259
+ describe '#read_with_retry' do
260
+ let(:read_operation) do
261
+ retryable.read
262
+ end
263
+
264
+ it_behaves_like 'reads with retries'
265
+
266
+ context 'zero argument legacy invocation' do
267
+
268
+ before do
269
+ allow_any_instance_of(Mongo::ServerSelector::PrimaryPreferred).to receive(:select_server).and_return(server)
270
+ end
271
+
272
+ let(:read_operation) do
273
+ retryable.read_legacy
274
+ end
275
+
276
+ it_behaves_like 'reads with retries'
277
+ end
278
+ end
279
+
227
280
  describe '#retry_write_allowed?' do
228
281
  let(:retryable) { RetryableHost.new }
229
282
 
@@ -413,7 +466,7 @@ describe Mongo::Retryable do
413
466
  describe '#write_with_retry - modern' do
414
467
 
415
468
  let(:retryable) do
416
- ModernRetryableTestConsumer.new(operation, cluster)
469
+ ModernRetryableTestConsumer.new(operation, cluster, client)
417
470
  end
418
471
 
419
472
  before do
@@ -2,8 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongo::Server::ConnectionPool do
4
4
 
5
- let(:options) do
6
- SpecConfig.instance.test_options.merge(max_pool_size: 2)
5
+ let(:options) { {max_pool_size: 2} }
6
+
7
+ let(:server_options) do
8
+ SpecConfig.instance.test_options.merge(options)
7
9
  end
8
10
 
9
11
  let(:address) do
@@ -25,15 +27,260 @@ describe Mongo::Server::ConnectionPool do
25
27
  allow(cl).to receive(:topology).and_return(topology)
26
28
  allow(cl).to receive(:app_metadata).and_return(app_metadata)
27
29
  allow(cl).to receive(:options).and_return({})
30
+ allow(cl).to receive(:update_cluster_time)
31
+ end
32
+ end
33
+
34
+ let(:server) do
35
+ Mongo::Server.new(address, cluster, monitoring, listeners, server_options)
36
+ end
37
+
38
+ let(:pool) do
39
+ described_class.new(server)
40
+ end
41
+
42
+ describe '#initialize' do
43
+
44
+ context 'when a min size is provided' do
45
+
46
+ let(:pool) do
47
+ described_class.new(server, :min_pool_size => 2)
48
+ end
49
+
50
+ it 'creates the pool with no connections' do
51
+ expect(pool.size).to eq(0)
52
+ expect(pool.available_count).to eq(0)
53
+ end
54
+
55
+ it 'does not use the same objects in the pool' do
56
+ expect(pool.check_out).to_not equal(pool.check_out)
57
+ end
58
+ end
59
+
60
+ context 'when min size exceeds default max size' do
61
+
62
+ let(:pool) do
63
+ described_class.new(server, :min_pool_size => 10)
64
+ end
65
+
66
+ it 'sets max size to equal provided min size' do
67
+ expect(pool.max_size).to eq(10)
68
+ end
69
+ end
70
+
71
+ context 'when no min size is provided' do
72
+
73
+ let(:pool) do
74
+ described_class.new(server)
75
+ end
76
+
77
+ it 'creates the pool with no connections' do
78
+ expect(pool.size).to eq(0)
79
+ expect(pool.available_count).to eq(0)
80
+ end
81
+ end
82
+
83
+ context 'sizes given as min_size and max_size' do
84
+
85
+ let(:pool) do
86
+ described_class.new(server, min_size: 3, max_size: 7)
87
+ end
88
+
89
+ it 'sets sizes correctly' do
90
+ expect(pool.min_size).to eq(3)
91
+ expect(pool.max_size).to eq(7)
92
+ end
93
+ end
94
+
95
+ context 'sizes given as min_pool_size and max_pool_size' do
96
+
97
+ let(:pool) do
98
+ described_class.new(server, min_pool_size: 3, max_pool_size: 7)
99
+ end
100
+
101
+ it 'sets sizes correctly' do
102
+ expect(pool.min_size).to eq(3)
103
+ expect(pool.max_size).to eq(7)
104
+ end
105
+ end
106
+
107
+ context 'timeout given as wait_timeout' do
108
+
109
+ let(:pool) do
110
+ described_class.new(server, wait_timeout: 4)
111
+ end
112
+
113
+ it 'sets wait timeout correctly' do
114
+ expect(pool.wait_timeout).to eq(4)
115
+ end
116
+ end
117
+
118
+ context 'timeout given as wait_queue_timeout' do
119
+
120
+ let(:pool) do
121
+ described_class.new(server, wait_queue_timeout: 4)
122
+ end
123
+
124
+ it 'sets wait timeout correctly' do
125
+ expect(pool.wait_timeout).to eq(4)
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#max_size' do
131
+
132
+ context 'when a max pool size option is provided' do
133
+
134
+ let(:pool) do
135
+ described_class.new(server, :max_pool_size => 3)
136
+ end
137
+
138
+ it 'returns the max size' do
139
+ expect(pool.max_size).to eq(3)
140
+ end
141
+ end
142
+
143
+ context 'when no pool size option is provided' do
144
+
145
+ it 'returns the default size' do
146
+ expect(pool.max_size).to eq(5)
147
+ end
148
+ end
149
+
150
+ context 'when pool is closed' do
151
+ before do
152
+ pool.close
153
+ end
154
+
155
+ it 'returns max size' do
156
+ expect(pool.max_size).to eq(5)
157
+ end
28
158
  end
29
159
  end
30
160
 
31
- describe '#checkin' do
161
+ describe '#wait_timeout' do
162
+
163
+ context 'when the wait timeout option is provided' do
164
+
165
+ let(:pool) do
166
+ described_class.new(server, :wait_queue_timeout => 3)
167
+ end
32
168
 
33
- let(:server) do
34
- Mongo::Server.new(address, cluster, monitoring, listeners, options)
169
+ it 'returns the wait timeout' do
170
+ expect(pool.wait_timeout).to eq(3)
171
+ end
35
172
  end
36
173
 
174
+ context 'when the wait timeout option is not provided' do
175
+
176
+ it 'returns the default wait timeout' do
177
+ expect(pool.wait_timeout).to eq(1)
178
+ end
179
+ end
180
+ end
181
+
182
+ describe '#size' do
183
+ context 'pool without connections' do
184
+ it 'is 0' do
185
+ expect(pool.size).to eq(0)
186
+ end
187
+ end
188
+
189
+ context 'pool with a checked out connection' do
190
+ before do
191
+ pool.check_out
192
+ end
193
+
194
+ it 'is 1' do
195
+ expect(pool.size).to eq(1)
196
+ end
197
+ end
198
+
199
+ context 'pool with an available connection' do
200
+ before do
201
+ connection = pool.check_out
202
+ pool.check_in(connection)
203
+ end
204
+
205
+ it 'is 1' do
206
+ expect(pool.size).to eq(1)
207
+ end
208
+ end
209
+
210
+ context 'when pool is closed' do
211
+ before do
212
+ pool.close
213
+ end
214
+
215
+ it 'raises PoolClosedError' do
216
+ expect do
217
+ pool.size
218
+ end.to raise_error(Mongo::Error::PoolClosedError)
219
+ end
220
+ end
221
+ end
222
+
223
+ describe '#available_count' do
224
+ context 'pool without connections' do
225
+ it 'is 0' do
226
+ expect(pool.available_count).to eq(0)
227
+ end
228
+ end
229
+
230
+ context 'pool with a checked out connection' do
231
+ before do
232
+ pool.check_out
233
+ end
234
+
235
+ it 'is 0' do
236
+ expect(pool.available_count).to eq(0)
237
+ end
238
+ end
239
+
240
+ context 'pool with an available connection' do
241
+ before do
242
+ connection = pool.check_out
243
+ pool.check_in(connection)
244
+ end
245
+
246
+ it 'is 1' do
247
+ expect(pool.available_count).to eq(1)
248
+ end
249
+ end
250
+
251
+ context 'when pool is closed' do
252
+ before do
253
+ pool.close
254
+ end
255
+
256
+ it 'raises PoolClosedError' do
257
+ expect do
258
+ pool.available_count
259
+ end.to raise_error(Mongo::Error::PoolClosedError)
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#closed?' do
265
+ context 'pool is not closed' do
266
+ it 'is false' do
267
+ expect(pool.closed?).to be false
268
+ end
269
+ end
270
+
271
+ context 'pool is closed' do
272
+ before do
273
+ pool.close
274
+ end
275
+
276
+ it 'is true' do
277
+ expect(pool.closed?).to be true
278
+ end
279
+ end
280
+ end
281
+
282
+ describe '#check_in' do
283
+
37
284
  let!(:pool) do
38
285
  server.pool
39
286
  end
@@ -43,108 +290,315 @@ describe Mongo::Server::ConnectionPool do
43
290
  server.disconnect!
44
291
  end
45
292
 
293
+ let(:options) { {max_pool_size: 2} }
294
+
295
+ let(:connection) do
296
+ pool.check_out
297
+ end
298
+
46
299
  context 'when a connection is checked out on the thread' do
47
300
 
48
- let!(:connection) do
49
- pool.checkout
301
+ before do
302
+ pool.check_in(connection)
50
303
  end
51
304
 
305
+ it 'returns the connection to the pool' do
306
+ expect(pool.size).to eq(1)
307
+ end
308
+ end
309
+
310
+ context 'connection of the same generation as pool' do
52
311
  before do
53
- pool.checkin(connection)
312
+ expect(pool.generation).to eq(connection.generation)
54
313
  end
55
314
 
56
- let(:queue) do
57
- pool.send(:queue).queue
315
+ it 'adds the connection to the pool' do
316
+ # connection is checked out
317
+ expect(pool.available_count).to eq(0)
318
+ expect(pool.size).to eq(1)
319
+ pool.check_in(connection)
320
+ # now connection is in the queue
321
+ expect(pool.available_count).to eq(1)
322
+ expect(pool.size).to eq(1)
323
+ expect(pool.check_out).to eq(connection)
58
324
  end
325
+ end
59
326
 
60
- it 'returns the connection to the queue' do
61
- expect(queue.size).to eq(1)
327
+ shared_examples 'does not add connection to pool' do
328
+ it 'disconnects connection and does not add connection to pool' do
329
+ # connection was checked out
330
+ expect(pool.available_count).to eq(0)
331
+ expect(pool.size).to eq(1)
332
+ expect(connection).to receive(:disconnect!)
333
+ pool.check_in(connection)
334
+ # connection is not added to the pool, and no replacement
335
+ # connection has been created at this point
336
+ expect(pool.available_count).to eq(0)
337
+ expect(pool.size).to eq(0)
338
+ expect(pool.check_out).not_to eq(connection)
62
339
  end
63
340
  end
64
- end
65
341
 
66
- describe '#checkout' do
342
+ context 'connection of earlier generation than pool' do
343
+ let(:connection) do
344
+ pool.check_out.tap do |connection|
345
+ expect(connection).to receive(:generation).at_least(:once).and_return(0)
346
+ expect(connection).not_to receive(:record_checkin!)
347
+ end
348
+ end
67
349
 
68
- let(:server) do
69
- Mongo::Server.new(address, cluster, monitoring, listeners, options)
70
- end
350
+ before do
351
+ expect(connection.generation < pool.generation).to be true
352
+ end
71
353
 
72
- let!(:pool) do
73
- server.pool
354
+ it_behaves_like 'does not add connection to pool'
74
355
  end
75
356
 
76
- context 'when no connection is checked out on the same thread' do
77
-
78
- let!(:connection) do
79
- pool.checkout
357
+ context 'connection of later generation than pool' do
358
+ let(:connection) do
359
+ pool.check_out.tap do |connection|
360
+ expect(connection).to receive(:generation).at_least(:once).and_return(7)
361
+ expect(connection).not_to receive(:record_checkin!)
362
+ end
80
363
  end
81
364
 
82
- it 'returns a new connection' do
83
- expect(connection.address).to eq(server.address)
365
+ before do
366
+ expect(connection.generation > pool.generation).to be true
84
367
  end
368
+
369
+ it_behaves_like 'does not add connection to pool'
85
370
  end
86
371
 
87
- context 'when a connection is checked out on the same thread' do
372
+ context 'when pool is closed' do
373
+ let(:connection) { pool.check_out }
88
374
 
89
375
  before do
90
- pool.checkout
376
+ connection
377
+ pool.close
91
378
  end
92
379
 
93
- it 'returns the threads connection' do
94
- expect(pool.checkout.address).to eq(server.address)
380
+ it 'closes connection' do
381
+ expect(connection.closed?).to be false
382
+ expect(pool.instance_variable_get('@available_connections').length).to eq(0)
383
+ pool.check_in(connection)
384
+ expect(connection.closed?).to be true
385
+ expect(pool.instance_variable_get('@available_connections').length).to eq(0)
95
386
  end
96
387
  end
388
+ end
389
+
390
+ describe '#check_out' do
391
+
392
+ let!(:pool) do
393
+ server.pool
394
+ end
97
395
 
98
396
  context 'when a connection is checked out on a different thread' do
99
397
 
100
398
  let!(:connection) do
101
- Thread.new { pool.checkout }.join
399
+ Thread.new { pool.check_out }.join
102
400
  end
103
401
 
104
402
  it 'returns a new connection' do
105
- expect(pool.checkout.address).to eq(server.address)
403
+ expect(pool.check_out.address).to eq(server.address)
106
404
  end
107
405
 
108
406
  it 'does not return the same connection instance' do
109
- expect(pool.checkout).to_not eql(connection)
407
+ expect(pool.check_out).to_not eql(connection)
110
408
  end
111
409
  end
112
410
 
113
411
  context 'when connections are checked out and checked back in' do
114
412
 
115
413
  it 'pulls the connection from the front of the queue' do
116
- first = pool.checkout
117
- second = pool.checkout
118
- pool.checkin(second)
119
- pool.checkin(first)
120
- expect(pool.checkout).to be(first)
414
+ first = pool.check_out
415
+ second = pool.check_out
416
+ pool.check_in(second)
417
+ pool.check_in(first)
418
+ expect(pool.check_out).to be(first)
419
+ end
420
+ end
421
+
422
+ context 'when there is an available connection which is stale' do
423
+ let(:options) do
424
+ {max_pool_size: 2, max_idle_time: 0.1}
425
+ end
426
+
427
+ let(:connection) do
428
+ pool.check_out.tap do |connection|
429
+ allow(connection).to receive(:generation).and_return(pool.generation)
430
+ allow(connection).to receive(:record_checkin!).and_return(connection)
431
+ expect(connection).to receive(:last_checkin).at_least(:once).and_return(Time.now - 10)
432
+ end
433
+ end
434
+
435
+ before do
436
+ pool.check_in(connection)
437
+ end
438
+
439
+ after do
440
+ pool.close(force: true)
441
+ end
442
+
443
+ it 'closes stale connection and creates a new one' do
444
+ expect(connection).to receive(:disconnect!)
445
+ expect(Mongo::Server::Connection).to receive(:new).and_call_original
446
+ pool.check_out
447
+ end
448
+ end
449
+
450
+ context 'when there are no available connections' do
451
+
452
+ let(:options) do
453
+ {max_pool_size: 1}
454
+ end
455
+
456
+ context 'when the max size is not reached' do
457
+
458
+ it 'creates a new connection' do
459
+ expect(Mongo::Server::Connection).to receive(:new).once.and_call_original
460
+ expect(pool.check_out).to be_a(Mongo::Server::Connection)
461
+ expect(pool.size).to eq(1)
462
+ end
463
+ end
464
+
465
+ context 'when the max size is reached' do
466
+
467
+ it 'raises a timeout error' do
468
+ expect(Mongo::Server::Connection).to receive(:new).once.and_call_original
469
+ expect {
470
+ pool.check_out
471
+ pool.check_out
472
+ }.to raise_error(Timeout::Error)
473
+ expect(pool.size).to eq(1)
474
+ end
475
+ end
476
+ end
477
+
478
+ context 'when waiting for a connection to be checked in' do
479
+
480
+ let!(:connection) { pool.check_out }
481
+
482
+ before do
483
+ allow(connection).to receive(:record_checkin!).and_return(connection)
484
+ Thread.new do
485
+ sleep(0.5)
486
+ pool.check_in(connection)
487
+ end.join
488
+ end
489
+
490
+ it 'returns the checked in connection' do
491
+ expect(pool.check_out).to eq(connection)
492
+ end
493
+ end
494
+
495
+ context 'when pool is closed' do
496
+ before do
497
+ pool.close
498
+ end
499
+
500
+ it 'raises PoolClosedError' do
501
+ expect do
502
+ pool.check_out
503
+ end.to raise_error(Mongo::Error::PoolClosedError)
121
504
  end
122
505
  end
123
506
  end
124
507
 
125
508
  describe '#disconnect!' do
126
509
 
127
- let(:server) do
128
- Mongo::Server.new(address, cluster, monitoring, listeners, options)
510
+ def create_pool(min_pool_size)
511
+ described_class.new(server, max_pool_size: 3, min_pool_size: min_pool_size).tap do |pool|
512
+ # make pool be of size 2 so that it has enqueued connections
513
+ # when told to disconnect
514
+ c1 = pool.check_out
515
+ c2 = pool.check_out
516
+ allow(c1).to receive(:record_checkin!).and_return(c1)
517
+ allow(c2).to receive(:record_checkin!).and_return(c2)
518
+ pool.check_in(c1)
519
+ pool.check_in(c2)
520
+ expect(pool.size).to eq(2)
521
+ expect(pool.available_count).to eq(2)
522
+ end
523
+ end
524
+
525
+ shared_examples_for 'disconnects and removes all connections in the pool and bumps generation' do
526
+ it 'disconnects and removes and bumps' do
527
+ old_connections = []
528
+ pool.instance_variable_get('@available_connections').each do |connection|
529
+ expect(connection).to receive(:disconnect!)
530
+ old_connections << connection
531
+ end
532
+
533
+ expect(pool.size).to eq(2)
534
+ expect(pool.available_count).to eq(2)
535
+
536
+ pool.disconnect!
537
+
538
+ expect(pool.size).to eq(0)
539
+ expect(pool.available_count).to eq(0)
540
+
541
+ new_connection = pool.check_out
542
+ expect(old_connections).not_to include(new_connection)
543
+ expect(new_connection.generation).to eq(2)
544
+ end
129
545
  end
130
546
 
131
- let!(:pool) do
132
- server.pool
547
+ context 'min size is 0' do
548
+ let(:pool) do
549
+ create_pool(0)
550
+ end
551
+
552
+ it_behaves_like 'disconnects and removes all connections in the pool and bumps generation'
133
553
  end
134
554
 
135
- it 'disconnects the queue' do
136
- expect(server).to receive(:pool).and_return(pool)
137
- expect(pool.send(:queue)).to receive(:disconnect!).once.and_call_original
138
- server.disconnect!
555
+ context 'min size is not 0' do
556
+ let(:pool) do
557
+ create_pool(1)
558
+ end
559
+
560
+ it_behaves_like 'disconnects and removes all connections in the pool and bumps generation'
561
+ end
562
+
563
+ context 'when pool is closed' do
564
+ before do
565
+ pool.close
566
+ end
567
+
568
+ it 'raises PoolClosedError' do
569
+ expect do
570
+ pool.disconnect!
571
+ end.to raise_error(Mongo::Error::PoolClosedError)
572
+ end
139
573
  end
140
574
  end
141
575
 
142
- describe '#inspect' do
576
+ describe '#close' do
577
+ context 'when pool is not closed' do
578
+ it 'closes the pool' do
579
+ expect(pool).not_to be_closed
580
+
581
+ pool.close
143
582
 
144
- let(:server) do
145
- Mongo::Server.new(address, cluster, monitoring, listeners, options)
583
+ expect(pool).to be_closed
584
+ end
146
585
  end
147
586
 
587
+ context 'when pool is closed' do
588
+ before do
589
+ pool.close
590
+ end
591
+
592
+ it 'is a no-op' do
593
+ pool.close
594
+ expect(pool).to be_closed
595
+ end
596
+ end
597
+ end
598
+
599
+ describe '#inspect' do
600
+ let(:options) { {min_pool_size: 3, max_pool_size: 7, wait_timeout: 9, wait_queue_timeout: 9} }
601
+
148
602
  let!(:pool) do
149
603
  server.pool
150
604
  end
@@ -158,16 +612,48 @@ describe Mongo::Server::ConnectionPool do
158
612
  expect(pool.inspect).to include(pool.object_id.to_s)
159
613
  end
160
614
 
615
+ it 'includes the min size' do
616
+ expect(pool.inspect).to include('min_size=3')
617
+ end
618
+
619
+ it 'includes the max size' do
620
+ expect(pool.inspect).to include('max_size=7')
621
+ end
622
+
623
+ it 'includes the wait timeout' do
624
+ expect(pool.inspect).to include('wait_timeout=9')
625
+ end
626
+
627
+ it 'includes the current size' do
628
+ expect(pool.inspect).to include('current_size=0')
629
+ end
630
+
631
+ =begin obsolete
161
632
  it 'includes the queue inspection' do
162
633
  expect(pool.inspect).to include(pool.__send__(:queue).inspect)
163
634
  end
164
- end
635
+ =end
165
636
 
166
- describe '#with_connection' do
637
+ it 'indicates the pool is not closed' do
638
+ expect(pool.inspect).not_to include('closed')
639
+ end
640
+
641
+ context 'when pool is closed' do
642
+ before do
643
+ pool.close
644
+ end
167
645
 
168
- let(:server) do
169
- Mongo::Server.new(address, cluster, monitoring, listeners, options)
646
+ it 'returns inspection string' do
647
+ expect(pool.inspect).to include('min_size=')
648
+ end
649
+
650
+ it 'indicates the pool is closed' do
651
+ expect(pool.inspect).to include('closed')
652
+ end
170
653
  end
654
+ end
655
+
656
+ describe '#with_connection' do
171
657
 
172
658
  let!(:pool) do
173
659
  server.pool
@@ -175,69 +661,86 @@ describe Mongo::Server::ConnectionPool do
175
661
 
176
662
  context 'when a connection cannot be checked out' do
177
663
 
178
- before do
179
- allow(pool).to receive(:checkout).and_return(nil)
664
+ it 'does not add the connection to the pool' do
665
+ pending 'Re-enable when connections are connected prior to being returned from check_out method'
666
+
667
+ allow(pool).to receive(:check_out).and_raise(Mongo::Error::SocketError)
180
668
  pool.with_connection { |c| c }
669
+
670
+ expect(pool.size).to eq(0)
181
671
  end
672
+ end
182
673
 
183
- let(:queue) do
184
- pool.send(:queue).queue
674
+ context 'when pool is closed' do
675
+ before do
676
+ pool.close
185
677
  end
186
678
 
187
- it 'does not add the connection to the pool' do
188
- expect(queue.size).to eq(1)
679
+ it 'raises PoolClosedError' do
680
+ expect do
681
+ pool.with_connection { |c| c }
682
+ end.to raise_error(Mongo::Error::PoolClosedError)
189
683
  end
190
684
  end
191
685
  end
192
686
 
193
687
  context 'when the connection does not finish authenticating before the thread is killed' do
194
688
 
195
- let(:server) do
196
- Mongo::Server.new(address, cluster, monitoring, listeners, options)
197
- end
198
-
199
689
  let!(:pool) do
200
690
  server.pool
201
691
  end
202
692
 
203
- let(:options) do
693
+ let(:server_options) do
204
694
  { user: SpecConfig.instance.root_user.name, password: SpecConfig.instance.root_user.password }.merge(SpecConfig.instance.test_options).merge(max_pool_size: 1)
205
695
  end
206
696
 
207
697
  before do
208
- t = Thread.new {
698
+ pool
699
+ ClientRegistry.instance.close_all_clients
700
+ end
701
+
702
+ it 'creates a new connection' do
703
+ invoked = nil
704
+
705
+ t = Thread.new do
209
706
  # Kill the thread when it's authenticating
210
- allow(Mongo::Auth).to receive(:get) { t.kill && !t.alive? }
211
- pool.with_connection { |c| c.send(:ensure_connected) { |socket| socket } }
212
- }
707
+ expect(Mongo::Auth).to receive(:get) do
708
+ if Thread.current != t
709
+ raise 'Auth invoked on unexpected thread'
710
+ end
711
+ invoked = true
712
+ t.kill
713
+ raise 'Should not get here'
714
+ end
715
+ pool.with_connection do |c|
716
+ c.send(:ensure_connected) { |socket| socket }
717
+ end
718
+ end
213
719
  t.join
214
- end
215
720
 
216
- it 'disconnects the socket' do
217
- expect(pool.checkout.send(:socket)).to be_nil
721
+ #expect(Mongo::Auth).to receive(:get).and_call_original
722
+ expect(pool.check_out).to be_a(Mongo::Server::Connection)
723
+ expect(invoked).to be true
218
724
  end
219
725
  end
220
726
 
221
- describe '#close_stale_sockets!' do
222
-
223
- let(:server) do
224
- Mongo::Server.new(address, authorized_client.cluster, monitoring, listeners, options)
225
- end
727
+ describe '#close_idle_sockets' do
226
728
 
227
729
  let!(:pool) do
228
730
  server.pool
229
731
  end
230
732
 
231
- let(:queue) do
232
- pool.instance_variable_get(:@queue).queue
233
- end
234
-
235
733
  context 'when there is a max_idle_time specified' do
236
734
 
237
735
  let(:options) do
238
- SpecConfig.instance.test_options.merge(max_pool_size: 2, max_idle_time: 0.5)
736
+ {max_pool_size: 2, max_idle_time: 0.5}
239
737
  end
240
738
 
739
+ after do
740
+ Timecop.return
741
+ end
742
+
743
+ =begin obsolete
241
744
  context 'when the connections have not been checked out' do
242
745
 
243
746
  before do
@@ -245,66 +748,64 @@ describe Mongo::Server::ConnectionPool do
245
748
  expect(conn).not_to receive(:disconnect!)
246
749
  end
247
750
  sleep(0.5)
248
- pool.close_stale_sockets!
751
+ pool.close_idle_sockets
249
752
  end
250
753
 
251
754
  it 'does not close any sockets' do
252
755
  expect(queue.none? { |c| c.connected? }).to be(true)
253
756
  end
254
757
  end
758
+ =end
255
759
 
256
- context 'when the sockets have already been checked out and returned to the pool' do
760
+ context 'when connections have been checked out and returned to the pool' do
257
761
 
258
762
  context 'when min size is 0' do
259
763
 
260
764
  let(:options) do
261
- SpecConfig.instance.test_options.merge(max_pool_size: 2, min_pool_size: 0, max_idle_time: 0.5)
765
+ {max_pool_size: 2, min_pool_size: 0, max_idle_time: 0.5}
262
766
  end
263
767
 
264
768
  before do
265
- queue.each do |conn|
266
- expect(conn).to receive(:disconnect!).and_call_original
267
- end
268
- pool.checkin(pool.checkout)
269
- pool.checkin(pool.checkout)
769
+ c1 = pool.check_out
770
+ c2 = pool.check_out
771
+ pool.check_in(c1)
772
+ pool.check_in(c2)
270
773
  sleep(0.5)
271
- pool.close_stale_sockets!
774
+ expect(c1).to receive(:disconnect!).and_call_original
775
+ expect(c2).to receive(:disconnect!).and_call_original
776
+ pool.close_idle_sockets
272
777
  end
273
778
 
274
- it 'closes all stale sockets' do
275
- expect(queue.all? { |c| !c.connected? }).to be(true)
779
+ it 'closes all idle sockets' do
780
+ expect(pool.size).to be(0)
276
781
  end
277
782
  end
278
783
 
279
784
  context 'when min size is > 0' do
280
- after do
281
- Timecop.return
282
- end
283
-
284
785
  context 'when more than the number of min_size are checked out' do
285
786
  let(:options) do
286
- SpecConfig.instance.test_options.merge(max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5)
787
+ {max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5}
287
788
  end
288
789
 
289
- it 'closes and removes connections with stale sockets and does not connect new ones' do
290
- first = pool.checkout
291
- second = pool.checkout
292
- third = pool.checkout
293
- fourth = pool.checkout
294
- fifth = pool.checkout
790
+ it 'closes and removes connections with idle sockets and does not connect new ones' do
791
+ first = pool.check_out
792
+ second = pool.check_out
793
+ third = pool.check_out
794
+ fourth = pool.check_out
795
+ fifth = pool.check_out
295
796
 
296
- pool.checkin(fifth)
797
+ pool.check_in(fifth)
297
798
 
298
799
  expect(fifth).to receive(:disconnect!).and_call_original
299
800
  expect(fifth).not_to receive(:connect!)
300
801
 
301
802
  Timecop.travel(Time.now + 1)
302
- expect(queue.length).to be(1)
303
- pool.close_stale_sockets!
803
+ expect(pool.size).to be(5)
804
+ expect(pool.available_count).to be(1)
805
+ pool.close_idle_sockets
304
806
 
305
- expect(pool.send(:queue).pool_size).to be(4)
306
- expect(pool.send(:queue).queue_size).to be(0)
307
- expect(queue.length).to be(0)
807
+ expect(pool.size).to be(4)
808
+ expect(pool.available_count).to be(0)
308
809
  expect(fifth.connected?).to be(false)
309
810
  end
310
811
  end
@@ -312,19 +813,19 @@ describe Mongo::Server::ConnectionPool do
312
813
  context 'when between 0 and min_size number of connections are checked out' do
313
814
 
314
815
  let(:options) do
315
- SpecConfig.instance.test_options.merge(max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5)
816
+ {max_pool_size: 5, min_pool_size: 3, max_idle_time: 0.5}
316
817
  end
317
818
 
318
- it 'closes and removes connections with stale sockets and does not connect new ones' do
319
- first = pool.checkout
320
- second = pool.checkout
321
- third = pool.checkout
322
- fourth = pool.checkout
323
- fifth = pool.checkout
819
+ it 'closes and removes connections with idle sockets and does not connect new ones' do
820
+ first = pool.check_out
821
+ second = pool.check_out
822
+ third = pool.check_out
823
+ fourth = pool.check_out
824
+ fifth = pool.check_out
324
825
 
325
- pool.checkin(third)
326
- pool.checkin(fourth)
327
- pool.checkin(fifth)
826
+ pool.check_in(third)
827
+ pool.check_in(fourth)
828
+ pool.check_in(fifth)
328
829
 
329
830
 
330
831
  expect(third).to receive(:disconnect!).and_call_original
@@ -337,12 +838,12 @@ describe Mongo::Server::ConnectionPool do
337
838
  expect(fifth).not_to receive(:connect!).and_call_original
338
839
 
339
840
  Timecop.travel(Time.now + 1)
340
- expect(queue.length).to be(3)
341
- pool.close_stale_sockets!
841
+ expect(pool.size).to be(5)
842
+ expect(pool.available_count).to be(3)
843
+ pool.close_idle_sockets
342
844
 
343
- expect(pool.send(:queue).pool_size).to be(2)
344
- expect(pool.send(:queue).queue_size).to be(0)
345
- expect(queue.length).to be(0)
845
+ expect(pool.size).to be(2)
846
+ expect(pool.available_count).to be(0)
346
847
 
347
848
  expect(third.connected?).to be(false)
348
849
  expect(fourth.connected?).to be(false)
@@ -353,18 +854,52 @@ describe Mongo::Server::ConnectionPool do
353
854
  end
354
855
  end
355
856
 
857
+ context 'when available connections include idle and non-idle ones' do
858
+ let(:pool) do
859
+ described_class.new(server, max_pool_size: 2, max_idle_time: 0.5)
860
+ end
861
+
862
+ let(:connection) do
863
+ pool.check_out.tap do |con|
864
+ allow(con).to receive(:disconnect!)
865
+ end
866
+ end
867
+
868
+ it 'disconnects all expired and only expired connections' do
869
+ c1 = pool.check_out
870
+ expect(c1).to receive(:disconnect!)
871
+ c2 = pool.check_out
872
+ expect(c2).not_to receive(:disconnect!)
873
+
874
+ pool.check_in(c1)
875
+ Timecop.travel(Time.now + 1)
876
+ pool.check_in(c2)
877
+
878
+ expect(pool.size).to eq(2)
879
+ expect(pool.available_count).to eq(2)
880
+
881
+ expect(c1).not_to receive(:connect!)
882
+ expect(c2).not_to receive(:connect!)
883
+
884
+ pool.close_idle_sockets
885
+
886
+ expect(pool.size).to eq(1)
887
+ expect(pool.available_count).to eq(1)
888
+ end
889
+ end
890
+
356
891
  context 'when there is no max_idle_time specified' do
357
892
 
358
893
  let(:connection) do
359
- conn = pool.checkout
894
+ conn = pool.check_out
360
895
  conn.connect!
361
- pool.checkin(conn)
896
+ pool.check_in(conn)
362
897
  conn
363
898
  end
364
899
 
365
900
  before do
366
901
  expect(connection).not_to receive(:disconnect!)
367
- pool.close_stale_sockets!
902
+ pool.close_idle_sockets
368
903
  end
369
904
 
370
905
  it 'does not close any sockets' do