mongo 2.5.0.beta → 2.5.0

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