mongo 2.4.3 → 2.5.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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