mongo 2.5.0.beta → 2.5.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 (172) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/address.rb +1 -1
  5. data/lib/mongo/address/unix.rb +1 -1
  6. data/lib/mongo/auth/user.rb +0 -5
  7. data/lib/mongo/auth/user/view.rb +4 -4
  8. data/lib/mongo/bulk_write.rb +60 -32
  9. data/lib/mongo/client.rb +44 -8
  10. data/lib/mongo/cluster.rb +14 -12
  11. data/lib/mongo/cluster/periodic_executor.rb +106 -0
  12. data/lib/mongo/cluster/{cursor_reaper.rb → reapers/cursor_reaper.rb} +5 -37
  13. data/lib/mongo/cluster/reapers/socket_reaper.rb +59 -0
  14. data/lib/mongo/collection.rb +9 -6
  15. data/lib/mongo/collection/view.rb +2 -2
  16. data/lib/mongo/collection/view/builder/aggregation.rb +2 -1
  17. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  18. data/lib/mongo/collection/view/change_stream.rb +14 -1
  19. data/lib/mongo/collection/view/map_reduce.rb +30 -13
  20. data/lib/mongo/collection/view/readable.rb +5 -5
  21. data/lib/mongo/collection/view/writable.rb +98 -51
  22. data/lib/mongo/error.rb +3 -0
  23. data/lib/mongo/error/invalid_txt_record.rb +27 -0
  24. data/lib/mongo/error/invalid_uri.rb +7 -6
  25. data/lib/mongo/error/mismatched_domain.rb +27 -0
  26. data/lib/mongo/error/no_srv_records.rb +26 -0
  27. data/lib/mongo/error/unsupported_features.rb +0 -18
  28. data/lib/mongo/index/view.rb +2 -2
  29. data/lib/mongo/operation.rb +1 -0
  30. data/lib/mongo/operation/causally_consistent.rb +33 -0
  31. data/lib/mongo/operation/commands.rb +2 -1
  32. data/lib/mongo/operation/commands/aggregate.rb +2 -7
  33. data/lib/mongo/operation/commands/count.rb +27 -0
  34. data/lib/mongo/operation/commands/distinct.rb +27 -0
  35. data/lib/mongo/operation/commands/find.rb +3 -1
  36. data/lib/mongo/operation/commands/map_reduce.rb +1 -0
  37. data/lib/mongo/operation/commands/parallel_scan.rb +1 -0
  38. data/lib/mongo/operation/specifiable.rb +12 -0
  39. data/lib/mongo/operation/uses_command_op_msg.rb +36 -5
  40. data/lib/mongo/operation/write.rb +0 -5
  41. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -8
  42. data/lib/mongo/operation/write/bulk/mergable.rb +2 -0
  43. data/lib/mongo/operation/write/command/create_index.rb +19 -0
  44. data/lib/mongo/operation/write/command/create_user.rb +19 -0
  45. data/lib/mongo/operation/write/command/delete.rb +1 -2
  46. data/lib/mongo/operation/write/command/drop_index.rb +19 -0
  47. data/lib/mongo/operation/write/command/insert.rb +1 -2
  48. data/lib/mongo/operation/write/command/remove_user.rb +19 -0
  49. data/lib/mongo/operation/write/command/update.rb +1 -2
  50. data/lib/mongo/operation/write/command/update_user.rb +19 -0
  51. data/lib/mongo/operation/write/write_command_enabled.rb +1 -3
  52. data/lib/mongo/protocol/compressed.rb +2 -1
  53. data/lib/mongo/protocol/serializers.rb +6 -6
  54. data/lib/mongo/retryable.rb +48 -5
  55. data/lib/mongo/server.rb +15 -0
  56. data/lib/mongo/server/connection.rb +21 -1
  57. data/lib/mongo/server/connection_pool.rb +3 -0
  58. data/lib/mongo/server/connection_pool/queue.rb +50 -5
  59. data/lib/mongo/server/description.rb +11 -3
  60. data/lib/mongo/server/description/features.rb +26 -7
  61. data/lib/mongo/session.rb +133 -6
  62. data/lib/mongo/session/server_session.rb +30 -0
  63. data/lib/mongo/session/session_pool.rb +20 -20
  64. data/lib/mongo/uri.rb +88 -44
  65. data/lib/mongo/uri/srv_protocol.rb +158 -0
  66. data/lib/mongo/version.rb +1 -1
  67. data/lib/mongo/write_concern/normalizable.rb +12 -0
  68. data/mongo.gemspec +1 -2
  69. data/spec/mongo/address_spec.rb +12 -0
  70. data/spec/mongo/auth/user/view_spec.rb +1 -5
  71. data/spec/mongo/bulk_write_spec.rb +232 -401
  72. data/spec/mongo/change_stream_examples_spec.rb +150 -0
  73. data/spec/mongo/client_spec.rb +142 -2
  74. data/spec/mongo/cluster/cursor_reaper_spec.rb +0 -70
  75. data/spec/mongo/cluster/socket_reaper_spec.rb +32 -0
  76. data/spec/mongo/cluster_spec.rb +11 -7
  77. data/spec/mongo/collection/view/aggregation_spec.rb +46 -1
  78. data/spec/mongo/collection/view/builder/find_command_spec.rb +15 -0
  79. data/spec/mongo/collection/view/change_stream_spec.rb +79 -12
  80. data/spec/mongo/collection/view/map_reduce_spec.rb +120 -4
  81. data/spec/mongo/collection/view/readable_spec.rb +23 -5
  82. data/spec/mongo/collection_spec.rb +292 -102
  83. data/spec/mongo/command_monitoring_spec.rb +26 -32
  84. data/spec/mongo/crud_spec.rb +1 -1
  85. data/spec/mongo/cursor_spec.rb +2 -3
  86. data/spec/mongo/database_spec.rb +30 -14
  87. data/spec/mongo/dns_seedlist_discovery_spec.rb +94 -0
  88. data/spec/mongo/grid/fs_bucket_spec.rb +1 -1
  89. data/spec/mongo/grid/stream/write_spec.rb +1 -1
  90. data/spec/mongo/index/view_spec.rb +8 -46
  91. data/spec/mongo/operation/write/bulk/delete_spec.rb +2 -2
  92. data/spec/mongo/operation/write/bulk/insert_spec.rb +2 -10
  93. data/spec/mongo/operation/write/{create_index_spec.rb → command/create_index_spec.rb} +2 -6
  94. data/spec/mongo/operation/write/command/delete_spec.rb +35 -7
  95. data/spec/mongo/operation/write/{drop_index_spec.rb → command/drop_index_spec.rb} +1 -1
  96. data/spec/mongo/operation/write/command/insert_spec.rb +37 -6
  97. data/spec/mongo/operation/write/{remove_user_spec.rb → command/remove_user_spec.rb} +2 -6
  98. data/spec/mongo/operation/write/command/update_spec.rb +34 -7
  99. data/spec/mongo/operation/write/{update_user_spec.rb → command/update_user_spec.rb} +1 -1
  100. data/spec/mongo/operation/write/create_user_spec.rb +1 -1
  101. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  102. data/spec/mongo/operation/write/insert_spec.rb +2 -10
  103. data/spec/mongo/operation/write/update_spec.rb +3 -15
  104. data/spec/mongo/retryable_spec.rb +1 -1
  105. data/spec/mongo/retryable_writes_spec.rb +815 -0
  106. data/spec/mongo/server/connection_pool/queue_spec.rb +35 -2
  107. data/spec/mongo/server/connection_pool_spec.rb +234 -1
  108. data/spec/mongo/server/connection_spec.rb +10 -6
  109. data/spec/mongo/server/description/features_spec.rb +51 -37
  110. data/spec/mongo/server/description_spec.rb +6 -3
  111. data/spec/mongo/server_spec.rb +87 -0
  112. data/spec/mongo/session/server_session_spec.rb +43 -0
  113. data/spec/mongo/session/session_pool_spec.rb +63 -27
  114. data/spec/mongo/session_spec.rb +247 -0
  115. data/spec/mongo/shell_examples_spec.rb +2 -2
  116. data/spec/mongo/uri/srv_protocol_spec.rb +933 -0
  117. data/spec/mongo/uri_spec.rb +42 -3
  118. data/spec/mongo/write_concern/acknowledged_spec.rb +11 -0
  119. data/spec/mongo/write_concern/unacknowledged_spec.rb +11 -0
  120. data/spec/spec_helper.rb +11 -25
  121. data/spec/support/authorization.rb +2 -1
  122. data/spec/support/connection_string.rb +8 -4
  123. data/spec/support/crud.rb +38 -24
  124. data/spec/support/crud/write.rb +30 -3
  125. data/spec/support/crud_tests/read/aggregate-out.yml +21 -0
  126. data/spec/support/crud_tests/write/bulkWrite-arrayFilters.yml +44 -0
  127. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +1 -1
  128. data/spec/support/crud_tests/write/insertMany.yml +1 -3
  129. data/spec/support/crud_tests/write/replaceOne.yml +1 -1
  130. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +1 -1
  131. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +1 -1
  132. data/spec/support/dns_seedlist_discovery_tests/longer-parent-in-return.yml +11 -0
  133. data/spec/support/dns_seedlist_discovery_tests/misformatted-option.yml +5 -0
  134. data/spec/support/dns_seedlist_discovery_tests/no-results.yml +5 -0
  135. data/spec/support/dns_seedlist_discovery_tests/not-enough-parts.yml +5 -0
  136. data/spec/support/dns_seedlist_discovery_tests/one-result-default-port.yml +10 -0
  137. data/spec/support/dns_seedlist_discovery_tests/one-txt-record-multiple-strings.yml +10 -0
  138. data/spec/support/dns_seedlist_discovery_tests/one-txt-record.yml +11 -0
  139. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch1.yml +5 -0
  140. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch2.yml +5 -0
  141. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch3.yml +5 -0
  142. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch4.yml +5 -0
  143. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch5.yml +5 -0
  144. data/spec/support/dns_seedlist_discovery_tests/returned-parent-too-short.yml +5 -0
  145. data/spec/support/dns_seedlist_discovery_tests/returned-parent-wrong.yml +5 -0
  146. data/spec/support/dns_seedlist_discovery_tests/two-results-default-port.yml +11 -0
  147. data/spec/support/dns_seedlist_discovery_tests/two-results-nonstandard-port.yml +11 -0
  148. data/spec/support/dns_seedlist_discovery_tests/two-txt-records.yml +5 -0
  149. data/spec/support/dns_seedlist_discovery_tests/txt-record-not-allowed-option.yml +5 -0
  150. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-ssl-option.yml +11 -0
  151. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-uri-option.yml +11 -0
  152. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-unallowed-option.yml +5 -0
  153. data/spec/support/dns_seedlist_discovery_tests/uri-with-port.yml +5 -0
  154. data/spec/support/dns_seedlist_discovery_tests/uri-with-two-hosts.yml +5 -0
  155. data/spec/support/retryable_writes_tests/bulkWrite.yml +305 -0
  156. data/spec/support/retryable_writes_tests/deleteOne.yml +51 -0
  157. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +52 -0
  158. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +57 -0
  159. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +56 -0
  160. data/spec/support/retryable_writes_tests/insertMany.yml +72 -0
  161. data/spec/support/retryable_writes_tests/insertOne.yml +55 -0
  162. data/spec/support/retryable_writes_tests/replaceOne.yml +60 -0
  163. data/spec/support/retryable_writes_tests/updateOne.yml +120 -0
  164. data/spec/support/shared/session.rb +525 -24
  165. metadata +437 -350
  166. metadata.gz.sig +0 -0
  167. data/lib/mongo/operation/commands/user_query.rb +0 -72
  168. data/lib/mongo/operation/write/create_index.rb +0 -67
  169. data/lib/mongo/operation/write/create_user.rb +0 -50
  170. data/lib/mongo/operation/write/drop_index.rb +0 -63
  171. data/lib/mongo/operation/write/remove_user.rb +0 -48
  172. data/lib/mongo/operation/write/update_user.rb +0 -50
@@ -34,6 +34,7 @@ module Mongo
34
34
  #
35
35
  # @since 2.0.0
36
36
  def aggregate_write_errors(count)
37
+ return unless @replies
37
38
  @replies.reduce(nil) do |errors, reply|
38
39
  if write_errors = reply.documents.first[Error::WRITE_ERRORS]
39
40
  wes = write_errors.collect do |we|
@@ -55,6 +56,7 @@ module Mongo
55
56
  #
56
57
  # @since 2.0.0
57
58
  def aggregate_write_concern_errors(count)
59
+ return unless @replies
58
60
  @replies.each_with_index.reduce(nil) do |errors, (reply, _)|
59
61
  if write_concern_errors = reply.documents.first[Error::WRITE_CONCERN_ERRORS]
60
62
  (errors || []) << write_concern_errors.reduce(nil) do |errs, wce|
@@ -33,6 +33,25 @@ module Mongo
33
33
  include TakesWriteConcern
34
34
  include UsesCommandOpMsg
35
35
 
36
+ # Execute the operation.
37
+ #
38
+ # @example Execute the operation.
39
+ # operation.execute(server)
40
+ #
41
+ # @param [ Mongo::Server ] server The server to send this operation to.
42
+ #
43
+ # @return [ Result ] The operation response, if there is one.
44
+ #
45
+ # @since 2.5.0
46
+ def execute(server)
47
+ result = Result.new(server.with_connection do |connection|
48
+ connection.dispatch([ message(server) ], operation_id)
49
+ end)
50
+ server.update_cluster_time(result)
51
+ session.process(result) if session
52
+ result.validate!
53
+ end
54
+
36
55
  private
37
56
 
38
57
  # The query selector for this ensure index command operation.
@@ -25,6 +25,25 @@ module Mongo
25
25
  include Specifiable
26
26
  include Writable
27
27
 
28
+ # Execute the operation.
29
+ #
30
+ # @example Execute the operation.
31
+ # operation.execute(server)
32
+ #
33
+ # @param [ Mongo::Server ] server The server to send this operation to.
34
+ #
35
+ # @return [ Result ] The operation response, if there is one.
36
+ #
37
+ # @since 2.5.0
38
+ def execute(server)
39
+ result = Result.new(server.with_connection do |connection|
40
+ connection.dispatch([ message(server) ], operation_id)
41
+ end)
42
+ server.update_cluster_time(result)
43
+ session.process(result) if session
44
+ result.validate!
45
+ end
46
+
28
47
  private
29
48
 
30
49
  # The query selector for this create user command operation.
@@ -52,8 +52,7 @@ module Mongo
52
52
  global_args = { delete: coll_name,
53
53
  Protocol::Msg::DATABASE_IDENTIFIER => db_name
54
54
  }.merge!(command_options)
55
- add_cluster_time!(global_args, server)
56
- session.add_id!(global_args) if session
55
+ update_selector_for_session!(global_args, server)
57
56
 
58
57
  section = { type: 1, payload: { identifier: IDENTIFIER, sequence: deletes } }
59
58
  flags = unacknowledged_write? ? [:more_to_come] : [:none]
@@ -34,6 +34,25 @@ module Mongo
34
34
  include TakesWriteConcern
35
35
  include UsesCommandOpMsg
36
36
 
37
+ # Execute the operation.
38
+ #
39
+ # @example Execute the operation.
40
+ # operation.execute(server)
41
+ #
42
+ # @param [ Mongo::Server ] server The server to send this operation to.
43
+ #
44
+ # @return [ Result ] The operation response, if there is one.
45
+ #
46
+ # @since 2.5.0
47
+ def execute(server)
48
+ result = Result.new(server.with_connection do |connection|
49
+ connection.dispatch([ message(server) ], operation_id)
50
+ end)
51
+ server.update_cluster_time(result)
52
+ session.process(result) if session
53
+ result.validate!
54
+ end
55
+
37
56
  private
38
57
 
39
58
  # The query selector for this drop index command operation.
@@ -46,8 +46,7 @@ module Mongo
46
46
  global_args = { insert: coll_name,
47
47
  Protocol::Msg::DATABASE_IDENTIFIER => db_name
48
48
  }.merge!(command_options)
49
- add_cluster_time!(global_args, server)
50
- session.add_id!(global_args) if session
49
+ update_selector_for_session!(global_args, server)
51
50
 
52
51
  section = { type: 1, payload: { identifier: IDENTIFIER, sequence: documents } }
53
52
  flags = unacknowledged_write? ? [:more_to_come] : [:none]
@@ -24,6 +24,25 @@ module Mongo
24
24
  include Specifiable
25
25
  include Writable
26
26
 
27
+ # Execute the operation.
28
+ #
29
+ # @example Execute the operation.
30
+ # operation.execute(server)
31
+ #
32
+ # @param [ Mongo::Server ] server The server to send this operation to.
33
+ #
34
+ # @return [ Result ] The operation response, if there is one.
35
+ #
36
+ # @since 2.5.0
37
+ def execute(server)
38
+ result = Result.new(server.with_connection do |connection|
39
+ connection.dispatch([ message(server) ], operation_id)
40
+ end)
41
+ server.update_cluster_time(result)
42
+ session.process(result) if session
43
+ result.validate!
44
+ end
45
+
27
46
  private
28
47
 
29
48
  # The query selector for this drop user command operation.
@@ -55,8 +55,7 @@ module Mongo
55
55
  global_args = { update: coll_name,
56
56
  Protocol::Msg::DATABASE_IDENTIFIER => db_name
57
57
  }.merge!(command_options)
58
- add_cluster_time!(global_args, server)
59
- session.add_id!(global_args) if session
58
+ update_selector_for_session!(global_args, server)
60
59
 
61
60
  section = { type: 1, payload: { identifier: IDENTIFIER, sequence: updates } }
62
61
  flags = unacknowledged_write? ? [:more_to_come] : [:none]
@@ -25,6 +25,25 @@ module Mongo
25
25
  include Specifiable
26
26
  include Writable
27
27
 
28
+ # Execute the operation.
29
+ #
30
+ # @example Execute the operation.
31
+ # operation.execute(server)
32
+ #
33
+ # @param [ Mongo::Server ] server The server to send this operation to.
34
+ #
35
+ # @return [ Result ] The operation response, if there is one.
36
+ #
37
+ # @since 2.5.0
38
+ def execute(server)
39
+ result = Result.new(server.with_connection do |connection|
40
+ connection.dispatch([ message(server) ], operation_id)
41
+ end)
42
+ server.update_cluster_time(result)
43
+ session.process(result) if session
44
+ result.validate!
45
+ end
46
+
28
47
  private
29
48
 
30
49
  # The query selector for this update user command operation.
@@ -39,9 +39,7 @@ module Mongo
39
39
  raise Error::UnsupportedArrayFilters.new(Error::UnsupportedArrayFilters::UNACKNOWLEDGED_WRITES_MESSAGE) if has_array_filters?
40
40
  end
41
41
 
42
- if !server.features.write_command_enabled? # version < 2.4
43
- execute_message(server)
44
- elsif server.features.op_msg_enabled? # version 3.6
42
+ if server.features.op_msg_enabled? # version 3.6
45
43
  execute_write_command(server)
46
44
  else # server version is 2.6 through 3.4
47
45
  if unacknowledged_write?
@@ -48,7 +48,8 @@ module Mongo
48
48
  #
49
49
  # @param [ Mongo::Protocol::Message ] message The original message.
50
50
  # @param [ String, Symbol ] compressor The compression algorithm to use.
51
- # @param [ Integer ] level The zlib compression level to use. -1 and nil imply default.
51
+ # @param [ Integer ] zlib_compression_level The zlib compression level to use.
52
+ # -1 and nil imply default.
52
53
  #
53
54
  # @since 2.5.0
54
55
  def initialize(message, compressor, zlib_compression_level = nil)
@@ -355,9 +355,9 @@ module Mongo
355
355
 
356
356
  # Writes a byte into the buffer.
357
357
  #
358
- # @param buffer [ BSON::ByteBuffer ] buffer Buffer to receive the single byte.
359
- # @param value [ String ] value The byte to write to the buffer.
360
- # @param value [ true, false ] validating_keys Whether to validate keys.
358
+ # @param [ BSON::ByteBuffer ] buffer Buffer to receive the single byte.
359
+ # @param [ String ] value The byte to write to the buffer.
360
+ # @param [ true, false ] validating_keys Whether to validate keys.
361
361
  #
362
362
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
363
363
  #
@@ -385,9 +385,9 @@ module Mongo
385
385
 
386
386
  # Writes bytes into the buffer.
387
387
  #
388
- # @param buffer [ BSON::ByteBuffer ] buffer Buffer to receive the bytes.
389
- # @param value [ String ] value The bytes to write to the buffer.
390
- # @param value [ true, false ] validating_keys Whether to validate keys.
388
+ # @param [ BSON::ByteBuffer ] buffer Buffer to receive the bytes.
389
+ # @param [ String ] value The bytes to write to the buffer.
390
+ # @param [ true, false ] validating_keys Whether to validate keys.
391
391
  #
392
392
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
393
393
  #
@@ -30,7 +30,6 @@ module Mongo
30
30
  #
31
31
  # @note This only retries read operations on socket errors.
32
32
  #
33
- # @param [ Integer ] attempt The retry attempt count - for internal use.
34
33
  # @param [ Proc ] block The block to execute.
35
34
  #
36
35
  # @yieldparam [ Server ] server The server to which the write should be sent.
@@ -99,12 +98,58 @@ module Mongo
99
98
  # @return [ Result ] The result of the operation.
100
99
  #
101
100
  # @since 2.1.0
102
- def write_with_retry(session, server_selector)
101
+ def write_with_retry(session, write_concern, &block)
102
+ unless retry_write_allowed?(session, write_concern)
103
+ return legacy_write_with_retry(&block)
104
+ end
105
+
106
+ server = cluster.next_primary
107
+ unless server.retry_writes?
108
+ return legacy_write_with_retry(server, &block)
109
+ end
110
+
111
+ begin
112
+ txn_num = session.next_txn_num
113
+ yield(server, txn_num)
114
+ rescue Error::SocketError, Error::SocketTimeoutError => e
115
+ retry_write(e, txn_num, &block)
116
+ rescue Error::OperationFailure => e
117
+ raise e unless e.write_retryable?
118
+ retry_write(e, txn_num, &block)
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def retry_write_allowed?(session, write_concern)
125
+ session && session.retry_writes? &&
126
+ (write_concern.nil? || write_concern.acknowledged?)
127
+ end
128
+
129
+ def retry_write(original_error, txn_num, &block)
130
+ cluster.scan!
131
+ server = cluster.next_primary
132
+ raise original_error unless (server.retry_writes? && txn_num)
133
+ log_retry(original_error)
134
+ yield(server, txn_num)
135
+ rescue Error::SocketError, Error::SocketTimeoutError => e
136
+ cluster.scan!
137
+ raise e
138
+ rescue Error::OperationFailure => e
139
+ raise original_error unless e.write_retryable?
140
+ cluster.scan!
141
+ raise e
142
+ rescue
143
+ raise original_error
144
+ end
145
+
146
+ def legacy_write_with_retry(server = nil)
103
147
  attempt = 0
104
148
  begin
105
149
  attempt += 1
106
- yield(server_selector.call)
150
+ yield(server || cluster.next_primary)
107
151
  rescue Error::OperationFailure => e
152
+ server = nil
108
153
  raise(e) if attempt > Cluster::MAX_WRITE_RETRIES
109
154
  if e.write_retryable?
110
155
  log_retry(e)
@@ -116,8 +161,6 @@ module Mongo
116
161
  end
117
162
  end
118
163
 
119
- private
120
-
121
164
  # Log a warning so that any application slow down is immediately obvious.
122
165
  def log_retry(e)
123
166
  Logger.logger.warn "Retry due to: #{e.class.name} #{e.message}"
data/lib/mongo/server.rb CHANGED
@@ -268,5 +268,20 @@ module Mongo
268
268
  unknown!
269
269
  raise
270
270
  end
271
+
272
+ # Will writes sent to this server be retried.
273
+ #
274
+ # @example Will writes be retried.
275
+ # server.retry_writes?
276
+ #
277
+ # @return [ true, false ] If writes will be retried.
278
+ #
279
+ # @note Retryable writes are only available on server versions 3.6+ and with
280
+ # sharded clusters or replica sets.
281
+ #
282
+ # @since 2.5.0
283
+ def retry_writes?
284
+ !!(features.sessions_enabled? && logical_session_timeout && !standalone?)
285
+ end
271
286
  end
272
287
  end
@@ -54,6 +54,11 @@ module Mongo
54
54
  # @since 2.5.0
55
55
  PING_OP_MSG_BYTES = PING_OP_MSG_MESSAGE.serialize.to_s.freeze
56
56
 
57
+ # The last time the connection was checked back into a pool.
58
+ #
59
+ # @since 2.5.0
60
+ attr_reader :last_checkin
61
+
57
62
  def_delegators :@server,
58
63
  :features,
59
64
  :max_bson_object_size,
@@ -97,9 +102,10 @@ module Mongo
97
102
  #
98
103
  # @since 2.0.0
99
104
  def disconnect!
105
+ @auth_mechanism = nil
106
+ @last_checkin = nil
100
107
  if socket
101
108
  socket.close
102
- @auth_mechanism = nil
103
109
  @socket = nil
104
110
  end
105
111
  true
@@ -151,6 +157,7 @@ module Mongo
151
157
  @server = server
152
158
  @ssl_options = options.reject { |k, v| !k.to_s.start_with?(SSL) }
153
159
  @socket = nil
160
+ @last_checkin = nil
154
161
  @auth_mechanism = nil
155
162
  @pid = Process.pid
156
163
  end
@@ -189,6 +196,19 @@ module Mongo
189
196
  # @deprecated Please use :socket_timeout instead. Will be removed in 3.0.0
190
197
  alias :timeout :socket_timeout
191
198
 
199
+ # Record the last checkin time.
200
+ #
201
+ # @example Record the checkin time on this connection.
202
+ # connection.record_checkin!
203
+ #
204
+ # @return [ self ]
205
+ #
206
+ # @since 2.5.0
207
+ def record_checkin!
208
+ @last_checkin = Time.now
209
+ self
210
+ end
211
+
192
212
  private
193
213
 
194
214
  def deliver(messages)
@@ -22,10 +22,13 @@ module Mongo
22
22
  # @since 2.0.0
23
23
  class ConnectionPool
24
24
  include Loggable
25
+ extend Forwardable
25
26
 
26
27
  # @return [ Hash ] options The pool options.
27
28
  attr_reader :options
28
29
 
30
+ def_delegators :queue, :close_stale_sockets!
31
+
29
32
  # Check a connection back into the pool. Will pull the connection from a
30
33
  # thread local stack that should contain it after it was checked out.
31
34
  #
@@ -16,9 +16,8 @@ module Mongo
16
16
  class Server
17
17
  class ConnectionPool
18
18
 
19
- # A FIFO queue of connections to be used by the connection pool. This is
20
- # based on mperham's connection pool, implemented with a queue instead of a
21
- # stack.
19
+ # A LIFO queue of connections to be used by the connection pool. This is
20
+ # based on mperham's connection pool.
22
21
  #
23
22
  # @since 2.0.0
24
23
  class Queue
@@ -88,7 +87,7 @@ module Mongo
88
87
  # @since 2.0.0
89
88
  def enqueue(connection)
90
89
  mutex.synchronize do
91
- queue.unshift(connection)
90
+ queue.unshift(connection.record_checkin!)
92
91
  resource.broadcast
93
92
  end
94
93
  end
@@ -165,12 +164,58 @@ module Mongo
165
164
  @wait_timeout ||= options[:wait_queue_timeout] || WAIT_TIMEOUT
166
165
  end
167
166
 
167
+ # The maximum seconds a socket can remain idle since it has been checked in to the pool.
168
+ #
169
+ # @example Get the max idle time.
170
+ # queue.max_idle_time
171
+ #
172
+ # @return [ Float ] The max socket idle time in seconds.
173
+ #
174
+ # @since 2.5.0
175
+ def max_idle_time
176
+ @max_idle_time ||= options[:max_idle_time]
177
+ end
178
+
179
+ # Close sockets that have been open for longer than the max idle time, if the
180
+ # option is set.
181
+ #
182
+ # @example Close the stale sockets
183
+ # queue.close_stale_sockets!
184
+ #
185
+ # @since 2.5.0
186
+ def close_stale_sockets!
187
+ return unless max_idle_time
188
+
189
+ to_refresh = []
190
+ queue.each do |connection|
191
+ if last_checkin = connection.last_checkin
192
+ if (Time.now - last_checkin) > max_idle_time
193
+ to_refresh << connection
194
+ end
195
+ end
196
+ end
197
+
198
+ mutex.synchronize do
199
+ num_checked_out = @connections - queue.size
200
+ min_size_delta = [(min_size - num_checked_out), 0].max
201
+
202
+ to_refresh.each do |connection|
203
+ if queue.include?(connection)
204
+ connection.disconnect!
205
+ if queue.index(connection) < min_size_delta
206
+ begin; connection.connect!; rescue; end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+
168
213
  private
169
214
 
170
215
  def dequeue_connection
171
216
  deadline = Time.now + wait_timeout
172
217
  loop do
173
- return queue.pop unless queue.empty?
218
+ return queue.shift unless queue.empty?
174
219
  connection = create_connection
175
220
  return connection if connection
176
221
  wait_for_next!(deadline)