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
@@ -50,12 +50,14 @@ module Mongo
50
50
  # @param [ Mongo::Connection ] connection The connection to log into.
51
51
  # on.
52
52
  #
53
- # @return [ Protocol::Reply ] The authentication response.
53
+ # @return [ Protocol::Message ] The authentication response.
54
54
  #
55
55
  # @since 2.0.0
56
56
  def login(connection)
57
57
  conversation = Conversation.new(user)
58
- conversation.finalize(connection.dispatch([ conversation.start ]))
58
+ reply = connection.dispatch([ conversation.start(connection) ])
59
+ connection.update_cluster_time(Operation::Result.new(reply))
60
+ conversation.finalize(reply)
59
61
  end
60
62
  end
61
63
  end
@@ -27,7 +27,7 @@ module Mongo
27
27
  # @since 2.0.0
28
28
  LOGIN = { authenticate: 1 }.freeze
29
29
 
30
- # @return [ Protocol::Reply ] reply The current reply in the
30
+ # @return [ Protocol::Message ] reply The current reply in the
31
31
  # conversation.
32
32
  attr_reader :reply
33
33
 
@@ -40,7 +40,7 @@ module Mongo
40
40
  # @example Finalize the conversation.
41
41
  # conversation.finalize(reply)
42
42
  #
43
- # @param [ Protocol::Reply ] reply The reply of the previous
43
+ # @param [ Protocol::Message ] reply The reply of the previous
44
44
  # message.
45
45
  #
46
46
  # @return [ Protocol::Query ] The next message to send.
@@ -56,18 +56,28 @@ module Mongo
56
56
  # @example Start the conversation.
57
57
  # conversation.start
58
58
  #
59
+ # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
60
+ #
59
61
  # @return [ Protocol::Query ] The first x.509 conversation message.
60
62
  #
61
63
  # @since 2.0.0
62
- def start
64
+ def start(connection = nil)
63
65
  login = LOGIN.merge(mechanism: X509::MECHANISM)
64
66
  login[:user] = user.name if user.name
65
- Protocol::Query.new(
66
- Auth::EXTERNAL,
67
- Database::COMMAND,
68
- login,
69
- limit: -1
70
- )
67
+ if connection && connection.features.op_msg_enabled?
68
+ selector = login
69
+ selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
70
+ cluster_time = connection.mongos? && connection.cluster_time
71
+ selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
72
+ Protocol::Msg.new([:none], {}, selector)
73
+ else
74
+ Protocol::Query.new(
75
+ Auth::EXTERNAL,
76
+ Database::COMMAND,
77
+ login,
78
+ limit: -1
79
+ )
80
+ end
71
81
  end
72
82
 
73
83
  # Create the new conversation.
@@ -53,18 +53,23 @@ module Mongo
53
53
  def execute
54
54
  operation_id = Monitoring.next_operation_id
55
55
  result_combiner = ResultCombiner.new
56
- write_with_retry do
57
- operations = op_combiner.combine
58
- server = next_primary
59
- raise Error::UnsupportedCollation.new if op_combiner.has_collation && !server.features.collation_enabled?
60
- operations.each do |operation|
61
- execute_operation(
62
- operation.keys.first,
63
- operation.values.first,
64
- server,
65
- operation_id,
66
- result_combiner
67
- )
56
+
57
+ client.send(:with_session, @options) do |session|
58
+ write_with_retry(session, Proc.new { next_primary }) do |server|
59
+ operations = op_combiner.combine
60
+ raise Error::UnsupportedCollation.new if op_combiner.has_collation && !server.features.collation_enabled?
61
+ raise Error::UnsupportedArrayFilters.new if op_combiner.has_array_filters && !server.features.array_filters_enabled?
62
+
63
+ operations.each do |operation|
64
+ execute_operation(
65
+ operation.keys.first,
66
+ operation.values.first,
67
+ server,
68
+ operation_id,
69
+ result_combiner,
70
+ session
71
+ )
72
+ end
68
73
  end
69
74
  end
70
75
  result_combiner.result
@@ -132,7 +137,7 @@ module Mongo
132
137
 
133
138
  private
134
139
 
135
- def base_spec(operation_id)
140
+ def base_spec(operation_id, session)
136
141
  {
137
142
  :db_name => database.name,
138
143
  :coll_name => collection.name,
@@ -141,20 +146,21 @@ module Mongo
141
146
  :operation_id => operation_id,
142
147
  :bypass_document_validation => !!options[:bypass_document_validation],
143
148
  :options => options,
144
- :id_generator => client.options[:id_generator]
149
+ :id_generator => client.options[:id_generator],
150
+ :session => session
145
151
  }
146
152
  end
147
153
 
148
- def execute_operation(name, values, server, operation_id, combiner)
154
+ def execute_operation(name, values, server, operation_id, combiner, session)
149
155
  begin
150
156
  if values.size > server.max_write_batch_size
151
- split_execute(name, values, server, operation_id, combiner)
157
+ split_execute(name, values, server, operation_id, combiner, session)
152
158
  else
153
- combiner.combine!(send(name, values, server, operation_id), values.size)
159
+ combiner.combine!(send(name, values, server, operation_id, session), values.size)
154
160
  end
155
161
  rescue Error::MaxBSONSize, Error::MaxMessageSize => e
156
162
  raise e if values.size <= 1
157
- split_execute(name, values, server, operation_id, combiner)
163
+ split_execute(name, values, server, operation_id, combiner, session)
158
164
  end
159
165
  end
160
166
 
@@ -162,29 +168,29 @@ module Mongo
162
168
  @op_combiner ||= ordered? ? OrderedCombiner.new(requests) : UnorderedCombiner.new(requests)
163
169
  end
164
170
 
165
- def split_execute(name, values, server, operation_id, combiner)
166
- execute_operation(name, values.shift(values.size / 2), server, operation_id, combiner)
167
- execute_operation(name, values, server, operation_id, combiner)
171
+ def split_execute(name, values, server, operation_id, combiner, session)
172
+ execute_operation(name, values.shift(values.size / 2), server, operation_id, combiner, session)
173
+ execute_operation(name, values, server, operation_id, combiner, session)
168
174
  end
169
175
 
170
- def delete(documents, server, operation_id)
176
+ def delete(documents, server, operation_id, session)
171
177
  Operation::Write::Bulk::Delete.new(
172
- base_spec(operation_id).merge(:deletes => documents)
178
+ base_spec(operation_id, session).merge(:deletes => documents)
173
179
  ).execute(server)
174
180
  end
175
181
 
176
182
  alias :delete_one :delete
177
183
  alias :delete_many :delete
178
184
 
179
- def insert_one(documents, server, operation_id)
185
+ def insert_one(documents, server, operation_id, session)
180
186
  Operation::Write::Bulk::Insert.new(
181
- base_spec(operation_id).merge(:documents => documents)
187
+ base_spec(operation_id, session).merge(:documents => documents)
182
188
  ).execute(server)
183
189
  end
184
190
 
185
- def update(documents, server, operation_id)
191
+ def update(documents, server, operation_id, session)
186
192
  Operation::Write::Bulk::Update.new(
187
- base_spec(operation_id).merge(:updates => documents)
193
+ base_spec(operation_id, session).merge(:updates => documents)
188
194
  ).execute(server)
189
195
  end
190
196
 
@@ -28,6 +28,10 @@ module Mongo
28
28
  # @return [ true, false ] has_collation Whether one or more operations has a collation defined.
29
29
  attr_reader :has_collation
30
30
 
31
+ # @return [ true, false ] has_array_filters Whether one or more operations specifies an array
32
+ # filters option.
33
+ attr_reader :has_array_filters
34
+
31
35
  # Create the ordered combiner.
32
36
  #
33
37
  # @api private
@@ -41,6 +45,7 @@ module Mongo
41
45
  def initialize(requests)
42
46
  @requests = requests
43
47
  @has_collation = false
48
+ @has_array_filters = false
44
49
  end
45
50
 
46
51
  private
@@ -104,6 +104,7 @@ module Mongo
104
104
  Operation::UPSERT => doc.fetch(:upsert, false)
105
105
  }.tap do |d|
106
106
  d[Operation::COLLATION] = doc[:collation] if doc[:collation]
107
+ d[Operation::ARRAY_FILTERS] = doc[:array_filters] if doc[:array_filters]
107
108
  end
108
109
  }
109
110
 
@@ -118,6 +119,7 @@ module Mongo
118
119
  Operation::UPSERT => doc.fetch(:upsert, false)
119
120
  }.tap do |d|
120
121
  d[Operation::COLLATION] = doc[:collation] if doc[:collation]
122
+ d[Operation::ARRAY_FILTERS] = doc[:array_filters] if doc[:array_filters]
121
123
  end
122
124
  }
123
125
 
@@ -43,6 +43,10 @@ module Mongo
43
43
  if document.respond_to?(:keys) && (document[:collation] || document[Operation::COLLATION])
44
44
  @has_collation = true
45
45
  end
46
+
47
+ if document.respond_to?(:keys) && document[:array_filters]
48
+ @has_array_filters = true
49
+ end
46
50
  end
47
51
 
48
52
  private
data/lib/mongo/client.rb CHANGED
@@ -38,6 +38,7 @@ module Mongo
38
38
  :auth_source,
39
39
  :connect,
40
40
  :connect_timeout,
41
+ :compressors,
41
42
  :database,
42
43
  :heartbeat_frequency,
43
44
  :id_generator,
@@ -69,9 +70,15 @@ module Mongo
69
70
  :truncate_logs,
70
71
  :user,
71
72
  :wait_queue_timeout,
72
- :write
73
+ :write,
74
+ :zlib_compression_level
73
75
  ].freeze
74
76
 
77
+ # The compression algorithms supported by the driver.
78
+ #
79
+ # @since 2.5.0
80
+ VALID_COMPRESSORS = [ Mongo::Protocol::Compressed::ZLIB ].freeze
81
+
75
82
  # @return [ Mongo::Cluster ] cluster The cluster of servers for the client.
76
83
  attr_reader :cluster
77
84
 
@@ -87,6 +94,8 @@ module Mongo
87
94
  # Delegate subscription to monitoring.
88
95
  def_delegators :@monitoring, :subscribe, :unsubscribe
89
96
 
97
+ def_delegators :@cluster, :logical_session_timeout
98
+
90
99
  # Determine if this client is equivalent to another object.
91
100
  #
92
101
  # @example Check client equality.
@@ -165,6 +174,8 @@ module Mongo
165
174
  # seconds, in the connection pool for a connection to be checked in.
166
175
  # @option options [ Float ] :connect_timeout The timeout, in seconds, to
167
176
  # attempt a connection.
177
+ # @option options [ Array<String> ] :compressors The compressor to use. Currently the driver
178
+ # only supports zlib.
168
179
  # @option options [ Hash ] :read The read preference options. They consist of a
169
180
  # mode specified as a symbol, an array of hashes known as tag_sets,
170
181
  # and local_threshold.
@@ -229,14 +240,32 @@ module Mongo
229
240
  # @since 2.0.0
230
241
  def initialize(addresses_or_uri, options = Options::Redacted.new)
231
242
  @monitoring = Monitoring.new(options)
243
+ Session::SessionPool.create(self)
232
244
  if addresses_or_uri.is_a?(::String)
233
245
  create_from_uri(addresses_or_uri, validate_options!(options))
234
246
  else
235
247
  create_from_addresses(addresses_or_uri, validate_options!(options))
236
248
  end
249
+ ObjectSpace.define_finalizer(self, self.class.finalize(@session_pool))
237
250
  yield(self) if block_given?
238
251
  end
239
252
 
253
+ # Finalize the client for garbage collection. Ends all sessions in the session pool.
254
+ #
255
+ # @example Finalize the client.
256
+ # Client.finalize(session_pool)
257
+ #
258
+ # @param [ Session::SessionPool ] session_pool The session pool.
259
+ #
260
+ # @return [ Proc ] The Finalizer.
261
+ #
262
+ # @since 2.5.0
263
+ def self.finalize(session_pool)
264
+ proc do
265
+ begin; session_pool.end_sessions; rescue; end
266
+ end
267
+ end
268
+
240
269
  # Get an inspection of the client as a string.
241
270
  #
242
271
  # @example Inspect the client.
@@ -249,17 +278,30 @@ module Mongo
249
278
  "#<Mongo::Client:0x#{object_id} cluster=#{cluster.addresses.join(', ')}>"
250
279
  end
251
280
 
281
+ # Get the server selector. It either uses the read preference defined in the client options
282
+ # or defaults to a Primary server selector.
283
+ #
284
+ # @example Get the server selector.
285
+ # client.server_selector
286
+ #
287
+ # @return [ Mongo::ServerSelector ] The server selector using the user-defined read preference
288
+ # or a Primary server selector default.
289
+ #
290
+ # @since 2.5.0
291
+ def server_selector
292
+ @server_selector ||= ServerSelector.get(read_preference || ServerSelector::PRIMARY)
293
+ end
294
+
252
295
  # Get the read preference from the options passed to the client.
253
296
  #
254
297
  # @example Get the read preference.
255
298
  # client.read_preference
256
299
  #
257
- # @return [ Object ] The appropriate read preference or primary if none
258
- # was provided to the client.
300
+ # @return [ BSON::Document ] The user-defined read preference.
259
301
  #
260
302
  # @since 2.0.0
261
303
  def read_preference
262
- @read_preference ||= ServerSelector.get(options[:read] || ServerSelector::PRIMARY)
304
+ @read_preference ||= options[:read]
263
305
  end
264
306
 
265
307
  # Use the database with the provided name. This will switch the current
@@ -294,6 +336,7 @@ module Mongo
294
336
  opts = validate_options!(new_options)
295
337
  client.options.update(opts)
296
338
  Database.create(client)
339
+ Session::SessionPool.create(client)
297
340
  # We can't use the same cluster if some options that would affect it
298
341
  # have changed.
299
342
  if cluster_modifying?(opts)
@@ -363,8 +406,58 @@ module Mongo
363
406
  use(Database::ADMIN).command(listDatabases: 1).first[Database::DATABASES]
364
407
  end
365
408
 
409
+ # Start a session.
410
+ #
411
+ # @example Start a session.
412
+ # client.start_session
413
+ #
414
+ # @param [ Hash ] options The session options.
415
+ #
416
+ # @note A Session cannot be used by multiple threads at once; session objects are not
417
+ # thread-safe.
418
+ #
419
+ # @return [ Session ] The session.
420
+ #
421
+ # @since 2.5.0
422
+ def start_session(options = {})
423
+ if !sessions_supported?
424
+ raise Error::InvalidSession.new(Session::SESSIONS_NOT_SUPPORTED)
425
+ end
426
+ Session.new(@session_pool.checkout, self, options)
427
+ end
428
+
366
429
  private
367
430
 
431
+ def get_session(options = {})
432
+ if options[:session]
433
+ options[:session].validate!(self)
434
+ options[:session]
435
+ elsif sessions_supported?
436
+ Session.new(@session_pool.checkout, self, options.merge(implicit: true))
437
+ end
438
+ end
439
+
440
+ def with_session(options = {})
441
+ if options[:session]
442
+ options[:session].validate!(self)
443
+ yield(options[:session])
444
+ elsif sessions_supported?
445
+ @session_pool.with_session do |server_session|
446
+ yield(Session.new(server_session, self, options))
447
+ end
448
+ else
449
+ yield
450
+ end
451
+ end
452
+
453
+ def sessions_supported?
454
+ if cluster.servers.empty?
455
+ ServerSelector.get(mode: :primary_preferred).select_server(cluster)
456
+ end
457
+ !!logical_session_timeout
458
+ rescue Error::NoServerAvailable
459
+ end
460
+
368
461
  def create_from_addresses(addresses, opts = Options::Redacted.new)
369
462
  @options = Database::DEFAULT_OPTIONS.merge(opts).freeze
370
463
  @cluster = Cluster.new(addresses, @monitoring, options)
@@ -373,8 +466,7 @@ module Mongo
373
466
 
374
467
  def create_from_uri(connection_string, opts = Options::Redacted.new)
375
468
  uri = URI.new(connection_string, opts)
376
- @options = Database::DEFAULT_OPTIONS.merge(uri.client_options.merge(opts)).freeze
377
- validate_options!(@options)
469
+ @options = validate_options!(Database::DEFAULT_OPTIONS.merge(uri.client_options.merge(opts))).freeze
378
470
  @cluster = Cluster.new(uri.servers, @monitoring, options)
379
471
  @database = Database.new(self, options[:database], options)
380
472
  end
@@ -398,18 +490,37 @@ module Mongo
398
490
 
399
491
  def validate_options!(opts = Options::Redacted.new)
400
492
  return Options::Redacted.new unless opts
401
- Options::Redacted.new(opts.select do |o|
402
- if VALID_OPTIONS.include?(o.to_sym)
403
- validate_max_min_pool_size!(o.to_sym, opts) and true
493
+ opts.each.inject(Options::Redacted.new) do |_options, (k, v)|
494
+ key = k.to_sym
495
+ if VALID_OPTIONS.include?(key)
496
+ validate_max_min_pool_size!(key, opts)
497
+ if key == :compressors
498
+ compressors = valid_compressors(v)
499
+ _options[key] = compressors unless compressors.empty?
500
+ else
501
+ _options[key] = v
502
+ end
404
503
  else
405
- log_warn("Unsupported client option '#{o}'. It will be ignored.")
504
+ log_warn("Unsupported client option '#{k}'. It will be ignored.")
505
+ end
506
+ _options
507
+ end
508
+ end
509
+
510
+ def valid_compressors(compressors)
511
+ compressors.select do |compressor|
512
+ if !VALID_COMPRESSORS.include?(compressor)
513
+ log_warn("Unsupported compressor '#{compressor}' in list '#{compressors}'. " +
514
+ "This compressor will not be used.")
406
515
  false
516
+ else
517
+ true
407
518
  end
408
- end)
519
+ end
409
520
  end
410
521
 
411
522
  def validate_max_min_pool_size!(option, opts)
412
- if option == :min_pool_size
523
+ if option == :min_pool_size && opts[:min_pool_size]
413
524
  max = opts[:max_pool_size] || Server::ConnectionPool::Queue::MAX_SIZE
414
525
  raise Error::InvalidMinPoolSize.new(opts[:min_pool_size], max) unless opts[:min_pool_size] <= max
415
526
  end