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
@@ -124,6 +124,11 @@ module Mongo
124
124
  # @since 2.0.0
125
125
  PRIMARY = 'ismaster'.freeze
126
126
 
127
+ # Constant for reading primary host field from config.
128
+ #
129
+ # @since 2.5.0
130
+ PRIMARY_HOST = 'primary'.freeze
131
+
127
132
  # Constant for reading secondary info from config.
128
133
  #
129
134
  # @since 2.0.0
@@ -169,7 +174,8 @@ module Mongo
169
174
  # @since 2.0.6
170
175
  EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME,
171
176
  LAST_WRITE,
172
- OPERATION_TIME ].freeze
177
+ OPERATION_TIME,
178
+ Operation::CLUSTER_TIME ].freeze
173
179
 
174
180
  # @return [ Address ] address The server's address.
175
181
  attr_reader :address
@@ -258,7 +264,7 @@ module Mongo
258
264
  def initialize(address, config = {}, average_round_trip_time = 0)
259
265
  @address = address
260
266
  @config = config
261
- @features = Features.new(wire_versions)
267
+ @features = Features.new(wire_versions, me)
262
268
  @average_round_trip_time = average_round_trip_time
263
269
  end
264
270
 
@@ -464,7 +470,9 @@ module Mongo
464
470
  #
465
471
  # @since 2.0.0
466
472
  def primary?
467
- !!config[PRIMARY] && !replica_set_name.nil?
473
+ !!config[PRIMARY] &&
474
+ (config[PRIMARY_HOST].nil? || config[PRIMARY_HOST] == address.to_s) &&
475
+ !replica_set_name.nil?
468
476
  end
469
477
 
470
478
  # Get the name of the replica set the server belongs to, returns nil if
@@ -38,10 +38,22 @@ module Mongo
38
38
  :users_info => 2
39
39
  }.freeze
40
40
 
41
+ # Error message if the server is too old for this version of the driver.
42
+ #
43
+ # @since 2.5.0
44
+ SERVER_TOO_OLD = "Server at (%s) reports wire version (%s), but this version of the Ruby driver " +
45
+ "requires at least (%s)."
46
+
47
+ # Error message if the driver is too old for the version of the server.
48
+ #
49
+ # @since 2.5.0
50
+ DRIVER_TOO_OLD = "Server at (%s) requires wire version (%s), but this version of the Ruby driver " +
51
+ "only supports up to (%s)."
52
+
41
53
  # The wire protocol versions that this version of the driver supports.
42
54
  #
43
55
  # @since 2.0.0
44
- DRIVER_WIRE_VERSIONS = (0..6).freeze
56
+ DRIVER_WIRE_VERSIONS = (2..6).freeze
45
57
 
46
58
  # Create the methods for each mapping to tell if they are supported.
47
59
  #
@@ -74,17 +86,24 @@ module Mongo
74
86
  # versions.
75
87
  #
76
88
  # @since 2.0.0
77
- def initialize(server_wire_versions)
89
+ def initialize(server_wire_versions, address = nil)
78
90
  @server_wire_versions = server_wire_versions
79
- check_driver_support!
91
+ check_driver_support!(address) unless server_wire_versions == ZERO_RANGE
80
92
  end
81
93
 
82
94
  private
83
95
 
84
- def check_driver_support!
85
- if DRIVER_WIRE_VERSIONS.max < server_wire_versions.min ||
86
- DRIVER_WIRE_VERSIONS.min > server_wire_versions.max
87
- raise Error::UnsupportedFeatures.new(server_wire_versions)
96
+ ZERO_RANGE = (0..0).freeze
97
+
98
+ def check_driver_support!(address)
99
+ if DRIVER_WIRE_VERSIONS.min > server_wire_versions.max
100
+ raise Error::UnsupportedFeatures.new(SERVER_TOO_OLD % [address,
101
+ server_wire_versions.max,
102
+ DRIVER_WIRE_VERSIONS.min])
103
+ elsif DRIVER_WIRE_VERSIONS.max < server_wire_versions.min
104
+ raise Error::UnsupportedFeatures.new(DRIVER_TOO_OLD % [address,
105
+ server_wire_versions.min,
106
+ DRIVER_WIRE_VERSIONS.max])
88
107
  end
89
108
  end
90
109
  end
data/lib/mongo/session.rb CHANGED
@@ -34,13 +34,23 @@ module Mongo
34
34
  # @since 2.5.0
35
35
  attr_reader :client
36
36
 
37
- def_delegators :@server_session, :session_id
37
+ # The cluster time for this session.
38
+ #
39
+ # @since 2.5.0
40
+ attr_reader :cluster_time
41
+
42
+ # The latest seen operation time for this session.
43
+ #
44
+ # @since 2.5.0
45
+ attr_reader :operation_time
46
+
47
+ def_delegators :client, :cluster
38
48
 
39
49
  # Error message describing that the session was attempted to be used by a client different from the
40
50
  # one it was originally associated with.
41
51
  #
42
52
  # @since 2.5.0
43
- MISTMATCHED_CLUSTER_ERROR_MSG = 'The client used to create this session does not match that of client ' +
53
+ MISMATCHED_CLUSTER_ERROR_MSG = 'The client used to create this session does not match that of client ' +
44
54
  'initiating this operation. Please only use this session for operations through its parent client.'.freeze
45
55
 
46
56
  # Error message describing that the session cannot be used because it has already been ended.
@@ -66,7 +76,20 @@ module Mongo
66
76
  def initialize(server_session, client, options = {})
67
77
  @server_session = server_session
68
78
  @client = client
69
- @options = options
79
+ @options = options.dup.freeze
80
+ @cluster_time = nil
81
+ end
82
+
83
+ # Get a formatted string for use in inspection.
84
+ #
85
+ # @example Inspect the session object.
86
+ # session.inspect
87
+ #
88
+ # @return [ String ] The session inspection.
89
+ #
90
+ # @since 2.5.0
91
+ def inspect
92
+ "#<Mongo::Session:0x#{object_id} session_id=#{session_id} options=#{@options}>"
70
93
  end
71
94
 
72
95
  # End this session.
@@ -144,19 +167,113 @@ module Mongo
144
167
  # @example Process a response from the server.
145
168
  # session.process(result)
146
169
  #
147
- # @param [ Operation::Result ] The result from the operation.
170
+ # @param [ Operation::Result ] result The result from the operation.
148
171
  #
149
172
  # @return [ Operation::Result ] The result.
150
173
  #
151
174
  # @since 2.5.0
152
175
  def process(result)
153
- set_operation_time(result)
176
+ unless implicit_session?
177
+ set_operation_time(result)
178
+ set_cluster_time(result)
179
+ end
154
180
  @server_session.set_last_use!
155
181
  result
156
182
  end
157
183
 
184
+ # Advance the cached cluster time document for this session.
185
+ #
186
+ # @example Advance the cluster time.
187
+ # session.advance_cluster_time(doc)
188
+ #
189
+ # @param [ BSON::Document, Hash ] new_cluster_time The new cluster time.
190
+ #
191
+ # @return [ BSON::Document, Hash ] The new cluster time.
192
+ #
193
+ # @since 2.5.0
194
+ def advance_cluster_time(new_cluster_time)
195
+ if @cluster_time
196
+ @cluster_time = [ @cluster_time, new_cluster_time ].max_by { |doc| doc[Cluster::CLUSTER_TIME] }
197
+ else
198
+ @cluster_time = new_cluster_time
199
+ end
200
+ end
201
+
202
+ # Advance the cached operation time for this session.
203
+ #
204
+ # @example Advance the operation time.
205
+ # session.advance_operation_time(timestamp)
206
+ #
207
+ # @param [ BSON::Timestamp ] new_operation_time The new operation time.
208
+ #
209
+ # @return [ BSON::Timestamp ] The max operation time, considering the current and new times.
210
+ #
211
+ # @since 2.5.0
212
+ def advance_operation_time(new_operation_time)
213
+ if @operation_time
214
+ @operation_time = [ @operation_time, new_operation_time ].max
215
+ else
216
+ @operation_time = new_operation_time
217
+ end
218
+ end
219
+
220
+ # Will writes executed with this session be retried.
221
+ #
222
+ # @example Will writes be retried.
223
+ # session.retry_writes?
224
+ #
225
+ # @return [ true, false ] If writes will be retried.
226
+ #
227
+ # @note Retryable writes are only available on server versions at least 3.6 and with
228
+ # sharded clusters or replica sets.
229
+ #
230
+ # @since 2.5.0
231
+ def retry_writes?
232
+ !!client.options[:retry_writes] && (cluster.replica_set? || cluster.sharded?)
233
+ end
234
+
235
+ # Get the session id.
236
+ #
237
+ # @example Get the session id.
238
+ # session.session_id
239
+ #
240
+ # @return [ BSON::Document ] The session id.
241
+ #
242
+ # @since 2.5.0
243
+ def session_id
244
+ @server_session.session_id if @server_session
245
+ end
246
+
247
+ # Increment and return the next transaction number.
248
+ #
249
+ # @example Get the next transaction number.
250
+ # server_session.next_txn_num
251
+ #
252
+ # @return [ Integer ] The next transaction number.
253
+ #
254
+ # @since 2.5.0
255
+ def next_txn_num
256
+ @server_session.next_txn_num if @server_session
257
+ end
258
+
158
259
  private
159
260
 
261
+ def causal_consistency_doc(read_concern)
262
+ if operation_time && causal_consistency?
263
+ (read_concern || {}).merge(:afterClusterTime => operation_time)
264
+ else
265
+ read_concern
266
+ end
267
+ end
268
+
269
+ def causal_consistency?
270
+ @causal_consistency ||= (if @options.key?(:causal_consistency)
271
+ @options[:causal_consistency] == true
272
+ else
273
+ true
274
+ end)
275
+ end
276
+
160
277
  def implicit_session?
161
278
  @implicit_session ||= !!(@options.key?(:implicit) && @options[:implicit] == true)
162
279
  end
@@ -167,13 +284,23 @@ module Mongo
167
284
  end
168
285
  end
169
286
 
287
+ def set_cluster_time(result)
288
+ if cluster_time_doc = result.cluster_time
289
+ if @cluster_time.nil?
290
+ @cluster_time = cluster_time_doc
291
+ elsif cluster_time_doc[Cluster::CLUSTER_TIME] > @cluster_time[Cluster::CLUSTER_TIME]
292
+ @cluster_time = cluster_time_doc
293
+ end
294
+ end
295
+ end
296
+
170
297
  def check_if_ended!
171
298
  raise Mongo::Error::InvalidSession.new(SESSION_ENDED_ERROR_MSG) if ended?
172
299
  end
173
300
 
174
301
  def check_matching_client!(client)
175
302
  if @client != client
176
- raise Mongo::Error::InvalidSession.new(MISTMATCHED_CLUSTER_ERROR_MSG)
303
+ raise Mongo::Error::InvalidSession.new(MISMATCHED_CLUSTER_ERROR_MSG)
177
304
  end
178
305
  end
179
306
  end
@@ -46,6 +46,8 @@ module Mongo
46
46
  # @since 2.5.0
47
47
  def initialize
48
48
  set_last_use!
49
+ session_id
50
+ @txn_num = -1
49
51
  end
50
52
 
51
53
  # Update the last_use attribute of the server session to now.
@@ -53,6 +55,8 @@ module Mongo
53
55
  # @example Set the last use field to now.
54
56
  # server_session.set_last_use!
55
57
  #
58
+ # @return [ Time ] The last time the session was used.
59
+ #
56
60
  # @since 2.5.0
57
61
  def set_last_use!
58
62
  @last_use = Time.now
@@ -63,11 +67,37 @@ module Mongo
63
67
  # @example Get the session id.
64
68
  # server_session.session_id
65
69
  #
70
+ # @return [ BSON::Document ] The session id.
71
+ #
66
72
  # @since 2.5.0
67
73
  def session_id
68
74
  @session_id ||= (bytes = [SecureRandom.uuid.gsub(DASH_REGEX, '')].pack(UUID_PACK)
69
75
  BSON::Document.new(id: BSON::Binary.new(bytes, :uuid)))
70
76
  end
77
+
78
+ # Increment and return the next transaction number.
79
+ #
80
+ # @example Get the next transaction number.
81
+ # server_session.next_txn_num
82
+ #
83
+ # @return [ Integer ] The next transaction number.
84
+ #
85
+ # @since 2.5.0
86
+ def next_txn_num
87
+ @txn_num += 1
88
+ end
89
+
90
+ # Get a formatted string for use in inspection.
91
+ #
92
+ # @example Inspect the session object.
93
+ # session.inspect
94
+ #
95
+ # @return [ String ] The session inspection.
96
+ #
97
+ # @since 2.5.0
98
+ def inspect
99
+ "#<Mongo::Session::ServerSession:0x#{object_id} session_id=#{session_id} last_use=#{@last_use}>"
100
+ end
71
101
  end
72
102
  end
73
103
  end
@@ -23,11 +23,6 @@ module Mongo
23
23
  # @since 2.5.0
24
24
  class SessionPool
25
25
 
26
- # The command sent to the server to end a session.
27
- #
28
- # @since 2.5.0
29
- END_SESSION = { :endSessions => 1 }.freeze
30
-
31
26
  # Create a SessionPool.
32
27
  #
33
28
  # @example
@@ -57,6 +52,18 @@ module Mongo
57
52
  @client = client
58
53
  end
59
54
 
55
+ # Get a formatted string for use in inspection.
56
+ #
57
+ # @example Inspect the session pool object.
58
+ # session_pool.inspect
59
+ #
60
+ # @return [ String ] The session pool inspection.
61
+ #
62
+ # @since 2.5.0
63
+ def inspect
64
+ "#<Mongo::Session::SessionPool:0x#{object_id} current_size=#{@queue.size}>"
65
+ end
66
+
60
67
  # Checkout a session to be used in the context of a block and return the session back to
61
68
  # the pool after the block completes.
62
69
  #
@@ -70,8 +77,7 @@ module Mongo
70
77
  # @since 2.5.0
71
78
  def with_session
72
79
  server_session = checkout
73
- result = yield(server_session)
74
- result
80
+ yield(server_session)
75
81
  ensure
76
82
  begin; checkin(server_session) if server_session; rescue; end
77
83
  end
@@ -104,7 +110,7 @@ module Mongo
104
110
  # @example Checkin a session.
105
111
  # pool.checkin(session)
106
112
  #
107
- # @param [ Session::ServerSession ] The session to checkin.
113
+ # @param [ Session::ServerSession ] session The session to checkin.
108
114
  #
109
115
  # @since 2.5.0
110
116
  def checkin(session)
@@ -123,19 +129,13 @@ module Mongo
123
129
  #
124
130
  # @since 2.5.0
125
131
  def end_sessions
126
- if @client
127
- ids = @queue.collect { |s| s.session_id }
128
-
129
- while !ids.empty?
130
- begin
131
- Operation::Commands::Command.new({
132
- :selector => END_SESSION.merge(ids: ids.shift(10_000)),
133
- :db_name => Database::ADMIN
134
- }).execute(@client.cluster.next_primary)
135
- rescue
136
- end
137
- end
132
+ while !@queue.empty?
133
+ server = ServerSelector.get(mode: :primary_preferred).select_server(@client.cluster)
134
+ Operation::Commands::Command.new(
135
+ :selector => {endSessions: @queue.shift(10_000).collect { |s| s.session_id }},
136
+ :db_name => Database::ADMIN).execute(server)
138
137
  end
138
+ rescue
139
139
  end
140
140
 
141
141
  private
data/lib/mongo/uri.rb CHANGED
@@ -22,8 +22,8 @@ module Mongo
22
22
  # http://docs.mongodb.org/manual/reference/connection-string/
23
23
  #
24
24
  # @example Use the uri string to make a client connection.
25
- # uri = URI.new('mongodb://localhost:27017')
26
- # client = Client.new(uri.server, uri.options)
25
+ # uri = Mongo::URI.new('mongodb://localhost:27017')
26
+ # client = Mongo::Client.new(uri.servers, uri.options)
27
27
  # client.login(uri.credentials)
28
28
  # client[uri.database]
29
29
  #
@@ -46,6 +46,39 @@ module Mongo
46
46
  # @since 2.0.0
47
47
  attr_reader :servers
48
48
 
49
+ # The mongodb connection string scheme.
50
+ #
51
+ # @deprecated Will be removed in 3.0.
52
+ #
53
+ # @since 2.0.0
54
+ SCHEME = 'mongodb://'.freeze
55
+
56
+ # The mongodb connection string scheme root.
57
+ #
58
+ # @since 2.5.0
59
+ MONGODB_SCHEME = 'mongodb'.freeze
60
+
61
+ # The mongodb srv protocol connection string scheme root.
62
+ #
63
+ # @since 2.5.0
64
+ MONGODB_SRV_SCHEME = 'mongodb+srv'.freeze
65
+
66
+ # Error details for an invalid scheme.
67
+ #
68
+ # @since 2.1.0
69
+ INVALID_SCHEME = "Invalid scheme. Scheme must be '#{MONGODB_SCHEME}' or '#{MONGODB_SRV_SCHEME}'".freeze
70
+
71
+ # MongoDB URI format specification.
72
+ #
73
+ # @since 2.0.0
74
+ FORMAT = 'mongodb://[username:password@]host1[:port1][,host2[:port2]' +
75
+ ',...[,hostN[:portN]]][/[database][?options]]'.freeze
76
+
77
+ # MongoDB URI (connection string) documentation url
78
+ #
79
+ # @since 2.0.0
80
+ HELP = 'http://docs.mongodb.org/manual/reference/connection-string/'.freeze
81
+
49
82
  # Unsafe characters that must be urlencoded.
50
83
  #
51
84
  # @since 2.1.0
@@ -56,11 +89,6 @@ module Mongo
56
89
  # @since 2.1.0
57
90
  UNIX_SOCKET = /.sock/
58
91
 
59
- # The mongodb connection string scheme.
60
- #
61
- # @since 2.0.0
62
- SCHEME = 'mongodb://'.freeze
63
-
64
92
  # The character delimiting hosts.
65
93
  #
66
94
  # @since 2.1.0
@@ -101,10 +129,10 @@ module Mongo
101
129
  # @since 2.1.0
102
130
  AUTH_DELIM = '@'.freeze
103
131
 
104
- # Error details for an invalid scheme.
132
+ # Scheme delimiter.
105
133
  #
106
- # @since 2.1.0
107
- INVALID_SCHEME = "Invalid scheme. Scheme must be '#{SCHEME}'".freeze
134
+ # @since 2.5.0
135
+ SCHEME_DELIM = '://'.freeze
108
136
 
109
137
  # Error details for an invalid options format.
110
138
  #
@@ -142,17 +170,6 @@ module Mongo
142
170
  # @since 2.1.0
143
171
  INVALID_PORT = "Invalid port. Port must be an integer greater than 0 and less than 65536".freeze
144
172
 
145
- # MongoDB URI format specification.
146
- #
147
- # @since 2.0.0
148
- FORMAT = 'mongodb://[username:password@]host1[:port1][,host2[:port2]' +
149
- ',...[,hostN[:portN]]][/[database][?options]]'.freeze
150
-
151
- # MongoDB URI (connection string) documentation url
152
- #
153
- # @since 2.0.0
154
- HELP = 'http://docs.mongodb.org/manual/reference/connection-string/'.freeze
155
-
156
173
  # Map of URI read preference modes to ruby driver read preference modes
157
174
  #
158
175
  # @since 2.0.0
@@ -180,23 +197,24 @@ module Mongo
180
197
  # @since 2.1.0
181
198
  REPEATABLE_OPTIONS = [ :tag_sets ]
182
199
 
183
- # Create the new uri from the provided string.
200
+ # Get either a URI object or a SRVProtocol URI object.
184
201
  #
185
- # @example Create the new URI.
186
- # URI.new('mongodb://localhost:27017')
202
+ # @example Get the uri object.
203
+ # URI.get(string)
187
204
  #
188
- # @param [ String ] string The uri string.
189
- # @param [ Hash ] options The options.
190
- #
191
- # @raise [ Error::InvalidURI ] If the uri does not match the spec.
205
+ # @return [URI, URI::SRVProtocol] The uri object.
192
206
  #
193
- # @since 2.0.0
194
- def initialize(string, options = {})
195
- @string = string
196
- @options = options
197
- _, scheme, remaining = @string.partition(SCHEME)
198
- raise_invalid_error!(INVALID_SCHEME) unless scheme == SCHEME
199
- setup!(remaining)
207
+ # @since 2.5.0
208
+ def self.get(string, opts = {})
209
+ scheme, _, remaining = string.partition(SCHEME_DELIM)
210
+ case scheme
211
+ when MONGODB_SCHEME
212
+ URI.new(string, opts)
213
+ when MONGODB_SRV_SCHEME
214
+ SRVProtocol.new(string, opts)
215
+ else
216
+ raise Error::InvalidURI.new(string, INVALID_SCHEME)
217
+ end
200
218
  end
201
219
 
202
220
  # Gets the options hash that needs to be passed to a Mongo::Client on
@@ -214,6 +232,25 @@ module Mongo
214
232
  @user ? opts.merge(credentials) : opts
215
233
  end
216
234
 
235
+ # Create the new uri from the provided string.
236
+ #
237
+ # @example Create the new URI.
238
+ # URI.new('mongodb://localhost:27017')
239
+ #
240
+ # @param [ String ] string The uri string.
241
+ # @param [ Hash ] options The options.
242
+ #
243
+ # @raise [ Error::InvalidURI ] If the uri does not match the spec.
244
+ #
245
+ # @since 2.0.0
246
+ def initialize(string, options = {})
247
+ @string = string
248
+ @options = options
249
+ parsed_scheme, _, remaining = string.partition(SCHEME_DELIM)
250
+ raise_invalid_error!(INVALID_SCHEME) unless parsed_scheme == scheme
251
+ parse!(remaining)
252
+ end
253
+
217
254
  # Get the credentials provided in the URI.
218
255
  #
219
256
  # @example Get the credentials.
@@ -242,7 +279,18 @@ module Mongo
242
279
 
243
280
  private
244
281
 
245
- def setup!(remaining)
282
+ def scheme
283
+ MONGODB_SCHEME
284
+ end
285
+
286
+ def parse_creds_hosts!(string)
287
+ hosts, creds = split_creds_hosts(string)
288
+ @servers = parse_servers!(hosts)
289
+ @user = parse_user!(creds)
290
+ @password = parse_password!(creds)
291
+ end
292
+
293
+ def parse!(remaining)
246
294
  creds_hosts, db_opts = extract_db_opts!(remaining)
247
295
  parse_creds_hosts!(creds_hosts)
248
296
  parse_db_opts!(db_opts)
@@ -257,13 +305,6 @@ module Mongo
257
305
  [ creds_hosts, db_opts ].map { |s| s.reverse }
258
306
  end
259
307
 
260
- def parse_creds_hosts!(string)
261
- hosts, creds = split_creds_hosts(string)
262
- @servers = parse_servers!(hosts)
263
- @user = parse_user!(creds)
264
- @password = parse_password!(creds)
265
- end
266
-
267
308
  def split_creds_hosts(string)
268
309
  hosts, _, creds = string.reverse.partition(AUTH_DELIM)
269
310
  hosts, creds = creds, hosts if hosts.empty?
@@ -330,13 +371,14 @@ module Mongo
330
371
  validate_port_string!(p)
331
372
  elsif host =~ UNIX_SOCKET
332
373
  raise_invalid_error!(UNESCAPED_UNIX_SOCKET) if host =~ UNSAFE
374
+ host = decode(host)
333
375
  end
334
376
  servers << host
335
377
  end
336
378
  end
337
379
 
338
380
  def raise_invalid_error!(details)
339
- raise Error::InvalidURI.new(@string, details)
381
+ raise Error::InvalidURI.new(@string, details, FORMAT)
340
382
  end
341
383
 
342
384
  def decode(value)
@@ -585,3 +627,5 @@ module Mongo
585
627
  end
586
628
  end
587
629
  end
630
+
631
+ require 'mongo/uri/srv_protocol'