mongo 2.5.3 → 2.6.0

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 (394) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +1 -1
  5. data/README.md +3 -2
  6. data/lib/mongo.rb +2 -2
  7. data/lib/mongo/address.rb +10 -2
  8. data/lib/mongo/address/ipv4.rb +1 -1
  9. data/lib/mongo/address/ipv6.rb +26 -5
  10. data/lib/mongo/address/unix.rb +1 -1
  11. data/lib/mongo/auth.rb +10 -3
  12. data/lib/mongo/auth/cr.rb +4 -1
  13. data/lib/mongo/auth/cr/conversation.rb +4 -1
  14. data/lib/mongo/auth/ldap.rb +1 -1
  15. data/lib/mongo/auth/ldap/conversation.rb +1 -1
  16. data/lib/mongo/auth/roles.rb +1 -1
  17. data/lib/mongo/auth/scram.rb +24 -7
  18. data/lib/mongo/auth/scram/conversation.rb +52 -19
  19. data/lib/mongo/auth/stringprep.rb +114 -0
  20. data/lib/mongo/auth/stringprep/profiles/sasl.rb +73 -0
  21. data/lib/mongo/auth/stringprep/tables.rb +3232 -0
  22. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +174 -0
  23. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1170 -0
  24. data/lib/mongo/auth/user.rb +14 -3
  25. data/lib/mongo/auth/user/view.rb +1 -1
  26. data/lib/mongo/auth/x509.rb +1 -1
  27. data/lib/mongo/auth/x509/conversation.rb +1 -1
  28. data/lib/mongo/bson.rb +1 -1
  29. data/lib/mongo/bulk_write.rb +8 -8
  30. data/lib/mongo/bulk_write/combineable.rb +1 -1
  31. data/lib/mongo/bulk_write/ordered_combiner.rb +1 -1
  32. data/lib/mongo/bulk_write/result.rb +1 -1
  33. data/lib/mongo/bulk_write/result_combiner.rb +4 -4
  34. data/lib/mongo/bulk_write/transformable.rb +1 -1
  35. data/lib/mongo/bulk_write/unordered_combiner.rb +1 -1
  36. data/lib/mongo/bulk_write/validatable.rb +1 -1
  37. data/lib/mongo/client.rb +115 -24
  38. data/lib/mongo/cluster.rb +17 -10
  39. data/lib/mongo/cluster/app_metadata.rb +7 -1
  40. data/lib/mongo/cluster/periodic_executor.rb +1 -1
  41. data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -1
  42. data/lib/mongo/cluster/topology.rb +12 -2
  43. data/lib/mongo/cluster/topology/replica_set.rb +9 -1
  44. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  45. data/lib/mongo/cluster/topology/single.rb +1 -1
  46. data/lib/mongo/cluster/topology/unknown.rb +1 -1
  47. data/lib/mongo/collection.rb +75 -19
  48. data/lib/mongo/collection/view.rb +1 -1
  49. data/lib/mongo/collection/view/aggregation.rb +1 -1
  50. data/lib/mongo/collection/view/builder.rb +1 -1
  51. data/lib/mongo/collection/view/builder/aggregation.rb +3 -3
  52. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  53. data/lib/mongo/collection/view/builder/flags.rb +1 -1
  54. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  55. data/lib/mongo/collection/view/builder/modifiers.rb +1 -1
  56. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  57. data/lib/mongo/collection/view/change_stream.rb +193 -17
  58. data/lib/mongo/collection/view/change_stream/retryable.rb +3 -20
  59. data/lib/mongo/collection/view/explainable.rb +1 -1
  60. data/lib/mongo/collection/view/immutable.rb +1 -1
  61. data/lib/mongo/collection/view/iterable.rb +2 -2
  62. data/lib/mongo/collection/view/map_reduce.rb +1 -1
  63. data/lib/mongo/collection/view/readable.rb +108 -29
  64. data/lib/mongo/collection/view/writable.rb +3 -3
  65. data/lib/mongo/cursor.rb +44 -4
  66. data/lib/mongo/cursor/builder.rb +1 -1
  67. data/lib/mongo/cursor/builder/get_more_command.rb +1 -1
  68. data/lib/mongo/cursor/builder/kill_cursors_command.rb +1 -1
  69. data/lib/mongo/cursor/builder/op_get_more.rb +1 -1
  70. data/lib/mongo/cursor/builder/op_kill_cursors.rb +1 -1
  71. data/lib/mongo/database.rb +46 -3
  72. data/lib/mongo/database/view.rb +11 -11
  73. data/lib/mongo/dbref.rb +1 -1
  74. data/lib/mongo/error.rb +57 -1
  75. data/lib/mongo/error/bulk_write_error.rb +2 -2
  76. data/lib/mongo/error/change_stream_resumable.rb +37 -0
  77. data/lib/mongo/error/closed_stream.rb +1 -1
  78. data/lib/mongo/error/extra_file_chunk.rb +1 -1
  79. data/lib/mongo/error/failed_stringprep_validation.rb +38 -0
  80. data/lib/mongo/error/file_not_found.rb +1 -1
  81. data/lib/mongo/error/insufficient_iteration_count.rb +38 -0
  82. data/lib/mongo/error/invalid_application_name.rb +1 -1
  83. data/lib/mongo/error/invalid_bulk_operation.rb +1 -1
  84. data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -1
  85. data/lib/mongo/error/invalid_collection_name.rb +1 -1
  86. data/lib/mongo/error/invalid_database_name.rb +1 -1
  87. data/lib/mongo/error/invalid_document.rb +1 -1
  88. data/lib/mongo/error/invalid_file.rb +1 -1
  89. data/lib/mongo/error/invalid_file_revision.rb +1 -1
  90. data/lib/mongo/error/invalid_min_pool_size.rb +1 -1
  91. data/lib/mongo/error/invalid_nonce.rb +1 -1
  92. data/lib/mongo/error/invalid_read_option.rb +35 -0
  93. data/lib/mongo/error/invalid_replacement_document.rb +1 -1
  94. data/lib/mongo/error/invalid_server_preference.rb +1 -1
  95. data/lib/mongo/error/invalid_session.rb +1 -1
  96. data/lib/mongo/error/invalid_signature.rb +1 -1
  97. data/lib/mongo/error/invalid_transaction_operation.rb +82 -0
  98. data/lib/mongo/error/invalid_txt_record.rb +1 -1
  99. data/lib/mongo/error/invalid_update_document.rb +1 -1
  100. data/lib/mongo/error/invalid_uri.rb +1 -1
  101. data/lib/mongo/error/invalid_write_concern.rb +1 -1
  102. data/lib/mongo/error/max_bson_size.rb +1 -1
  103. data/lib/mongo/error/max_message_size.rb +1 -1
  104. data/lib/mongo/error/mismatched_domain.rb +1 -1
  105. data/lib/mongo/error/missing_file_chunk.rb +1 -1
  106. data/lib/mongo/error/missing_resume_token.rb +1 -1
  107. data/lib/mongo/error/multi_index_drop.rb +1 -1
  108. data/lib/mongo/error/need_primary_server.rb +1 -1
  109. data/lib/mongo/error/no_server_available.rb +1 -1
  110. data/lib/mongo/error/no_srv_records.rb +1 -1
  111. data/lib/mongo/error/operation_failure.rb +108 -14
  112. data/lib/mongo/error/parser.rb +50 -1
  113. data/lib/mongo/error/socket_error.rb +5 -2
  114. data/lib/mongo/error/socket_timeout_error.rb +5 -2
  115. data/lib/mongo/error/unchangeable_collection_option.rb +1 -1
  116. data/lib/mongo/error/unexpected_chunk_length.rb +1 -1
  117. data/lib/mongo/error/unexpected_response.rb +1 -1
  118. data/lib/mongo/error/unknown_payload_type.rb +1 -1
  119. data/lib/mongo/error/unsupported_array_filters.rb +1 -1
  120. data/lib/mongo/error/unsupported_collation.rb +1 -1
  121. data/lib/mongo/error/unsupported_features.rb +1 -1
  122. data/lib/mongo/error/unsupported_message_type.rb +1 -1
  123. data/lib/mongo/error/write_retryable.rb +27 -0
  124. data/lib/mongo/event.rb +10 -9
  125. data/lib/mongo/event/base.rb +33 -0
  126. data/lib/mongo/event/description_changed.rb +2 -2
  127. data/lib/mongo/event/listeners.rb +1 -1
  128. data/lib/mongo/event/member_discovered.rb +4 -2
  129. data/lib/mongo/event/primary_elected.rb +2 -2
  130. data/lib/mongo/event/publisher.rb +1 -1
  131. data/lib/mongo/event/standalone_discovered.rb +2 -2
  132. data/lib/mongo/event/subscriber.rb +1 -1
  133. data/lib/mongo/grid.rb +1 -1
  134. data/lib/mongo/grid/file.rb +1 -1
  135. data/lib/mongo/grid/file/chunk.rb +3 -3
  136. data/lib/mongo/grid/file/info.rb +26 -3
  137. data/lib/mongo/grid/fs_bucket.rb +1 -1
  138. data/lib/mongo/grid/stream.rb +1 -1
  139. data/lib/mongo/grid/stream/read.rb +1 -1
  140. data/lib/mongo/grid/stream/write.rb +1 -1
  141. data/lib/mongo/index.rb +1 -1
  142. data/lib/mongo/index/view.rb +1 -1
  143. data/lib/mongo/loggable.rb +1 -1
  144. data/lib/mongo/logger.rb +1 -1
  145. data/lib/mongo/monitoring.rb +99 -62
  146. data/lib/mongo/monitoring/command_log_subscriber.rb +2 -2
  147. data/lib/mongo/monitoring/event.rb +2 -1
  148. data/lib/mongo/monitoring/event/command_failed.rb +19 -6
  149. data/lib/mongo/monitoring/event/command_started.rb +14 -3
  150. data/lib/mongo/monitoring/event/command_succeeded.rb +5 -3
  151. data/lib/mongo/monitoring/event/secure.rb +1 -1
  152. data/lib/mongo/monitoring/event/server_closed.rb +2 -2
  153. data/lib/mongo/monitoring/event/server_description_changed.rb +2 -2
  154. data/lib/mongo/monitoring/event/server_opening.rb +11 -2
  155. data/lib/mongo/monitoring/event/topology_changed.rb +13 -2
  156. data/lib/mongo/monitoring/event/topology_closed.rb +2 -2
  157. data/lib/mongo/monitoring/event/topology_opening.rb +11 -2
  158. data/lib/mongo/monitoring/publishable.rb +10 -6
  159. data/lib/mongo/monitoring/sdam_log_subscriber.rb +1 -1
  160. data/lib/mongo/monitoring/server_closed_log_subscriber.rb +1 -1
  161. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +1 -1
  162. data/lib/mongo/monitoring/server_opening_log_subscriber.rb +1 -1
  163. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  164. data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +1 -1
  165. data/lib/mongo/operation/aggregate/op_msg.rb +3 -0
  166. data/lib/mongo/operation/create/op_msg.rb +9 -0
  167. data/lib/mongo/operation/create_index/op_msg.rb +9 -0
  168. data/lib/mongo/operation/create_user/command.rb +1 -1
  169. data/lib/mongo/operation/create_user/op_msg.rb +10 -1
  170. data/lib/mongo/operation/delete/op_msg.rb +3 -0
  171. data/lib/mongo/operation/distinct/op_msg.rb +9 -0
  172. data/lib/mongo/operation/drop/op_msg.rb +9 -0
  173. data/lib/mongo/operation/drop_database/op_msg.rb +9 -0
  174. data/lib/mongo/operation/drop_index/op_msg.rb +9 -0
  175. data/lib/mongo/operation/explain/op_msg.rb +3 -0
  176. data/lib/mongo/operation/find/op_msg.rb +3 -0
  177. data/lib/mongo/operation/get_more.rb +1 -1
  178. data/lib/mongo/operation/get_more/command.rb +1 -1
  179. data/lib/mongo/operation/get_more/legacy.rb +1 -1
  180. data/lib/mongo/operation/get_more/op_msg.rb +3 -0
  181. data/lib/mongo/operation/indexes/op_msg.rb +3 -0
  182. data/lib/mongo/operation/indexes/result.rb +1 -1
  183. data/lib/mongo/operation/insert/bulk_result.rb +32 -2
  184. data/lib/mongo/operation/insert/op_msg.rb +3 -0
  185. data/lib/mongo/operation/insert/result.rb +1 -1
  186. data/lib/mongo/operation/kill_cursors/op_msg.rb +9 -0
  187. data/lib/mongo/operation/list_collections/op_msg.rb +3 -0
  188. data/lib/mongo/operation/list_collections/result.rb +5 -1
  189. data/lib/mongo/operation/map_reduce/op_msg.rb +3 -0
  190. data/lib/mongo/operation/map_reduce/result.rb +1 -1
  191. data/lib/mongo/operation/parallel_scan/op_msg.rb +3 -0
  192. data/lib/mongo/operation/remove_user/op_msg.rb +9 -0
  193. data/lib/mongo/operation/result.rb +27 -14
  194. data/lib/mongo/operation/shared/executable.rb +1 -0
  195. data/lib/mongo/operation/shared/sessions_supported.rb +78 -7
  196. data/lib/mongo/operation/shared/specifiable.rb +18 -2
  197. data/lib/mongo/operation/shared/write_concern_supported.rb +1 -1
  198. data/lib/mongo/operation/update/op_msg.rb +3 -0
  199. data/lib/mongo/operation/update_user/command.rb +1 -1
  200. data/lib/mongo/operation/update_user/op_msg.rb +10 -1
  201. data/lib/mongo/operation/users_info/op_msg.rb +3 -0
  202. data/lib/mongo/options.rb +1 -1
  203. data/lib/mongo/options/mapper.rb +1 -1
  204. data/lib/mongo/options/redacted.rb +1 -1
  205. data/lib/mongo/protocol/bit_vector.rb +1 -1
  206. data/lib/mongo/protocol/compressed.rb +1 -1
  207. data/lib/mongo/protocol/delete.rb +1 -1
  208. data/lib/mongo/protocol/get_more.rb +7 -7
  209. data/lib/mongo/protocol/insert.rb +1 -1
  210. data/lib/mongo/protocol/kill_cursors.rb +1 -1
  211. data/lib/mongo/protocol/message.rb +5 -5
  212. data/lib/mongo/protocol/msg.rb +9 -7
  213. data/lib/mongo/protocol/query.rb +1 -1
  214. data/lib/mongo/protocol/registry.rb +1 -1
  215. data/lib/mongo/protocol/reply.rb +10 -10
  216. data/lib/mongo/protocol/serializers.rb +1 -1
  217. data/lib/mongo/protocol/update.rb +1 -1
  218. data/lib/mongo/retryable.rb +22 -14
  219. data/lib/mongo/server.rb +1 -1
  220. data/lib/mongo/server/connectable.rb +1 -1
  221. data/lib/mongo/server/connection.rb +16 -4
  222. data/lib/mongo/server/connection_pool.rb +1 -1
  223. data/lib/mongo/server/connection_pool/queue.rb +1 -1
  224. data/lib/mongo/server/context.rb +1 -1
  225. data/lib/mongo/server/description.rb +14 -2
  226. data/lib/mongo/server/description/features.rb +10 -9
  227. data/lib/mongo/server/description/inspector.rb +1 -1
  228. data/lib/mongo/server/description/inspector/description_changed.rb +1 -1
  229. data/lib/mongo/server/description/inspector/member_discovered.rb +1 -1
  230. data/lib/mongo/server/description/inspector/primary_elected.rb +1 -1
  231. data/lib/mongo/server/description/inspector/standalone_discovered.rb +1 -1
  232. data/lib/mongo/server/monitor.rb +15 -3
  233. data/lib/mongo/server/monitor/connection.rb +1 -1
  234. data/lib/mongo/server_selector.rb +1 -1
  235. data/lib/mongo/server_selector/nearest.rb +1 -1
  236. data/lib/mongo/server_selector/primary.rb +1 -1
  237. data/lib/mongo/server_selector/primary_preferred.rb +1 -1
  238. data/lib/mongo/server_selector/secondary.rb +1 -1
  239. data/lib/mongo/server_selector/secondary_preferred.rb +1 -1
  240. data/lib/mongo/server_selector/selectable.rb +7 -2
  241. data/lib/mongo/session.rb +389 -12
  242. data/lib/mongo/session/server_session.rb +7 -2
  243. data/lib/mongo/session/session_pool.rb +1 -1
  244. data/lib/mongo/socket.rb +1 -1
  245. data/lib/mongo/socket/ssl.rb +1 -1
  246. data/lib/mongo/socket/tcp.rb +1 -1
  247. data/lib/mongo/socket/unix.rb +1 -1
  248. data/lib/mongo/uri.rb +6 -4
  249. data/lib/mongo/uri/srv_protocol.rb +1 -1
  250. data/lib/mongo/version.rb +2 -2
  251. data/lib/mongo/write_concern.rb +1 -1
  252. data/lib/mongo/write_concern/acknowledged.rb +1 -1
  253. data/lib/mongo/write_concern/normalizable.rb +1 -1
  254. data/lib/mongo/write_concern/unacknowledged.rb +1 -1
  255. data/mongo.gemspec +4 -1
  256. data/spec/atlas/atlas_connectivity_spec.rb +54 -0
  257. data/spec/integration/bulk_insert_spec.rb +78 -0
  258. data/spec/integration/change_stream_spec.rb +365 -0
  259. data/spec/integration/command_monitoring_spec.rb +92 -0
  260. data/spec/lite_spec_helper.rb +63 -0
  261. data/spec/mongo/address/ipv6_spec.rb +29 -1
  262. data/spec/mongo/address_spec.rb +34 -0
  263. data/spec/mongo/auth/scram/conversation_spec.rb +326 -120
  264. data/spec/mongo/auth/scram/negotiation_spec.rb +574 -0
  265. data/spec/mongo/auth/scram_spec.rb +107 -38
  266. data/spec/mongo/auth/stringprep/profiles/sasl_spec.rb +113 -0
  267. data/spec/mongo/auth/stringprep_spec.rb +188 -0
  268. data/spec/mongo/auth/user/view_spec.rb +5 -2
  269. data/spec/mongo/auth/user_spec.rb +1 -1
  270. data/spec/mongo/bulk_write/result_spec.rb +120 -0
  271. data/spec/mongo/bulk_write_spec.rb +42 -2
  272. data/spec/mongo/client_spec.rb +121 -9
  273. data/spec/mongo/cluster/app_metadata_spec.rb +14 -1
  274. data/spec/mongo/cluster/topology_spec.rb +1 -23
  275. data/spec/mongo/collection/view/change_stream_spec.rb +62 -180
  276. data/spec/mongo/collection_spec.rb +45 -12
  277. data/spec/mongo/cursor/builder/get_more_command_spec.rb +7 -7
  278. data/spec/mongo/cursor_spec.rb +2 -2
  279. data/spec/mongo/database_spec.rb +3 -3
  280. data/spec/mongo/docs_examples_spec.rb +194 -0
  281. data/spec/mongo/error/operation_failure_spec.rb +152 -0
  282. data/spec/mongo/error/parser_spec.rb +127 -0
  283. data/spec/mongo/grid/fs_bucket_spec.rb +32 -0
  284. data/spec/mongo/grid/stream/write_spec.rb +40 -1
  285. data/spec/mongo/monitoring/event/command_failed_spec.rb +30 -0
  286. data/spec/mongo/monitoring/event/command_started_spec.rb +26 -4
  287. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +29 -7
  288. data/spec/mongo/monitoring_spec.rb +28 -3
  289. data/spec/mongo/protocol/get_more_spec.rb +2 -2
  290. data/spec/mongo/retryable_spec.rb +252 -34
  291. data/spec/mongo/retryable_writes_spec.rb +468 -544
  292. data/spec/mongo/server/connection_spec.rb +5 -5
  293. data/spec/mongo/server/description_spec.rb +23 -6
  294. data/spec/mongo/session/server_session_spec.rb +2 -2
  295. data/spec/mongo/session/session_pool_spec.rb +2 -2
  296. data/spec/mongo/transactions_examples_spec.rb +227 -0
  297. data/spec/mongo/transactions_spec.rb +44 -0
  298. data/spec/spec_helper.rb +135 -49
  299. data/spec/spec_tests/change_streams_spec.rb +42 -0
  300. data/spec/{mongo → spec_tests}/command_monitoring_spec.rb +8 -2
  301. data/spec/{mongo → spec_tests}/connection_string_spec.rb +1 -1
  302. data/spec/{mongo → spec_tests}/crud_spec.rb +5 -7
  303. data/spec/{mongo → spec_tests}/dns_seedlist_discovery_spec.rb +1 -1
  304. data/spec/{mongo → spec_tests}/gridfs_spec.rb +0 -0
  305. data/spec/{mongo → spec_tests}/max_staleness_spec.rb +0 -0
  306. data/spec/spec_tests/retryable_writes_spec.rb +78 -0
  307. data/spec/{mongo → spec_tests}/sdam_monitoring_spec.rb +4 -3
  308. data/spec/{mongo → spec_tests}/sdam_spec.rb +7 -7
  309. data/spec/{mongo → spec_tests}/server_selection_rtt_spec.rb +0 -0
  310. data/spec/{mongo → spec_tests}/server_selection_spec.rb +0 -0
  311. data/spec/support/authorization.rb +18 -6
  312. data/spec/support/change_streams.rb +265 -0
  313. data/spec/support/change_streams/operation.rb +62 -0
  314. data/spec/support/change_streams_tests/change-streams-errors.yml +53 -0
  315. data/spec/support/change_streams_tests/change-streams.yml +299 -0
  316. data/spec/support/command_monitoring.rb +1 -1
  317. data/spec/support/command_monitoring/bulkWrite.yml +4 -28
  318. data/spec/support/command_monitoring/command.yml +19 -0
  319. data/spec/support/command_monitoring/find.yml +17 -19
  320. data/spec/support/command_monitoring/insertMany.yml +2 -8
  321. data/spec/support/command_monitoring/unacknowledgedBulkWrite.yml +34 -0
  322. data/spec/support/connection_string.rb +1 -1
  323. data/spec/support/constraints.rb +56 -0
  324. data/spec/support/crud.rb +9 -4
  325. data/spec/support/crud/read.rb +24 -3
  326. data/spec/support/crud/write.rb +7 -2
  327. data/spec/support/crud_tests/read/count-collation.yml +12 -2
  328. data/spec/support/crud_tests/read/count.yml +43 -5
  329. data/spec/support/gridfs.rb +1 -1
  330. data/spec/support/primary_socket.rb +21 -0
  331. data/spec/support/retryable_writes_tests/bulkWrite-serverErrors.yml +90 -0
  332. data/spec/support/retryable_writes_tests/bulkWrite.yml +99 -1
  333. data/spec/support/retryable_writes_tests/deleteOne-serverErrors.yml +50 -0
  334. data/spec/support/retryable_writes_tests/deleteOne.yml +10 -1
  335. data/spec/support/retryable_writes_tests/findOneAndDelete-serverErrors.yml +50 -0
  336. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +39 -30
  337. data/spec/support/retryable_writes_tests/findOneAndReplace-serverErrors.yml +54 -0
  338. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +9 -0
  339. data/spec/support/retryable_writes_tests/findOneAndUpdate-serverErrors.yml +54 -0
  340. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +9 -0
  341. data/spec/support/retryable_writes_tests/insertMany-serverErrors.yml +59 -0
  342. data/spec/support/retryable_writes_tests/insertMany.yml +11 -6
  343. data/spec/support/retryable_writes_tests/insertOne-serverErrors.yml +471 -0
  344. data/spec/support/retryable_writes_tests/insertOne.yml +9 -0
  345. data/spec/support/retryable_writes_tests/replaceOne-serverErrors.yml +58 -0
  346. data/spec/support/retryable_writes_tests/replaceOne.yml +9 -0
  347. data/spec/support/retryable_writes_tests/updateOne-serverErrors.yml +58 -0
  348. data/spec/support/retryable_writes_tests/updateOne.yml +71 -53
  349. data/spec/support/sdam/rs/normalize_case_me.yml +100 -0
  350. data/spec/support/sdam/sharded/compatible.yml +38 -0
  351. data/spec/support/sdam/sharded/mongos_disconnect.yml +9 -3
  352. data/spec/support/sdam/sharded/multiple_mongoses.yml +6 -2
  353. data/spec/support/sdam/sharded/non_mongos_removed.yml +6 -2
  354. data/spec/support/sdam/sharded/too_new.yml +36 -0
  355. data/spec/support/sdam/sharded/too_old.yml +36 -0
  356. data/spec/support/sdam/single/compatible.yml +26 -0
  357. data/spec/support/sdam/single/direct_connection_external_ip.yml +3 -1
  358. data/spec/support/sdam/single/direct_connection_mongos.yml +3 -1
  359. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +3 -1
  360. data/spec/support/sdam/single/direct_connection_rsprimary.yml +3 -1
  361. data/spec/support/sdam/single/direct_connection_rssecondary.yml +3 -1
  362. data/spec/support/sdam/single/direct_connection_slave.yml +3 -1
  363. data/spec/support/sdam/single/direct_connection_standalone.yml +3 -1
  364. data/spec/support/sdam/single/not_ok_response.yml +6 -2
  365. data/spec/support/sdam/single/standalone_removed.yml +3 -1
  366. data/spec/support/sdam/single/too_new.yml +26 -0
  367. data/spec/support/sdam/single/too_old.yml +24 -0
  368. data/spec/support/shared/session.rb +107 -0
  369. data/spec/support/transactions.rb +391 -0
  370. data/spec/support/transactions/operation.rb +373 -0
  371. data/spec/support/transactions_tests/abort.yml +403 -0
  372. data/spec/support/transactions_tests/bulk.yml +267 -0
  373. data/spec/support/transactions_tests/causal-consistency.yml +173 -0
  374. data/spec/support/transactions_tests/commit.yml +593 -0
  375. data/spec/support/transactions_tests/delete.yml +184 -0
  376. data/spec/support/transactions_tests/error-labels.yml +948 -0
  377. data/spec/support/transactions_tests/errors.yml +125 -0
  378. data/spec/support/transactions_tests/findOneAndDelete.yml +126 -0
  379. data/spec/support/transactions_tests/findOneAndReplace.yml +140 -0
  380. data/spec/support/transactions_tests/findOneAndUpdate.yml +228 -0
  381. data/spec/support/transactions_tests/insert.yml +264 -0
  382. data/spec/support/transactions_tests/isolation.yml +125 -0
  383. data/spec/support/transactions_tests/read-pref.yml +340 -0
  384. data/spec/support/transactions_tests/reads.yml +298 -0
  385. data/spec/support/transactions_tests/retryable-abort.yml +1292 -0
  386. data/spec/support/transactions_tests/retryable-commit.yml +1332 -0
  387. data/spec/support/transactions_tests/retryable-writes.yml +208 -0
  388. data/spec/support/transactions_tests/run-command.yml +189 -0
  389. data/spec/support/transactions_tests/transaction-options.yml +877 -0
  390. data/spec/support/transactions_tests/update.yml +246 -0
  391. data/spec/support/transactions_tests/write-concern.yml +236 -0
  392. metadata +494 -359
  393. metadata.gz.sig +0 -0
  394. data/lib/csasl/csasl.bundle +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2016 MongoDB, Inc.
1
+ # Copyright (C) 2016-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -55,6 +55,11 @@ module Mongo
55
55
  @app_name = cluster.options[:app_name].to_s if cluster.options[:app_name]
56
56
  @platform = cluster.options[:platform]
57
57
  @compressors = cluster.options[:compressors] || []
58
+
59
+ if cluster.options[:user] && !cluster.options[:auth_mech]
60
+ auth_db = cluster.options[:auth_source] || 'admin'
61
+ @request_auth_mech = "#{auth_db}.#{cluster.options[:user]}"
62
+ end
58
63
  end
59
64
 
60
65
  # Get the bytes of the ismaster message including this metadata.
@@ -108,6 +113,7 @@ module Mongo
108
113
  document = Server::Monitor::Connection::ISMASTER
109
114
  document = document.merge(compression: @compressors)
110
115
  document[:client] = client_document
116
+ document[:saslSupportedMechs] = @request_auth_mech if @request_auth_mech
111
117
  document
112
118
  end
113
119
 
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -20,7 +20,17 @@ require 'mongo/cluster/topology/unknown'
20
20
  module Mongo
21
21
  class Cluster
22
22
 
23
- # Defines behaviour for getting servers.
23
+ # Defines behavior for getting servers.
24
+ #
25
+ # Topologies are associated with their clusters - for example, a
26
+ # ReplicaSet topology contains the replica set name. A topology
27
+ # object therefore cannot be used with multiple cluster objects.
28
+ #
29
+ # At the same time, topology objects do not know anything about
30
+ # specific *servers* in a cluster, despite what their constructor
31
+ # may suggest. Which means, in particular, that topology change events
32
+ # require the application to maintain cluster references on its own
33
+ # if it wants to track server changes within a replica set.
24
34
  #
25
35
  # @since 2.0.0
26
36
  module Topology
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -123,6 +123,14 @@ module Mongo
123
123
  # @param [ Monitoring ] monitoring The monitoring.
124
124
  # @param [ Array<String> ] seeds The seeds.
125
125
  #
126
+ # @option options [ Symbol ] :replica_set Name of the replica set to
127
+ # connect to. Can be left blank (either nil or the empty string are
128
+ # accepted) to discover the name from the seeds. If the seeds
129
+ # belong to different replica sets there is no guarantee which
130
+ # replica set is selected - in particular, the driver may choose
131
+ # the replica set name of a secondary if it returns its response
132
+ # prior to a primary belonging to a different replica set.
133
+ #
126
134
  # @since 2.0.0
127
135
  def initialize(options, monitoring, seeds = [])
128
136
  @options = options
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -109,7 +109,7 @@ module Mongo
109
109
  #
110
110
  # @since 2.0.0
111
111
  def server_selector
112
- @server_selector ||= ServerSelector.get(read_preference || database.server_selector)
112
+ @server_selector ||= ServerSelector.get(read_preference || database.server_selector)
113
113
  end
114
114
 
115
115
  # Get the read preference on this collection.
@@ -295,12 +295,13 @@ module Mongo
295
295
  #
296
296
  # @since 2.1.0
297
297
  def aggregate(pipeline, options = {})
298
- View.new(self, {}).aggregate(pipeline, options)
298
+ View.new(self, {}, options).aggregate(pipeline, options)
299
299
  end
300
300
 
301
- # As of version 3.6 of the MongoDB server, a ``$changeStream`` pipeline stage is supported
302
- # in the aggregation framework. This stage allows users to request that notifications are sent for
303
- # all changes to a particular collection.
301
+ # As of version 3.6 of the MongoDB server, a ``$changeStream`` pipeline
302
+ # stage is supported in the aggregation framework. This stage allows users
303
+ # to request that notifications are sent for all changes to a particular
304
+ # collection.
304
305
  #
305
306
  # @example Get change notifications for a given collection.
306
307
  # collection.watch([{ '$match' => { operationType: { '$in' => ['insert', 'replace'] } } }])
@@ -308,30 +309,36 @@ module Mongo
308
309
  # @param [ Array<Hash> ] pipeline Optional additional filter operators.
309
310
  # @param [ Hash ] options The change stream options.
310
311
  #
311
- # @option options [ String ] :full_document Allowed values: ‘default’, ‘updateLookup’. Defaults to ‘default’.
312
- # When set to ‘updateLookup’, the change notification for partial updates will include both a delta
313
- # describing the changes to the document, as well as a copy of the entire document that was changed
314
- # from some time after the change occurred.
315
- # @option options [ BSON::Document, Hash ] :resume_after Specifies the logical starting point for the
316
- # new change stream.
317
- # @option options [ Integer ] :max_await_time_ms The maximum amount of time for the server to wait
318
- # on new documents to satisfy a change stream query.
319
- # @option options [ Integer ] :batch_size The number of documents to return per batch.
312
+ # @option options [ String ] :full_document Allowed values: ‘default’,
313
+ # ‘updateLookup’. Defaults to ‘default’. When set to ‘updateLookup’,
314
+ # the change notification for partial updates will include both a delta
315
+ # describing the changes to the document, as well as a copy of the entire
316
+ # document that was changed from some time after the change occurred.
317
+ # @option options [ BSON::Document, Hash ] :resume_after Specifies the
318
+ # logical starting point for the new change stream.
319
+ # @option options [ Integer ] :max_await_time_ms The maximum amount of time
320
+ # for the server to wait on new documents to satisfy a change stream query.
321
+ # @option options [ Integer ] :batch_size The number of documents to return
322
+ # per batch.
320
323
  # @option options [ BSON::Document, Hash ] :collation The collation to use.
321
324
  # @option options [ Session ] :session The session to use.
325
+ # @option options [ BSON::Timestamp ] :start_at_operation_time Only return
326
+ # changes that occurred at or after the specified timestamp. Any command run
327
+ # against the server will return a cluster time that can be used here.
328
+ # Only recognized by server versions 4.0+.
322
329
  #
323
330
  # @note A change stream only allows 'majority' read concern.
324
- # @note This helper method is preferable to running a raw aggregation with a $changeStream stage,
325
- # for the purpose of supporting resumability.
331
+ # @note This helper method is preferable to running a raw aggregation with
332
+ # a $changeStream stage, for the purpose of supporting resumability.
326
333
  #
327
334
  # @return [ ChangeStream ] The change stream object.
328
335
  #
329
336
  # @since 2.5.0
330
337
  def watch(pipeline = [], options = {})
331
- View::ChangeStream.new(View.new(self, {}, options), pipeline, options)
338
+ View::ChangeStream.new(View.new(self, {}, options), pipeline, nil, options)
332
339
  end
333
340
 
334
- # Get a count of matching documents in the collection.
341
+ # Gets the number of matching documents in the collection.
335
342
  #
336
343
  # @example Get the count.
337
344
  # collection.count(name: 1)
@@ -350,10 +357,59 @@ module Mongo
350
357
  # @return [ Integer ] The document count.
351
358
  #
352
359
  # @since 2.1.0
360
+ #
361
+ # @deprecated Use #count_documents or estimated_document_count instead. However, note that the
362
+ # following operators will need to be substituted when switching to #count_documents:
363
+ # * $where should be replaced with $expr (only works on 3.6+)
364
+ # * $near should be replaced with $geoWithin with $center
365
+ # * $nearSphere should be replaced with $geoWithin with $centerSphere
353
366
  def count(filter = nil, options = {})
354
367
  View.new(self, filter || {}, options).count(options)
355
368
  end
356
369
 
370
+ # Gets the number of of matching documents in the collection. Unlike the deprecated #count
371
+ # method, this will return the exact number of documents matching the filter rather than the estimate.
372
+ #
373
+ # @example Get the number of documents in the collection.
374
+ # collection_view.count_documents
375
+ #
376
+ # @param [ Hash ] filter A filter for matching documents.
377
+ # @param [ Hash ] options Options for the operation.
378
+ #
379
+ # @option opts :skip [ Integer ] The number of documents to skip.
380
+ # @option opts :hint [ Hash ] Override default index selection and force
381
+ # MongoDB to use a specific index for the query. Requires server version 3.6+.
382
+ # @option opts :limit [ Integer ] Max number of docs to count.
383
+ # @option opts :max_time_ms [ Integer ] The maximum amount of time to allow the
384
+ # command to run.
385
+ # @option opts [ Hash ] :read The read preference options.
386
+ # @option opts [ Hash ] :collation The collation to use.
387
+ #
388
+ # @return [ Integer ] The document count.
389
+ #
390
+ # @since 2.6.0
391
+ def count_documents(filter, options = {})
392
+ View.new(self, filter, options).count_documents(options)
393
+ end
394
+
395
+ # Gets an estimate of the count of documents in a collection using collection metadata.
396
+ #
397
+ # @example Get the number of documents in the collection.
398
+ # collection_view.estimated_document_count
399
+ #
400
+ # @param [ Hash ] options Options for the operation.
401
+ #
402
+ # @option opts :max_time_ms [ Integer ] The maximum amount of time to allow the command to
403
+ # run.
404
+ # @option opts [ Hash ] :read The read preference options.
405
+ #
406
+ # @return [ Integer ] The document count.
407
+ #
408
+ # @since 2.6.0
409
+ def estimated_document_count(options = {})
410
+ View.new(self, {}, options).estimated_document_count(options)
411
+ end
412
+
357
413
  # Get a list of distinct values for a specific field.
358
414
  #
359
415
  # @example Get the distinct values.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014-2017 MongoDB, Inc.
1
+ # Copyright (C) 2014-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -29,8 +29,8 @@ module Mongo
29
29
  MAPPINGS = BSON::Document.new(
30
30
  :allow_disk_use => 'allowDiskUse',
31
31
  :max_time_ms => 'maxTimeMS',
32
- # This is intentional; max_await_time_ms is an alias for maxTimeMS used on getmore
33
- # commands for change streams.
32
+ # This is intentional; max_await_time_ms is an alias for maxTimeMS
33
+ # used on getMore commands for change streams.
34
34
  :max_await_time_ms => 'maxTimeMS',
35
35
  :explain => 'explain',
36
36
  :bypass_document_validation => 'bypassDocumentValidation',
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2017 MongoDB, Inc.
1
+ # Copyright (C) 2015-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2017 MongoDB, Inc.
1
+ # Copyright (C) 2017-2018 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -19,14 +19,16 @@ module Mongo
19
19
  class View
20
20
 
21
21
  # Provides behaviour around a `$changeStream` pipeline stage in the
22
- # aggregation framework. Specifying this stage allows users to request that
23
- # notifications are sent for all changes to a particular collection or database.
22
+ # aggregation framework. Specifying this stage allows users to request
23
+ # that notifications are sent for all changes to a particular collection
24
+ # or database.
24
25
  #
25
26
  # @note Only available in server versions 3.6 and higher.
26
- # @note ChangeStreams do not work properly with JRuby because of the issue documented
27
- # here: https://github.com/jruby/jruby/issues/4212
28
- # Namely, JRuby eagerly evaluates #next on an Enumerator in a background green thread.
29
- # So calling #next on the change stream will cause getmores to be called in a loop in the background.
27
+ # @note ChangeStreams do not work properly with JRuby because of the
28
+ # issue documented here: https://github.com/jruby/jruby/issues/4212.
29
+ # Namely, JRuby eagerly evaluates #next on an Enumerator in a background
30
+ # green thread, therefore calling #next on the change stream will cause
31
+ # getMores to be called in a loop in the background.
30
32
  #
31
33
  #
32
34
  # @since 2.5.0
@@ -38,6 +40,18 @@ module Mongo
38
40
  # @since 2.5.0
39
41
  FULL_DOCUMENT_DEFAULT = 'default'.freeze
40
42
 
43
+ # @return [ Symbol ] Used to indicate that the change stream should listen for changes on
44
+ # the entire database rather than just the collection.
45
+ #
46
+ # @since 2.6.0
47
+ DATABASE = :database
48
+
49
+ # @return [ Symbol ] Used to indicate that the change stream should listen for changes on
50
+ # the entire cluster rather than just the collection.
51
+ #
52
+ # @since 2.6.0
53
+ CLUSTER = :cluster
54
+
41
55
  # @return [ BSON::Document ] The change stream options.
42
56
  #
43
57
  # @since 2.5.0
@@ -53,8 +67,8 @@ module Mongo
53
67
  # @param [ Array<Hash> ] pipeline The pipeline of operators to filter the change notifications.
54
68
  # @param [ Hash ] options The change stream options.
55
69
  #
56
- # @option options [ String ] :full_document Allowed values: default’, updateLookup’. Defaults to default’.
57
- # When set to updateLookup’, the change notification for partial updates will include both a delta
70
+ # @option options [ String ] :full_document Allowed values: 'default', 'updateLookup'. Defaults to 'default'.
71
+ # When set to 'updateLookup', the change notification for partial updates will include both a delta
58
72
  # describing the changes to the document, as well as a copy of the entire document that was changed
59
73
  # from some time after the change occurred.
60
74
  # @option options [ BSON::Document, Hash ] :resume_after Specifies the logical starting point for the
@@ -63,18 +77,31 @@ module Mongo
63
77
  # on new documents to satisfy a change stream query.
64
78
  # @option options [ Integer ] :batch_size The number of documents to return per batch.
65
79
  # @option options [ BSON::Document, Hash ] :collation The collation to use.
80
+ # @option options [ BSON::Timestamp ] :start_at_operation_time Only
81
+ # return changes that occurred at or after the specified timestamp. Any
82
+ # command run against the server will return a cluster time that can
83
+ # be used here. Only recognized by server versions 4.0+.
66
84
  #
67
85
  # @since 2.5.0
68
- def initialize(view, pipeline, options = {})
86
+ def initialize(view, pipeline, changes_for, options = {})
69
87
  @view = view
88
+ @changes_for = changes_for
70
89
  @change_stream_filters = pipeline && pipeline.dup
71
90
  @options = options && options.dup.freeze
72
91
  @resume_token = @options[:resume_after]
73
- read_with_one_retry { create_cursor! }
92
+ create_cursor!
93
+
94
+ # We send different parameters when we resume a change stream
95
+ # compared to when we send the first query
96
+ @resuming = true
74
97
  end
75
98
 
76
99
  # Iterate through documents returned by the change stream.
77
100
  #
101
+ # This method retries once per error on resumable errors
102
+ # (two consecutive errors result in the second error being raised,
103
+ # an error which is recovered from resets the error count to zero).
104
+ #
78
105
  # @example Iterate through the stream of documents.
79
106
  # stream.each do |document|
80
107
  # p document
@@ -87,20 +114,82 @@ module Mongo
87
114
  # @yieldparam [ BSON::Document ] Each change stream document.
88
115
  def each
89
116
  raise StopIteration.new if closed?
117
+ retried = false
90
118
  begin
91
119
  @cursor.each do |doc|
92
120
  cache_resume_token(doc)
93
121
  yield doc
94
122
  end if block_given?
95
123
  @cursor.to_enum
96
- rescue => e
124
+ rescue Mongo::Error => e
125
+ if retried || !e.change_stream_resumable?
126
+ raise
127
+ end
128
+
129
+ retried = true
130
+ # Rerun initial aggregation.
131
+ # Any errors here will stop iteration and break out of this
132
+ # method
97
133
  close
98
- if retryable?(e)
134
+ create_cursor!
135
+ retry
136
+ end
137
+ end
138
+
139
+ # Return one document from the change stream, if one is available.
140
+ #
141
+ # Retries once on a resumable error.
142
+ #
143
+ # Raises StopIteration if the change stream is closed.
144
+ #
145
+ # This method will wait up to max_await_time_ms milliseconds
146
+ # for changes from the server, and if no changes are received
147
+ # it will return nil.
148
+ #
149
+ # @note This method is experimental and subject to change.
150
+ #
151
+ # @return [ BSON::Document | nil ] A change stream document.
152
+ # @api private
153
+ def try_next
154
+ raise StopIteration.new if closed?
155
+ retried = false
156
+
157
+ begin
158
+ doc = @cursor.try_next
159
+ rescue Mongo::Error => e
160
+ unless e.change_stream_resumable?
161
+ raise
162
+ end
163
+
164
+ if retried
165
+ # Rerun initial aggregation.
166
+ # Any errors here will stop iteration and break out of this
167
+ # method
168
+ close
99
169
  create_cursor!
170
+ retried = false
171
+ else
172
+ # Attempt to retry a getMore once
173
+ retried = true
100
174
  retry
101
175
  end
102
- raise
103
176
  end
177
+
178
+ if doc
179
+ cache_resume_token(doc)
180
+ end
181
+ doc
182
+ end
183
+
184
+ def to_enum
185
+ enum = super
186
+ enum.send(:instance_variable_set, '@obj', self)
187
+ class << enum
188
+ def try_next
189
+ @obj.try_next
190
+ end
191
+ end
192
+ enum
104
193
  end
105
194
 
106
195
  # Close the change stream.
@@ -145,28 +234,115 @@ module Mongo
145
234
 
146
235
  private
147
236
 
237
+ def for_cluster?
238
+ @changes_for == CLUSTER
239
+ end
240
+
241
+ def for_database?
242
+ @changes_for == DATABASE
243
+ end
244
+
245
+ def for_collection?
246
+ !for_cluster? && !for_database?
247
+ end
248
+
148
249
  def cache_resume_token(doc)
250
+ # Always record both resume token and operation time,
251
+ # in case we get an older or newer server during rolling
252
+ # upgrades/downgrades
149
253
  unless @resume_token = (doc[:_id] && doc[:_id].dup)
150
- raise Error::MissingResumeToken.new
254
+ raise Error::MissingResumeToken
151
255
  end
152
256
  end
153
257
 
154
258
  def create_cursor!
259
+ # clear the cache because we may get a newer or an older server
260
+ # (rolling upgrades)
261
+ @start_at_operation_time_supported = nil
262
+
155
263
  session = client.send(:get_session, @options)
156
264
  server = server_selector.select_server(cluster)
157
265
  result = send_initial_query(server, session)
266
+ if doc = result.replies.first && result.replies.first.documents.first
267
+ @start_at_operation_time = doc['operationTime']
268
+ else
269
+ # The above may set @start_at_operation_time to nil
270
+ # if it was not in the document for some reason,
271
+ # for consistency set it to nil here as well
272
+ @start_at_operation_time = nil
273
+ end
158
274
  @cursor = Cursor.new(view, result, server, disable_retry: true, session: session)
159
275
  end
160
276
 
161
277
  def pipeline
162
- change_doc = { fullDocument: ( @options[:full_document] || FULL_DOCUMENT_DEFAULT ) }
163
- change_doc[:resumeAfter] = @resume_token if @resume_token
164
278
  [{ '$changeStream' => change_doc }] + @change_stream_filters
165
279
  end
166
280
 
281
+ def aggregate_spec(session)
282
+ super(session).tap do |spec|
283
+ spec[:selector][:aggregate] = 1 unless for_collection?
284
+ end
285
+ end
286
+
287
+ def change_doc
288
+ { fullDocument: ( @options[:full_document] || FULL_DOCUMENT_DEFAULT ) }.tap do |doc|
289
+ if resuming?
290
+ # We have a resume token once we retrieved any documents.
291
+ # However, if the first getMore fails and the user didn't pass
292
+ # a resume token we won't have a resume token to use.
293
+ # Use start_at_operation time in this case
294
+ if @resume_token
295
+ # Spec says we need to remove startAtOperationTime if
296
+ # one was passed in by user, thus we won't forward it
297
+ elsif start_at_operation_time_supported? && @start_at_operation_time
298
+ # It is crucial to check @start_at_operation_time_supported
299
+ # here - we may have switched to an older server that
300
+ # does not support operation times and therefore shouldn't
301
+ # try to send one to it!
302
+ #
303
+ # @start_at_operation_time is already a BSON::Timestamp
304
+ doc[:startAtOperationTime] = @start_at_operation_time
305
+ else
306
+ # Can't resume if we don't have either
307
+ raise Mongo::Error::MissingResumeToken
308
+ end
309
+ else
310
+ if options[:start_at_operation_time]
311
+ doc[:startAtOperationTime] = time_to_bson_timestamp(
312
+ options[:start_at_operation_time])
313
+ end
314
+ end
315
+ doc[:resumeAfter] = @resume_token if @resume_token
316
+ doc[:allChangesForCluster] = true if for_cluster?
317
+ end
318
+ end
319
+
167
320
  def send_initial_query(server, session)
168
321
  initial_query_op(session).execute(server)
169
322
  end
323
+
324
+ def time_to_bson_timestamp(time)
325
+ if time.is_a?(Time)
326
+ seconds = time.to_f
327
+ BSON::Timestamp.new(seconds.to_i, ((seconds - seconds.to_i) * 1000000).to_i)
328
+ elsif time.is_a?(BSON::Timestamp)
329
+ time
330
+ else
331
+ raise ArgumentError, 'Time must be a Time or a BSON::Timestamp instance'
332
+ end
333
+ end
334
+
335
+ def resuming?
336
+ !!@resuming
337
+ end
338
+
339
+ def start_at_operation_time_supported?
340
+ if @start_at_operation_time_supported.nil?
341
+ server = server_selector.select_server(cluster)
342
+ @start_at_operation_time_supported = server.description.max_wire_version >= 7
343
+ end
344
+ @start_at_operation_time_supported
345
+ end
170
346
  end
171
347
  end
172
348
  end