mongo 2.4.3 → 2.5.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -2
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +3 -2
  5. data/lib/mongo/auth/cr.rb +6 -4
  6. data/lib/mongo/auth/cr/conversation.rb +33 -17
  7. data/lib/mongo/auth/ldap.rb +4 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +19 -9
  9. data/lib/mongo/auth/scram.rb +7 -4
  10. data/lib/mongo/auth/scram/conversation.rb +62 -24
  11. data/lib/mongo/auth/user.rb +10 -0
  12. data/lib/mongo/auth/user/view.rb +44 -22
  13. data/lib/mongo/auth/x509.rb +4 -2
  14. data/lib/mongo/auth/x509/conversation.rb +19 -9
  15. data/lib/mongo/bulk_write.rb +33 -27
  16. data/lib/mongo/bulk_write/combineable.rb +5 -0
  17. data/lib/mongo/bulk_write/transformable.rb +2 -0
  18. data/lib/mongo/bulk_write/validatable.rb +4 -0
  19. data/lib/mongo/client.rb +123 -12
  20. data/lib/mongo/cluster.rb +52 -11
  21. data/lib/mongo/cluster/app_metadata.rb +8 -2
  22. data/lib/mongo/cluster/cursor_reaper.rb +0 -1
  23. data/lib/mongo/cluster/topology.rb +1 -1
  24. data/lib/mongo/collection.rb +114 -27
  25. data/lib/mongo/collection/view.rb +8 -2
  26. data/lib/mongo/collection/view/aggregation.rb +11 -7
  27. data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
  28. data/lib/mongo/collection/view/builder/find_command.rb +5 -3
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
  30. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  31. data/lib/mongo/collection/view/change_stream.rb +160 -0
  32. data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
  33. data/lib/mongo/collection/view/iterable.rb +11 -10
  34. data/lib/mongo/collection/view/map_reduce.rb +22 -18
  35. data/lib/mongo/collection/view/readable.rb +51 -37
  36. data/lib/mongo/collection/view/writable.rb +72 -40
  37. data/lib/mongo/cursor.rb +25 -4
  38. data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
  39. data/lib/mongo/database.rb +22 -11
  40. data/lib/mongo/database/view.rb +16 -12
  41. data/lib/mongo/error.rb +5 -0
  42. data/lib/mongo/error/invalid_session.rb +36 -0
  43. data/lib/mongo/error/missing_resume_token.rb +39 -0
  44. data/lib/mongo/error/operation_failure.rb +17 -0
  45. data/lib/mongo/error/parser.rb +3 -2
  46. data/lib/mongo/error/unknown_payload_type.rb +41 -0
  47. data/lib/mongo/error/unsupported_array_filters.rb +51 -0
  48. data/lib/mongo/error/unsupported_message_type.rb +23 -0
  49. data/lib/mongo/grid/fs_bucket.rb +5 -4
  50. data/lib/mongo/grid/stream/read.rb +3 -2
  51. data/lib/mongo/grid/stream/write.rb +2 -2
  52. data/lib/mongo/index/view.rb +35 -25
  53. data/lib/mongo/monitoring/event/secure.rb +14 -0
  54. data/lib/mongo/operation.rb +16 -0
  55. data/lib/mongo/operation/commands.rb +1 -0
  56. data/lib/mongo/operation/commands/aggregate.rb +9 -5
  57. data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
  58. data/lib/mongo/operation/commands/collections_info.rb +6 -6
  59. data/lib/mongo/operation/commands/command.rb +2 -1
  60. data/lib/mongo/operation/commands/create.rb +6 -2
  61. data/lib/mongo/operation/commands/drop.rb +6 -2
  62. data/lib/mongo/operation/commands/drop_database.rb +6 -2
  63. data/lib/mongo/operation/commands/explain.rb +27 -0
  64. data/lib/mongo/operation/commands/explain/result.rb +52 -0
  65. data/lib/mongo/operation/commands/indexes.rb +1 -1
  66. data/lib/mongo/operation/commands/list_collections.rb +1 -1
  67. data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
  68. data/lib/mongo/operation/commands/list_indexes.rb +1 -1
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
  70. data/lib/mongo/operation/commands/map_reduce.rb +8 -4
  71. data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
  72. data/lib/mongo/operation/commands/user_query.rb +1 -1
  73. data/lib/mongo/operation/commands/users_info.rb +6 -2
  74. data/lib/mongo/operation/executable.rb +4 -1
  75. data/lib/mongo/operation/read_preference.rb +10 -5
  76. data/lib/mongo/operation/result.rb +26 -2
  77. data/lib/mongo/operation/specifiable.rb +13 -1
  78. data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
  79. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
  80. data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
  81. data/lib/mongo/operation/write/command/create_index.rb +6 -1
  82. data/lib/mongo/operation/write/command/delete.rb +28 -4
  83. data/lib/mongo/operation/write/command/drop_index.rb +6 -1
  84. data/lib/mongo/operation/write/command/insert.rb +22 -18
  85. data/lib/mongo/operation/write/command/update.rb +24 -9
  86. data/lib/mongo/operation/write/command/writable.rb +14 -1
  87. data/lib/mongo/operation/write/insert.rb +4 -1
  88. data/lib/mongo/operation/write/insert/result.rb +2 -2
  89. data/lib/mongo/operation/write/update.rb +7 -1
  90. data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
  91. data/lib/mongo/protocol.rb +3 -0
  92. data/lib/mongo/protocol/bit_vector.rb +2 -2
  93. data/lib/mongo/protocol/compressed.rb +135 -0
  94. data/lib/mongo/protocol/delete.rb +8 -6
  95. data/lib/mongo/protocol/get_more.rb +8 -6
  96. data/lib/mongo/protocol/insert.rb +8 -6
  97. data/lib/mongo/protocol/kill_cursors.rb +8 -6
  98. data/lib/mongo/protocol/message.rb +31 -3
  99. data/lib/mongo/protocol/msg.rb +172 -0
  100. data/lib/mongo/protocol/query.rb +26 -6
  101. data/lib/mongo/protocol/registry.rb +76 -0
  102. data/lib/mongo/protocol/reply.rb +10 -5
  103. data/lib/mongo/protocol/serializers.rb +224 -0
  104. data/lib/mongo/protocol/update.rb +8 -6
  105. data/lib/mongo/retryable.rb +4 -2
  106. data/lib/mongo/server.rb +6 -3
  107. data/lib/mongo/server/connectable.rb +1 -1
  108. data/lib/mongo/server/connection.rb +30 -8
  109. data/lib/mongo/server/description.rb +25 -1
  110. data/lib/mongo/server/description/features.rb +4 -1
  111. data/lib/mongo/server/monitor.rb +5 -0
  112. data/lib/mongo/server/monitor/connection.rb +50 -2
  113. data/lib/mongo/server_selector/nearest.rb +10 -4
  114. data/lib/mongo/server_selector/primary.rb +20 -0
  115. data/lib/mongo/server_selector/primary_preferred.rb +10 -4
  116. data/lib/mongo/server_selector/secondary.rb +10 -4
  117. data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
  118. data/lib/mongo/session.rb +180 -0
  119. data/lib/mongo/session/server_session.rb +73 -0
  120. data/lib/mongo/session/session_pool.rb +161 -0
  121. data/lib/mongo/uri.rb +11 -0
  122. data/lib/mongo/version.rb +1 -1
  123. data/mongo.gemspec +2 -1
  124. data/spec/mongo/auth/cr_spec.rb +12 -0
  125. data/spec/mongo/auth/ldap_spec.rb +2 -0
  126. data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
  127. data/spec/mongo/auth/scram_spec.rb +25 -1
  128. data/spec/mongo/auth/user/view_spec.rb +268 -76
  129. data/spec/mongo/auth/x509_spec.rb +2 -0
  130. data/spec/mongo/bulk_write_spec.rb +435 -5
  131. data/spec/mongo/client_spec.rb +356 -39
  132. data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
  133. data/spec/mongo/cluster_spec.rb +176 -0
  134. data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
  135. data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
  136. data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
  137. data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
  138. data/spec/mongo/collection/view/readable_spec.rb +3 -12
  139. data/spec/mongo/collection_spec.rb +1048 -42
  140. data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
  141. data/spec/mongo/cursor_spec.rb +2 -2
  142. data/spec/mongo/database_spec.rb +50 -1
  143. data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
  144. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  145. data/spec/mongo/index/view_spec.rb +146 -8
  146. data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
  147. data/spec/mongo/operation/read/query_spec.rb +2 -1
  148. data/spec/mongo/operation/specifiable_spec.rb +2 -2
  149. data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
  150. data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
  151. data/spec/mongo/operation/write/command/update_spec.rb +93 -10
  152. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  153. data/spec/mongo/operation/write/insert_spec.rb +1 -1
  154. data/spec/mongo/operation/write/update_spec.rb +1 -1
  155. data/spec/mongo/protocol/compressed_spec.rb +66 -0
  156. data/spec/mongo/protocol/delete_spec.rb +14 -0
  157. data/spec/mongo/protocol/get_more_spec.rb +14 -0
  158. data/spec/mongo/protocol/insert_spec.rb +14 -0
  159. data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
  160. data/spec/mongo/protocol/msg_spec.rb +499 -0
  161. data/spec/mongo/protocol/query_spec.rb +45 -0
  162. data/spec/mongo/protocol/registry_spec.rb +31 -0
  163. data/spec/mongo/protocol/reply_spec.rb +14 -0
  164. data/spec/mongo/protocol/update_spec.rb +14 -0
  165. data/spec/mongo/retryable_spec.rb +6 -2
  166. data/spec/mongo/sdam_spec.rb +4 -0
  167. data/spec/mongo/server/connection_spec.rb +4 -2
  168. data/spec/mongo/server/description_spec.rb +28 -1
  169. data/spec/mongo/session/server_session_spec.rb +16 -0
  170. data/spec/mongo/session/session_pool_spec.rb +194 -0
  171. data/spec/mongo/uri_spec.rb +31 -2
  172. data/spec/spec_helper.rb +104 -0
  173. data/spec/support/authorization.rb +6 -1
  174. data/spec/support/crud.rb +3 -1
  175. data/spec/support/crud/write.rb +6 -1
  176. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
  177. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
  178. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
  179. data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
  180. data/spec/support/sdam/rs/discover_passives.yml +2 -2
  181. data/spec/support/sdam/rs/discover_primary.yml +1 -1
  182. data/spec/support/sdam/rs/discover_secondary.yml +1 -1
  183. data/spec/support/sdam/rs/discovery.yml +4 -4
  184. data/spec/support/sdam/rs/equal_electionids.yml +1 -0
  185. data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
  186. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
  187. data/spec/support/sdam/rs/ls_timeout.yml +88 -0
  188. data/spec/support/sdam/rs/member_reconfig.yml +2 -2
  189. data/spec/support/sdam/rs/member_standalone.yml +2 -2
  190. data/spec/support/sdam/rs/new_primary.yml +2 -2
  191. data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
  192. data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
  193. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
  194. data/spec/support/sdam/rs/non_rs_member.yml +1 -1
  195. data/spec/support/sdam/rs/normalize_case.yml +1 -1
  196. data/spec/support/sdam/rs/null_election_id.yml +4 -0
  197. data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
  198. data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
  199. data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
  200. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
  201. data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
  202. data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
  203. data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
  204. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
  205. data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
  206. data/spec/support/sdam/rs/response_from_removed.yml +2 -2
  207. data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
  208. data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
  209. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
  210. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
  211. data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
  212. data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
  213. data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
  214. data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
  215. data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
  216. data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
  217. data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
  218. data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
  219. data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
  220. data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  225. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  226. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  227. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  228. data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
  229. data/spec/support/sdam/single/not_ok_response.yml +1 -1
  230. data/spec/support/sdam/single/standalone_removed.yml +1 -1
  231. data/spec/support/sdam/single/unavailable_seed.yml +1 -1
  232. data/spec/support/server_discovery_and_monitoring.rb +4 -0
  233. data/spec/support/shared/session.rb +236 -0
  234. metadata +53 -15
  235. metadata.gz.sig +0 -0
@@ -154,10 +154,22 @@ module Mongo
154
154
  # @since 2.1.0
155
155
  LOCAL_TIME = 'localTime'.freeze
156
156
 
157
+ # Constant for reading operationTime info from config.
158
+ #
159
+ # @since 2.5.0
160
+ OPERATION_TIME = 'operationTime'.freeze
161
+
162
+ # Constant for reading logicalSessionTimeoutMinutes info from config.
163
+ #
164
+ # @since 2.5.0
165
+ LOGICAL_SESSION_TIMEOUT_MINUTES = 'logicalSessionTimeoutMinutes'.freeze
166
+
157
167
  # Fields to exclude when comparing two descriptions.
158
168
  #
159
169
  # @since 2.0.6
160
- EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME, LAST_WRITE ].freeze
170
+ EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME,
171
+ LAST_WRITE,
172
+ OPERATION_TIME ].freeze
161
173
 
162
174
  # @return [ Address ] address The server's address.
163
175
  attr_reader :address
@@ -382,6 +394,18 @@ module Mongo
382
394
  config[LAST_WRITE][LAST_WRITE_DATE] if config[LAST_WRITE]
383
395
  end
384
396
 
397
+ # Get the logicalSessionTimeoutMinutes from the config.
398
+ #
399
+ # @example Get the logicalSessionTimeoutMinutes value in minutes.
400
+ # description.logical_session_timeout
401
+ #
402
+ # @return [ Integer, nil ] The logical session timeout in minutes.
403
+ #
404
+ # @since 2.5.0
405
+ def logical_session_timeout
406
+ config[LOGICAL_SESSION_TIMEOUT_MINUTES] if config[LOGICAL_SESSION_TIMEOUT_MINUTES]
407
+ end
408
+
385
409
  # Is the server a mongos?
386
410
  #
387
411
  # @example Is the server a mongos?
@@ -25,6 +25,9 @@ module Mongo
25
25
  #
26
26
  # @since 2.0.0
27
27
  MAPPINGS = {
28
+ :array_filters => 6,
29
+ :op_msg => 6,
30
+ :sessions => 6,
28
31
  :collation => 5,
29
32
  :max_staleness => 5,
30
33
  :find_command => 4,
@@ -38,7 +41,7 @@ module Mongo
38
41
  # The wire protocol versions that this version of the driver supports.
39
42
  #
40
43
  # @since 2.0.0
41
- DRIVER_WIRE_VERSIONS = (0..5).freeze
44
+ DRIVER_WIRE_VERSIONS = (0..6).freeze
42
45
 
43
46
  # Create the methods for each mapping to tell if they are supported.
44
47
  #
@@ -23,6 +23,7 @@ module Mongo
23
23
  # @since 2.0.0
24
24
  class Monitor
25
25
  include Loggable
26
+ extend Forwardable
26
27
 
27
28
  # The default time for a server to refresh its status is 10 seconds.
28
29
  #
@@ -58,6 +59,10 @@ module Mongo
58
59
  # @since 2.4.0
59
60
  attr_reader :last_scan
60
61
 
62
+ # The compressor is determined during the handshake, so it must be an attribute
63
+ # of the connection.
64
+ def_delegators :connection, :compressor
65
+
61
66
  # Force the monitor to immediately do a check of its server.
62
67
  #
63
68
  # @example Force a scan.
@@ -22,22 +22,38 @@ module Mongo
22
22
  class Connection
23
23
  include Retryable
24
24
  include Connectable
25
+ include Loggable
25
26
 
26
27
  # The command used for determining server status.
27
28
  #
28
29
  # @since 2.2.0
29
30
  ISMASTER = { :ismaster => 1 }.freeze
30
31
 
32
+ # The command used for determining server status formatted for an OP_MSG (server versions >= 3.6).
33
+ #
34
+ # @since 2.5.0
35
+ ISMASTER_OP_MSG = { :ismaster => 1, '$db' => Database::ADMIN }.freeze
36
+
31
37
  # The constant for the ismaster command.
32
38
  #
33
39
  # @since 2.2.0
34
40
  ISMASTER_MESSAGE = Protocol::Query.new(Database::ADMIN, Database::COMMAND, ISMASTER, :limit => -1)
35
41
 
42
+ # The constant for the ismaster command as an OP_MSG (server versions >= 3.6).
43
+ #
44
+ # @since 2.5.0
45
+ ISMASTER_OP_MSG_MESSAGE = Protocol::Msg.new([:none], {}, ISMASTER_OP_MSG)
46
+
36
47
  # The raw bytes for the ismaster message.
37
48
  #
38
49
  # @since 2.2.0
39
50
  ISMASTER_BYTES = ISMASTER_MESSAGE.serialize.to_s.freeze
40
51
 
52
+ # The raw bytes for the ismaster OP_MSG message (server versions >= 3.6).
53
+ #
54
+ # @since 2.5.0
55
+ ISMASTER_OP_MSG_BYTES = ISMASTER_OP_MSG_MESSAGE.serialize.to_s.freeze
56
+
41
57
  # The default time in seconds to timeout a connection attempt.
42
58
  #
43
59
  # @since 2.1.2
@@ -45,6 +61,23 @@ module Mongo
45
61
  # @deprecated Please use Server::CONNECT_TIMEOUT instead. Will be removed in 3.0.0
46
62
  CONNECT_TIMEOUT = 10.freeze
47
63
 
64
+ # Key for compression algorithms in the response from the server during handshake.
65
+ #
66
+ # @since 2.5.0
67
+ COMPRESSION = 'compression'.freeze
68
+
69
+ # Warning message that the server has no compression algorithms in common with those requested
70
+ # by the client.
71
+ #
72
+ # @since 2.5.0
73
+ COMPRESSION_WARNING = 'The server has no compression algorithms in common with those requested. ' +
74
+ 'Compression will not be used.'.freeze
75
+
76
+ # The compressor, which is determined during the handshake.
77
+ #
78
+ # @since 2.5.0
79
+ attr_reader :compressor
80
+
48
81
  # Send the preserialized ismaster call.
49
82
  #
50
83
  # @example Send a preserialized ismaster message.
@@ -57,7 +90,7 @@ module Mongo
57
90
  ensure_connected do |socket|
58
91
  read_with_one_retry do
59
92
  socket.write(ISMASTER_BYTES)
60
- Protocol::Reply.deserialize(socket).documents[0]
93
+ Protocol::Message.deserialize(socket).documents[0]
61
94
  end
62
95
  end
63
96
  end
@@ -122,6 +155,7 @@ module Mongo
122
155
  @ssl_options = options.reject { |k, v| !k.to_s.start_with?(SSL) }
123
156
  @socket = nil
124
157
  @pid = Process.pid
158
+ @compressor = nil
125
159
  end
126
160
 
127
161
  # Get the socket timeout.
@@ -142,10 +176,24 @@ module Mongo
142
176
 
143
177
  private
144
178
 
179
+ def set_compressor!(reply)
180
+ server_compressors = reply[COMPRESSION]
181
+
182
+ if options[:compressors]
183
+ if intersection = (server_compressors & options[:compressors])
184
+ @compressor = intersection[0]
185
+ else
186
+ log_warn(COMPRESSION_WARNING)
187
+ end
188
+ end
189
+ end
190
+
145
191
  def handshake!
146
192
  if @app_metadata
147
193
  socket.write(@app_metadata.ismaster_bytes)
148
- Protocol::Reply.deserialize(socket, Mongo::Protocol::Message::MAX_MESSAGE_SIZE).documents[0]
194
+ reply = Protocol::Message.deserialize(socket, Mongo::Protocol::Message::MAX_MESSAGE_SIZE).documents[0]
195
+ set_compressor!(reply)
196
+ reply
149
197
  end
150
198
  end
151
199
  end
@@ -22,6 +22,11 @@ module Mongo
22
22
  class Nearest
23
23
  include Selectable
24
24
 
25
+ # Name of the this read preference in the server's format.
26
+ #
27
+ # @since 2.5.0
28
+ SERVER_FORMATTED_NAME = 'nearest'.freeze
29
+
25
30
  # Get the name of the server mode type.
26
31
  #
27
32
  # @example Get the name of the server mode for this preference.
@@ -65,11 +70,12 @@ module Mongo
65
70
  #
66
71
  # @since 2.0.0
67
72
  def to_mongos
68
- preference = { :mode => 'nearest' }
69
- preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
70
- preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
71
- preference
73
+ @doc ||= (preference = { :mode => SERVER_FORMATTED_NAME }
74
+ preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
75
+ preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
76
+ preference)
72
77
  end
78
+ alias :to_doc :to_mongos
73
79
 
74
80
  private
75
81
 
@@ -23,6 +23,11 @@ module Mongo
23
23
  class Primary
24
24
  include Selectable
25
25
 
26
+ # Name of the this read preference in the server's format.
27
+ #
28
+ # @since 2.5.0
29
+ SERVER_FORMATTED_NAME = 'primary'.freeze
30
+
26
31
  # Get the name of the server mode type.
27
32
  #
28
33
  # @example Get the name of the server mode for this preference.
@@ -69,6 +74,21 @@ module Mongo
69
74
  nil
70
75
  end
71
76
 
77
+ # Convert this server preference definition into a format appropriate
78
+ # for a mongodb server.
79
+ #
80
+ # @example Convert this server preference definition into a format
81
+ # for a server.
82
+ # preference = Mongo::ServerSelector::Primary.new
83
+ # preference.to_doc
84
+ #
85
+ # @return [ Hash ] The server preference formatted for a mongodb server.
86
+ #
87
+ # @since 2.5.0
88
+ def to_doc
89
+ @doc ||= { mode: SERVER_FORMATTED_NAME }
90
+ end
91
+
72
92
  private
73
93
 
74
94
  # Select the primary server from a list of candidates.
@@ -23,6 +23,11 @@ module Mongo
23
23
  class PrimaryPreferred
24
24
  include Selectable
25
25
 
26
+ # Name of the this read preference in the server's format.
27
+ #
28
+ # @since 2.5.0
29
+ SERVER_FORMATTED_NAME = 'primaryPreferred'.freeze
30
+
26
31
  # Get the name of the server mode type.
27
32
  #
28
33
  # @example Get the name of the server mode for this preference.
@@ -66,11 +71,12 @@ module Mongo
66
71
  #
67
72
  # @since 2.0.0
68
73
  def to_mongos
69
- preference = { :mode => 'primaryPreferred' }
70
- preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
71
- preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
72
- preference
74
+ @doc ||= (preference = { :mode => SERVER_FORMATTED_NAME }
75
+ preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
76
+ preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
77
+ preference)
73
78
  end
79
+ alias :to_doc :to_mongos
74
80
 
75
81
  private
76
82
 
@@ -23,6 +23,11 @@ module Mongo
23
23
  class Secondary
24
24
  include Selectable
25
25
 
26
+ # Name of the this read preference in the server's format.
27
+ #
28
+ # @since 2.5.0
29
+ SERVER_FORMATTED_NAME = 'secondary'.freeze
30
+
26
31
  # Get the name of the server mode type.
27
32
  #
28
33
  # @example Get the name of the server mode for this preference.
@@ -66,11 +71,12 @@ module Mongo
66
71
  #
67
72
  # @since 2.0.0
68
73
  def to_mongos
69
- preference = { :mode => 'secondary' }
70
- preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
71
- preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
72
- preference
74
+ @doc ||= (preference = { :mode => SERVER_FORMATTED_NAME }
75
+ preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
76
+ preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
77
+ preference)
73
78
  end
79
+ alias :to_doc :to_mongos
74
80
 
75
81
  private
76
82
 
@@ -23,6 +23,11 @@ module Mongo
23
23
  class SecondaryPreferred
24
24
  include Selectable
25
25
 
26
+ # Name of the this read preference in the server's format.
27
+ #
28
+ # @since 2.5.0
29
+ SERVER_FORMATTED_NAME = 'secondaryPreferred'.freeze
30
+
26
31
  # Get the name of the server mode type.
27
32
  #
28
33
  # @example Get the name of the server mode for this preference.
@@ -69,10 +74,25 @@ module Mongo
69
74
  # @since 2.0.0
70
75
  def to_mongos
71
76
  return nil if tag_sets.empty? && max_staleness.nil?
72
- preference = { mode: 'secondaryPreferred' }
73
- preference.merge!({ tags: tag_sets }) unless tag_sets.empty?
74
- preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
75
- preference
77
+ to_doc
78
+ end
79
+
80
+ # Convert this server preference definition into a format appropriate
81
+ # for a server.
82
+ #
83
+ # @example Convert this server preference definition into a format
84
+ # for a server.
85
+ # preference = Mongo::ServerSelector::SecondaryPreferred.new
86
+ # preference.to_doc
87
+ #
88
+ # @return [ Hash ] The server preference formatted for a server.
89
+ #
90
+ # @since 2.5.0
91
+ def to_doc
92
+ @doc ||= (preference = { mode: SERVER_FORMATTED_NAME }
93
+ preference.merge!({ tags: tag_sets }) unless tag_sets.empty?
94
+ preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness
95
+ preference)
76
96
  end
77
97
 
78
98
  private
@@ -0,0 +1,180 @@
1
+ # Copyright (C) 2017 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'mongo/session/session_pool'
16
+ require 'mongo/session/server_session'
17
+
18
+ module Mongo
19
+
20
+ # A logical client session representing a set of sequential operations executed
21
+ # by an application that are related in some way.
22
+ #
23
+ # @since 2.5.0
24
+ class Session
25
+ extend Forwardable
26
+
27
+ # Get the options for this session.
28
+ #
29
+ # @since 2.5.0
30
+ attr_reader :options
31
+
32
+ # Get the client through which this session was created.
33
+ #
34
+ # @since 2.5.0
35
+ attr_reader :client
36
+
37
+ def_delegators :@server_session, :session_id
38
+
39
+ # Error message describing that the session was attempted to be used by a client different from the
40
+ # one it was originally associated with.
41
+ #
42
+ # @since 2.5.0
43
+ MISTMATCHED_CLUSTER_ERROR_MSG = 'The client used to create this session does not match that of client ' +
44
+ 'initiating this operation. Please only use this session for operations through its parent client.'.freeze
45
+
46
+ # Error message describing that the session cannot be used because it has already been ended.
47
+ #
48
+ # @since 2.5.0
49
+ SESSION_ENDED_ERROR_MSG = 'This session has ended and cannot be used. Please create a new one.'.freeze
50
+
51
+ # Error message describing that sessions are not supported by the server version.
52
+ #
53
+ # @since 2.5.0
54
+ SESSIONS_NOT_SUPPORTED = 'Sessions are not supported by the connected servers.'.freeze
55
+
56
+ # Initialize a Session.
57
+ #
58
+ # @example
59
+ # Session.new(server_session, client, options)
60
+ #
61
+ # @param [ ServerSession ] server_session The server session this client session is associated with.
62
+ # @param [ Client ] client The client through which this session is created.
63
+ # @param [ Hash ] options The options for this session.
64
+ #
65
+ # @since 2.5.0
66
+ def initialize(server_session, client, options = {})
67
+ @server_session = server_session
68
+ @client = client
69
+ @options = options
70
+ end
71
+
72
+ # End this session.
73
+ #
74
+ # @example
75
+ # session.end_session
76
+ #
77
+ # @return [ nil ] Always nil.
78
+ #
79
+ # @since 2.5.0
80
+ def end_session
81
+ if !ended? && @client
82
+ @client.instance_variable_get(:@session_pool).checkin(@server_session)
83
+ nil
84
+ end
85
+ ensure
86
+ @server_session = nil
87
+ end
88
+
89
+ # End this session if it's an implicit session.
90
+ #
91
+ # @example
92
+ # session.end_implicit_session
93
+ #
94
+ # @return [ nil ] Always nil.
95
+ #
96
+ # @since 2.5.0
97
+ def end_implicit_session
98
+ end_session if implicit_session?
99
+ end
100
+
101
+ # Whether this session has ended.
102
+ #
103
+ # @example
104
+ # session.ended?
105
+ #
106
+ # @return [ true, false ] Whether the session has ended.
107
+ #
108
+ # @since 2.5.0
109
+ def ended?
110
+ @server_session.nil?
111
+ end
112
+
113
+ # Add this session's id to a command document.
114
+ #
115
+ # @example
116
+ # session.add_id!(cmd)
117
+ #
118
+ # @return [ Hash, BSON::Document ] The command document.
119
+ #
120
+ # @since 2.5.0
121
+ def add_id!(command)
122
+ command.merge!(lsid: session_id)
123
+ end
124
+
125
+ # Validate the session.
126
+ #
127
+ # @example
128
+ # session.validate!(client)
129
+ #
130
+ # @param [ Client ] client The client the session is attempted to be used with.
131
+ #
132
+ # @return [ nil ] nil if the session is valid.
133
+ #
134
+ # @raise [ Mongo::Error::InvalidSession ] Raise error if the session is not valid.
135
+ #
136
+ # @since 2.5.0
137
+ def validate!(client)
138
+ check_matching_client!(client)
139
+ check_if_ended!
140
+ end
141
+
142
+ # Process a response from the server that used this session.
143
+ #
144
+ # @example Process a response from the server.
145
+ # session.process(result)
146
+ #
147
+ # @param [ Operation::Result ] The result from the operation.
148
+ #
149
+ # @return [ Operation::Result ] The result.
150
+ #
151
+ # @since 2.5.0
152
+ def process(result)
153
+ set_operation_time(result)
154
+ @server_session.set_last_use!
155
+ result
156
+ end
157
+
158
+ private
159
+
160
+ def implicit_session?
161
+ @implicit_session ||= !!(@options.key?(:implicit) && @options[:implicit] == true)
162
+ end
163
+
164
+ def set_operation_time(result)
165
+ if result && result.operation_time
166
+ @operation_time = result.operation_time
167
+ end
168
+ end
169
+
170
+ def check_if_ended!
171
+ raise Mongo::Error::InvalidSession.new(SESSION_ENDED_ERROR_MSG) if ended?
172
+ end
173
+
174
+ def check_matching_client!(client)
175
+ if @client != client
176
+ raise Mongo::Error::InvalidSession.new(MISTMATCHED_CLUSTER_ERROR_MSG)
177
+ end
178
+ end
179
+ end
180
+ end