mongo 2.13.3 → 2.14.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/mongo/address/ipv4.rb +1 -1
  4. data/lib/mongo/address/ipv6.rb +1 -1
  5. data/lib/mongo/address.rb +1 -1
  6. data/lib/mongo/bulk_write.rb +17 -0
  7. data/lib/mongo/caching_cursor.rb +74 -0
  8. data/lib/mongo/client.rb +47 -8
  9. data/lib/mongo/cluster/topology/single.rb +1 -1
  10. data/lib/mongo/cluster.rb +3 -3
  11. data/lib/mongo/collection/view/aggregation.rb +25 -4
  12. data/lib/mongo/collection/view/builder/find_command.rb +38 -18
  13. data/lib/mongo/collection/view/explainable.rb +27 -8
  14. data/lib/mongo/collection/view/iterable.rb +72 -12
  15. data/lib/mongo/collection/view/readable.rb +12 -2
  16. data/lib/mongo/collection/view/writable.rb +15 -1
  17. data/lib/mongo/collection/view.rb +24 -20
  18. data/lib/mongo/collection.rb +26 -2
  19. data/lib/mongo/crypt/encryption_io.rb +6 -6
  20. data/lib/mongo/cursor.rb +1 -0
  21. data/lib/mongo/database/view.rb +1 -1
  22. data/lib/mongo/database.rb +8 -14
  23. data/lib/mongo/error/invalid_read_concern.rb +28 -0
  24. data/lib/mongo/error/server_certificate_revoked.rb +22 -0
  25. data/lib/mongo/error/unsupported_option.rb +14 -12
  26. data/lib/mongo/error.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +37 -37
  28. data/lib/mongo/lint.rb +2 -1
  29. data/lib/mongo/logger.rb +3 -3
  30. data/lib/mongo/operation/aggregate/result.rb +9 -8
  31. data/lib/mongo/operation/collections_info/command.rb +0 -5
  32. data/lib/mongo/operation/collections_info/result.rb +3 -16
  33. data/lib/mongo/operation/delete/bulk_result.rb +2 -0
  34. data/lib/mongo/operation/delete/result.rb +3 -0
  35. data/lib/mongo/operation/explain/command.rb +4 -0
  36. data/lib/mongo/operation/explain/legacy.rb +4 -0
  37. data/lib/mongo/operation/explain/op_msg.rb +6 -0
  38. data/lib/mongo/operation/explain/result.rb +3 -0
  39. data/lib/mongo/operation/find/legacy/result.rb +2 -0
  40. data/lib/mongo/operation/find/result.rb +3 -0
  41. data/lib/mongo/operation/get_more/result.rb +3 -0
  42. data/lib/mongo/operation/indexes/result.rb +5 -0
  43. data/lib/mongo/operation/insert/bulk_result.rb +5 -0
  44. data/lib/mongo/operation/insert/result.rb +5 -0
  45. data/lib/mongo/operation/list_collections/result.rb +5 -0
  46. data/lib/mongo/operation/map_reduce/result.rb +10 -0
  47. data/lib/mongo/operation/parallel_scan/command.rb +2 -1
  48. data/lib/mongo/operation/parallel_scan/result.rb +4 -0
  49. data/lib/mongo/operation/result.rb +35 -6
  50. data/lib/mongo/operation/shared/bypass_document_validation.rb +1 -0
  51. data/lib/mongo/operation/shared/causal_consistency_supported.rb +1 -0
  52. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +2 -0
  53. data/lib/mongo/operation/shared/executable.rb +1 -0
  54. data/lib/mongo/operation/shared/idable.rb +2 -1
  55. data/lib/mongo/operation/shared/limited.rb +1 -0
  56. data/lib/mongo/operation/shared/object_id_generator.rb +1 -0
  57. data/lib/mongo/operation/shared/read_preference_supported.rb +36 -38
  58. data/lib/mongo/operation/shared/result/aggregatable.rb +1 -0
  59. data/lib/mongo/operation/shared/sessions_supported.rb +3 -3
  60. data/lib/mongo/operation/shared/specifiable.rb +1 -0
  61. data/lib/mongo/operation/shared/write.rb +1 -0
  62. data/lib/mongo/operation/shared/write_concern_supported.rb +1 -0
  63. data/lib/mongo/operation/update/legacy/result.rb +7 -0
  64. data/lib/mongo/operation/update/result.rb +8 -0
  65. data/lib/mongo/operation/users_info/result.rb +3 -0
  66. data/lib/mongo/operation.rb +2 -0
  67. data/lib/mongo/protocol/msg.rb +2 -2
  68. data/lib/mongo/protocol/query.rb +11 -11
  69. data/lib/mongo/query_cache.rb +242 -0
  70. data/lib/mongo/retryable.rb +8 -1
  71. data/lib/mongo/server/connection_common.rb +2 -2
  72. data/lib/mongo/server/connection_pool.rb +3 -0
  73. data/lib/mongo/server/monitor/connection.rb +3 -3
  74. data/lib/mongo/server/monitor.rb +1 -1
  75. data/lib/mongo/server/pending_connection.rb +2 -2
  76. data/lib/mongo/server/push_monitor.rb +1 -1
  77. data/lib/mongo/server.rb +5 -1
  78. data/lib/mongo/server_selector/base.rb +5 -1
  79. data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
  80. data/lib/mongo/session.rb +3 -0
  81. data/lib/mongo/socket/ocsp_cache.rb +97 -0
  82. data/lib/mongo/socket/ocsp_verifier.rb +368 -0
  83. data/lib/mongo/socket/ssl.rb +45 -24
  84. data/lib/mongo/socket.rb +6 -4
  85. data/lib/mongo/srv/monitor.rb +7 -13
  86. data/lib/mongo/srv/resolver.rb +14 -10
  87. data/lib/mongo/timeout.rb +2 -0
  88. data/lib/mongo/uri/options_mapper.rb +582 -0
  89. data/lib/mongo/uri/srv_protocol.rb +3 -2
  90. data/lib/mongo/uri.rb +21 -390
  91. data/lib/mongo/utils.rb +12 -1
  92. data/lib/mongo/version.rb +1 -1
  93. data/lib/mongo.rb +9 -0
  94. data/spec/NOTES.aws-auth.md +12 -7
  95. data/spec/README.md +56 -1
  96. data/spec/integration/bson_symbol_spec.rb +2 -4
  97. data/spec/integration/bulk_write_spec.rb +48 -0
  98. data/spec/integration/client_authentication_options_spec.rb +55 -28
  99. data/spec/integration/connection_pool_populator_spec.rb +3 -1
  100. data/spec/integration/cursor_reaping_spec.rb +53 -17
  101. data/spec/integration/ocsp_connectivity_spec.rb +26 -0
  102. data/spec/integration/ocsp_verifier_cache_spec.rb +188 -0
  103. data/spec/integration/ocsp_verifier_spec.rb +334 -0
  104. data/spec/integration/query_cache_spec.rb +1045 -0
  105. data/spec/integration/query_cache_transactions_spec.rb +179 -0
  106. data/spec/integration/retryable_writes/retryable_writes_40_and_newer_spec.rb +1 -0
  107. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -0
  108. data/spec/integration/sdam_error_handling_spec.rb +69 -18
  109. data/spec/integration/sdam_events_spec.rb +7 -8
  110. data/spec/integration/server_selection_spec.rb +36 -0
  111. data/spec/integration/srv_monitoring_spec.rb +38 -3
  112. data/spec/integration/srv_spec.rb +56 -0
  113. data/spec/lite_spec_helper.rb +4 -2
  114. data/spec/mongo/address_spec.rb +1 -1
  115. data/spec/mongo/caching_cursor_spec.rb +70 -0
  116. data/spec/mongo/client_construction_spec.rb +54 -1
  117. data/spec/mongo/client_encryption_spec.rb +10 -16
  118. data/spec/mongo/client_spec.rb +40 -0
  119. data/spec/mongo/cluster/topology/single_spec.rb +14 -5
  120. data/spec/mongo/cluster_spec.rb +3 -0
  121. data/spec/mongo/collection/view/explainable_spec.rb +87 -4
  122. data/spec/mongo/collection/view/map_reduce_spec.rb +2 -0
  123. data/spec/mongo/collection_spec.rb +60 -0
  124. data/spec/mongo/crypt/auto_decryption_context_spec.rb +1 -1
  125. data/spec/mongo/crypt/auto_encryption_context_spec.rb +1 -1
  126. data/spec/mongo/crypt/data_key_context_spec.rb +1 -1
  127. data/spec/mongo/crypt/explicit_decryption_context_spec.rb +1 -1
  128. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +1 -1
  129. data/spec/mongo/database_spec.rb +44 -64
  130. data/spec/mongo/error/no_server_available_spec.rb +1 -1
  131. data/spec/mongo/index/view_spec.rb +2 -4
  132. data/spec/mongo/logger_spec.rb +13 -11
  133. data/spec/mongo/operation/read_preference_legacy_spec.rb +19 -9
  134. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  135. data/spec/mongo/query_cache_spec.rb +279 -0
  136. data/spec/mongo/server/app_metadata_shared.rb +7 -33
  137. data/spec/mongo/server/connection_pool_spec.rb +7 -3
  138. data/spec/mongo/server/connection_spec.rb +14 -7
  139. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  140. data/spec/mongo/socket/ssl_spec.rb +1 -1
  141. data/spec/mongo/socket_spec.rb +1 -1
  142. data/spec/mongo/uri/srv_protocol_spec.rb +64 -33
  143. data/spec/mongo/uri_option_parsing_spec.rb +11 -11
  144. data/spec/mongo/uri_spec.rb +68 -41
  145. data/spec/mongo/utils_spec.rb +39 -0
  146. data/spec/runners/auth.rb +3 -0
  147. data/spec/runners/connection_string.rb +35 -124
  148. data/spec/runners/transactions/operation.rb +2 -13
  149. data/spec/spec_tests/cmap_spec.rb +7 -3
  150. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +0 -1
  151. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  152. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +6 -2
  153. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +3 -0
  154. data/spec/spec_tests/data/connection_string/valid-warnings.yml +24 -0
  155. data/spec/spec_tests/data/sdam_monitoring/discovered_standalone.yml +1 -3
  156. data/spec/spec_tests/data/sdam_monitoring/standalone.yml +2 -2
  157. data/spec/spec_tests/data/sdam_monitoring/standalone_repeated.yml +2 -2
  158. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +2 -2
  159. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +2 -2
  160. data/spec/spec_tests/data/uri_options/auth-options.yml +25 -0
  161. data/spec/spec_tests/data/uri_options/compression-options.yml +6 -3
  162. data/spec/spec_tests/data/uri_options/read-preference-options.yml +24 -0
  163. data/spec/spec_tests/data/uri_options/ruby-connection-options.yml +1 -0
  164. data/spec/spec_tests/data/uri_options/tls-options.yml +160 -4
  165. data/spec/spec_tests/dns_seedlist_discovery_spec.rb +9 -1
  166. data/spec/spec_tests/uri_options_spec.rb +31 -33
  167. data/spec/support/certificates/atlas-ocsp-ca.crt +28 -0
  168. data/spec/support/certificates/atlas-ocsp.crt +41 -0
  169. data/spec/support/client_registry.rb +4 -8
  170. data/spec/support/client_registry_macros.rb +4 -4
  171. data/spec/support/common_shortcuts.rb +45 -0
  172. data/spec/support/constraints.rb +23 -0
  173. data/spec/support/lite_constraints.rb +24 -0
  174. data/spec/support/matchers.rb +16 -0
  175. data/spec/support/ocsp +1 -0
  176. data/spec/support/session_registry.rb +52 -0
  177. data/spec/support/spec_config.rb +22 -12
  178. data/spec/support/spec_setup.rb +38 -48
  179. data/spec/support/utils.rb +19 -1
  180. data.tar.gz.sig +1 -3
  181. metadata +938 -933
  182. metadata.gz.sig +0 -0
  183. data/spec/integration/secondary_reads_spec.rb +0 -102
  184. data/spec/shared/LICENSE +0 -20
  185. data/spec/shared/bin/get-mongodb-download-url +0 -17
  186. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  187. data/spec/shared/lib/mrss/cluster_config.rb +0 -221
  188. data/spec/shared/lib/mrss/constraints.rb +0 -346
  189. data/spec/shared/lib/mrss/docker_runner.rb +0 -265
  190. data/spec/shared/lib/mrss/lite_constraints.rb +0 -191
  191. data/spec/shared/lib/mrss/server_version_registry.rb +0 -115
  192. data/spec/shared/lib/mrss/spec_organizer.rb +0 -152
  193. data/spec/shared/lib/mrss/utils.rb +0 -15
  194. data/spec/shared/share/Dockerfile.erb +0 -231
  195. data/spec/shared/shlib/distro.sh +0 -73
  196. data/spec/shared/shlib/server.sh +0 -290
  197. data/spec/shared/shlib/set_env.sh +0 -128
@@ -0,0 +1,1045 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'QueryCache' do
4
+ around do |spec|
5
+ Mongo::QueryCache.clear
6
+ Mongo::QueryCache.cache { spec.run }
7
+ end
8
+
9
+ before do
10
+ authorized_collection.delete_many
11
+ subscriber.clear_events!
12
+ end
13
+
14
+ before(:all) do
15
+ # It is likely that there are other session leaks in the driver that are
16
+ # unrelated to the query cache. Clear the SessionRegistry at the start of
17
+ # these tests in order to detect leaks that occur only within the scope of
18
+ # these tests.
19
+ #
20
+ # Other session leaks will be detected and addressed as part of RUBY-2391.
21
+ SessionRegistry.instance.clear_registry
22
+ end
23
+
24
+ after do
25
+ SessionRegistry.instance.verify_sessions_ended!
26
+ end
27
+
28
+ let(:subscriber) { EventSubscriber.new }
29
+
30
+ let(:client) do
31
+ authorized_client.tap do |client|
32
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
33
+ end
34
+ end
35
+
36
+ let(:authorized_collection) { client['collection_spec'] }
37
+
38
+ let(:events) do
39
+ subscriber.command_started_events('find')
40
+ end
41
+
42
+ describe '#cache' do
43
+
44
+ before do
45
+ Mongo::QueryCache.enabled = false
46
+ authorized_collection.insert_one({ name: 'testing' })
47
+ authorized_collection.find(name: 'testing').to_a
48
+ end
49
+
50
+ it 'enables the query cache inside the block' do
51
+ Mongo::QueryCache.cache do
52
+ authorized_collection.find(name: 'testing').to_a
53
+ expect(Mongo::QueryCache.enabled?).to be(true)
54
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
55
+ expect(events.length).to eq(2)
56
+ end
57
+ authorized_collection.find(name: 'testing').to_a
58
+ expect(Mongo::QueryCache.enabled?).to be(false)
59
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
60
+ expect(events.length).to eq(2)
61
+ end
62
+ end
63
+
64
+ describe '#uncached' do
65
+
66
+ before do
67
+ authorized_collection.insert_one({ name: 'testing' })
68
+ authorized_collection.find(name: 'testing').to_a
69
+ end
70
+
71
+ it 'disables the query cache inside the block' do
72
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
73
+ Mongo::QueryCache.uncached do
74
+ authorized_collection.find(name: 'testing').to_a
75
+ expect(Mongo::QueryCache.enabled?).to be(false)
76
+ expect(events.length).to eq(2)
77
+ end
78
+ authorized_collection.find(name: 'testing').to_a
79
+ expect(Mongo::QueryCache.enabled?).to be(true)
80
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
81
+ expect(events.length).to eq(2)
82
+ end
83
+ end
84
+
85
+ describe 'query with multiple batches' do
86
+ min_server_fcv '3.2'
87
+
88
+ before do
89
+ 102.times { |i| authorized_collection.insert_one(_id: i) }
90
+ end
91
+
92
+ let(:expected_results) { [*0..101].map { |id| { "_id" => id } } }
93
+
94
+ it 'returns the correct result' do
95
+ result = authorized_collection.find.to_a
96
+ expect(result.length).to eq(102)
97
+ expect(result).to eq(expected_results)
98
+ end
99
+
100
+ it 'returns the correct result multiple times' do
101
+ result1 = authorized_collection.find.to_a
102
+ result2 = authorized_collection.find.to_a
103
+ expect(result1).to eq(expected_results)
104
+ expect(result2).to eq(expected_results)
105
+ end
106
+
107
+ it 'caches the query' do
108
+ authorized_collection.find.to_a
109
+ authorized_collection.find.to_a
110
+ expect(subscriber.command_started_events('find').length).to eq(1)
111
+ expect(subscriber.command_started_events('getMore').length).to eq(1)
112
+ end
113
+
114
+ it 'uses cached cursor when limited' do
115
+ authorized_collection.find.to_a
116
+ result = authorized_collection.find({}, limit: 5).to_a
117
+
118
+ expect(result.length).to eq(5)
119
+ expect(result).to eq(expected_results.first(5))
120
+
121
+ expect(subscriber.command_started_events('find').length).to eq(1)
122
+ expect(subscriber.command_started_events('getMore').length).to eq(1)
123
+ end
124
+
125
+ it 'can be used with a block API' do
126
+ authorized_collection.find.to_a
127
+
128
+ result = []
129
+ authorized_collection.find.each do |doc|
130
+ result << doc
131
+ end
132
+
133
+ expect(result).to eq(expected_results)
134
+
135
+ expect(subscriber.command_started_events('find').length).to eq(1)
136
+ expect(subscriber.command_started_events('getMore').length).to eq(1)
137
+ end
138
+
139
+ context 'when the cursor isn\'t fully iterated the first time' do
140
+ it 'continues iterating' do
141
+ result1 = authorized_collection.find.first(5)
142
+
143
+ expect(result1.length).to eq(5)
144
+ expect(result1).to eq(expected_results.first(5))
145
+
146
+ expect(subscriber.command_started_events('find').length).to eq(1)
147
+ expect(subscriber.command_started_events('getMore').length).to eq(0)
148
+
149
+ result2 = authorized_collection.find.to_a
150
+
151
+ expect(result2.length).to eq(102)
152
+ expect(result2).to eq(expected_results)
153
+
154
+ expect(subscriber.command_started_events('find').length).to eq(1)
155
+ expect(subscriber.command_started_events('getMore').length).to eq(1)
156
+ end
157
+
158
+ it 'can be iterated multiple times' do
159
+ authorized_collection.find.first(5)
160
+ authorized_collection.find.to_a
161
+
162
+ result = authorized_collection.find.to_a
163
+
164
+ expect(result.length).to eq(102)
165
+ expect(result).to eq(expected_results)
166
+
167
+ expect(subscriber.command_started_events('find').length).to eq(1)
168
+ expect(subscriber.command_started_events('getMore').length).to eq(1)
169
+ end
170
+
171
+ it 'can be used with a block API' do
172
+ authorized_collection.find.first(5)
173
+
174
+ result = []
175
+ authorized_collection.find.each do |doc|
176
+ result << doc
177
+ end
178
+
179
+ expect(result.length).to eq(102)
180
+ expect(result).to eq(expected_results)
181
+
182
+ expect(subscriber.command_started_events('find').length).to eq(1)
183
+ expect(subscriber.command_started_events('getMore').length).to eq(1)
184
+ end
185
+ end
186
+ end
187
+
188
+ describe 'queries with read concern' do
189
+ require_wired_tiger
190
+ min_server_fcv '3.6'
191
+
192
+ before do
193
+ authorized_client['test'].drop
194
+ end
195
+
196
+ context 'when two queries have same read concern' do
197
+ before do
198
+ authorized_client['test', read_concern: { level: :majority }].find.to_a
199
+ authorized_client['test', read_concern: { level: :majority }].find.to_a
200
+ end
201
+
202
+ it 'executes one query' do
203
+ expect(events.length).to eq(1)
204
+ end
205
+ end
206
+
207
+ context 'when two queries have different read concerns' do
208
+ before do
209
+ authorized_client['test', read_concern: { level: :majority }].find.to_a
210
+ authorized_client['test', read_concern: { level: :local }].find.to_a
211
+ end
212
+
213
+ it 'executes two queries' do
214
+ expect(events.length).to eq(2)
215
+ end
216
+ end
217
+ end
218
+
219
+ describe 'queries with read preference' do
220
+ before do
221
+ subscriber.clear_events!
222
+ authorized_client['test'].drop
223
+ end
224
+
225
+ context 'when two queries have different read preferences' do
226
+ before do
227
+ authorized_client['test', read: { mode: :primary }].find.to_a
228
+ authorized_client['test', read: { mode: :primary_preferred }].find.to_a
229
+ end
230
+
231
+ it 'executes two queries' do
232
+ expect(events.length).to eq(2)
233
+ end
234
+ end
235
+
236
+ context 'when two queries have same read preference' do
237
+ before do
238
+ authorized_client['test', read: { mode: :primary }].find.to_a
239
+ authorized_client['test', read: { mode: :primary }].find.to_a
240
+ end
241
+
242
+ it 'executes one query' do
243
+ expect(events.length).to eq(1)
244
+ end
245
+ end
246
+ end
247
+
248
+ describe 'query fills up entire batch' do
249
+ before do
250
+ subscriber.clear_events!
251
+ authorized_client['test'].drop
252
+
253
+ 2.times { |i| authorized_client['test'].insert_one(_id: i) }
254
+ end
255
+
256
+ let(:expected_result) do
257
+ [{ "_id" => 0 }, { "_id" => 1 }]
258
+ end
259
+
260
+ # When the last batch runs out, try_next will return nil instead of a
261
+ # document. This test checks that nil is not added to the list of cached
262
+ # documents or returned as a result.
263
+ it 'returns the correct response' do
264
+ expect(authorized_client['test'].find({}, batch_size: 2).to_a).to eq(expected_result)
265
+ expect(authorized_client['test'].find({}, batch_size: 2).to_a).to eq(expected_result)
266
+ end
267
+ end
268
+
269
+ context 'when querying in the same collection' do
270
+
271
+ before do
272
+ 10.times do |i|
273
+ authorized_collection.insert_one(test: i)
274
+ end
275
+ end
276
+
277
+ context 'when query cache is disabled' do
278
+
279
+ before do
280
+ Mongo::QueryCache.enabled = false
281
+ authorized_collection.find(test: 1).to_a
282
+ end
283
+
284
+ it 'queries again' do
285
+ authorized_collection.find(test: 1).to_a
286
+ expect(events.length).to eq(2)
287
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(0)
288
+ end
289
+ end
290
+
291
+ context 'when query cache is enabled' do
292
+
293
+ before do
294
+ authorized_collection.find(test: 1).to_a
295
+ end
296
+
297
+ it 'does not query again' do
298
+ authorized_collection.find(test: 1).to_a
299
+ expect(events.length).to eq(1)
300
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
301
+ end
302
+ end
303
+
304
+ context 'when query has collation' do
305
+ min_server_fcv '3.4'
306
+
307
+ let(:options1) do
308
+ { :collation => { locale: 'fr' } }
309
+ end
310
+
311
+ let(:options2) do
312
+ { collation: { locale: 'en_US' } }
313
+ end
314
+
315
+ before do
316
+ authorized_collection.find({ test: 3 }, options1).to_a
317
+ end
318
+
319
+ context 'when query has the same collation' do
320
+
321
+ it 'uses the cache' do
322
+ authorized_collection.find({ test: 3 }, options1).to_a
323
+ expect(events.length).to eq(1)
324
+ end
325
+ end
326
+
327
+ context 'when query has a different collation' do
328
+
329
+ it 'queries again' do
330
+ authorized_collection.find({ test: 3 }, options2).to_a
331
+ expect(events.length).to eq(2)
332
+ expect(Mongo::QueryCache.send(:cache_table)['ruby-driver.collection_spec'].length).to eq(2)
333
+ end
334
+ end
335
+ end
336
+
337
+ describe 'queries with limits' do
338
+ context 'when the first query has no limit and the second does' do
339
+ before do
340
+ authorized_collection.find.to_a.count
341
+ end
342
+
343
+ it 'uses the cache' do
344
+ results_limit_5 = authorized_collection.find.limit(5).to_a
345
+ results_limit_3 = authorized_collection.find.limit(3).to_a
346
+ results_no_limit = authorized_collection.find.to_a
347
+
348
+ expect(results_limit_5.length).to eq(5)
349
+ expect(results_limit_5.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4])
350
+
351
+ expect(results_limit_3.length).to eq(3)
352
+ expect(results_limit_3.map { |r| r["test"] }).to eq([0, 1, 2])
353
+
354
+ expect(results_no_limit.length).to eq(10)
355
+ expect(results_no_limit.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
356
+
357
+ expect(events.length).to eq(1)
358
+ end
359
+ end
360
+
361
+ context 'when the first query has a limit' do
362
+ before do
363
+ authorized_collection.find.limit(2).to_a
364
+ end
365
+
366
+ context 'and the second query has a larger limit' do
367
+ let(:results) { authorized_collection.find.limit(3).to_a }
368
+
369
+ it 'queries again' do
370
+ expect(results.length).to eq(3)
371
+ expect(results.map { |result| result["test"] }).to eq([0, 1, 2])
372
+ expect(events.length).to eq(2)
373
+ end
374
+ end
375
+
376
+ context 'and two queries are performed with a larger limit' do
377
+ it 'uses the query cache for the third query' do
378
+ results1 = authorized_collection.find.limit(3).to_a
379
+ results2 = authorized_collection.find.limit(3).to_a
380
+
381
+ expect(results1.length).to eq(3)
382
+ expect(results1.map { |r| r["test"] }).to eq([0, 1, 2])
383
+
384
+ expect(results2.length).to eq(3)
385
+ expect(results2.map { |r| r["test"] }).to eq([0, 1, 2])
386
+
387
+ expect(events.length).to eq(2)
388
+ end
389
+ end
390
+
391
+ context 'and the second query has a smaller limit' do
392
+ let(:results) { authorized_collection.find.limit(1).to_a }
393
+
394
+ it 'uses the cached query' do
395
+ expect(results.count).to eq(1)
396
+ expect(results.first["test"]).to eq(0)
397
+ expect(events.length).to eq(1)
398
+ end
399
+ end
400
+
401
+ context 'and the second query has no limit' do
402
+ it 'queries again' do
403
+ expect(authorized_collection.find.to_a.count).to eq(10)
404
+ expect(events.length).to eq(2)
405
+ end
406
+ end
407
+ end
408
+ end
409
+
410
+ context 'when querying only the first' do
411
+
412
+ before do
413
+ 5.times do |i|
414
+ authorized_collection.insert_one(test: 11)
415
+ end
416
+ end
417
+
418
+ before do
419
+ authorized_collection.find({test: 11}).to_a
420
+ end
421
+
422
+ it 'does not query again' do
423
+ expect(authorized_collection.find({test: 11}).count).to eq(5)
424
+ authorized_collection.find({test: 11}).first
425
+ expect(events.length).to eq(1)
426
+ end
427
+
428
+ context 'when limiting the result' do
429
+
430
+ it 'does not query again' do
431
+ authorized_collection.find({test: 11}, limit: 2).to_a
432
+ expect(authorized_collection.find({test: 11}, limit: 2).to_a.count).to eq(2)
433
+ expect(events.length).to eq(1)
434
+ end
435
+ end
436
+ end
437
+
438
+ context 'when specifying a different skip value' do
439
+
440
+ before do
441
+ authorized_collection.find({}, {limit: 2, skip: 3}).to_a
442
+ end
443
+
444
+ it 'queries again' do
445
+ results = authorized_collection.find({}, {limit: 2, skip: 5}).to_a
446
+ expect(results.count).to eq(2)
447
+ expect(events.length).to eq(2)
448
+ end
449
+ end
450
+
451
+ context 'when sorting documents' do
452
+
453
+ before do
454
+ authorized_collection.find({}, desc).to_a
455
+ end
456
+
457
+ let(:desc) do
458
+ { sort: {test: -1} }
459
+ end
460
+
461
+ let(:asc) do
462
+ { sort: {test: 1} }
463
+ end
464
+
465
+ context 'with different selector' do
466
+
467
+ it 'queries again' do
468
+ authorized_collection.find({}, asc).to_a
469
+ expect(events.length).to eq(2)
470
+ end
471
+ end
472
+
473
+ it 'does not query again' do
474
+ authorized_collection.find({}, desc).to_a
475
+ expect(events.length).to eq(1)
476
+ end
477
+ end
478
+
479
+ context 'when inserting new documents' do
480
+ context 'when inserting and querying from same collection' do
481
+ before do
482
+ authorized_collection.find.to_a
483
+ authorized_collection.insert_one({ name: "bob" })
484
+ end
485
+
486
+ it 'queries again' do
487
+ authorized_collection.find.to_a
488
+ expect(events.length).to eq(2)
489
+ end
490
+ end
491
+
492
+ context 'when inserting and querying from different collections' do
493
+ before do
494
+ authorized_collection.find.to_a
495
+ authorized_client['different_collection'].insert_one({ name: "bob" })
496
+ end
497
+
498
+ it 'uses the cached query' do
499
+ authorized_collection.find.to_a
500
+ expect(events.length).to eq(1)
501
+ end
502
+ end
503
+ end
504
+
505
+ [:delete_many, :delete_one].each do |method|
506
+ context "when deleting with #{method}" do
507
+ context 'when deleting and querying from same collection' do
508
+ before do
509
+ authorized_collection.find.to_a
510
+ authorized_collection.send(method)
511
+ end
512
+
513
+ it 'queries again' do
514
+ authorized_collection.find.to_a
515
+ expect(events.length).to eq(2)
516
+ end
517
+ end
518
+
519
+ context 'when deleting and querying from different collections' do
520
+ before do
521
+ authorized_collection.find.to_a
522
+ authorized_client['different_collection'].send(method)
523
+ end
524
+
525
+ it 'uses the cached query' do
526
+ authorized_collection.find.to_a
527
+ expect(events.length).to eq(1)
528
+ end
529
+ end
530
+ end
531
+ end
532
+
533
+ [:find_one_and_delete, :find_one_and_replace, :find_one_and_update,
534
+ :update_one, :replace_one].each do |method|
535
+ context "when updating with #{method}" do
536
+ context 'when updating and querying from same collection' do
537
+ before do
538
+ authorized_collection.find.to_a
539
+ authorized_collection.send(method, { field: 'value' }, { field: 'new value' })
540
+ end
541
+
542
+ it 'queries again' do
543
+ authorized_collection.find.to_a
544
+ expect(events.length).to eq(2)
545
+ end
546
+ end
547
+
548
+ context 'when updating and querying from different collections' do
549
+ before do
550
+ authorized_collection.find.to_a
551
+ authorized_client['different_collection'].send(method, { field: 'value' }, { field: 'new value' })
552
+ end
553
+
554
+ it 'uses the cached query' do
555
+ authorized_collection.find.to_a
556
+ expect(events.length).to eq(1)
557
+ end
558
+ end
559
+ end
560
+ end
561
+
562
+ context 'when updating with #update_many' do
563
+ context 'when updating and querying from same collection' do
564
+ before do
565
+ authorized_collection.find.to_a
566
+ authorized_collection.update_many({ field: 'value' }, { "$inc" => { :field => 1 } })
567
+ end
568
+
569
+ it 'queries again' do
570
+ authorized_collection.find.to_a
571
+ expect(events.length).to eq(2)
572
+ end
573
+ end
574
+
575
+ context 'when updating and querying from different collections' do
576
+ before do
577
+ authorized_collection.find.to_a
578
+ authorized_client['different_collection'].update_many({ field: 'value' }, { "$inc" => { :field => 1 } })
579
+ end
580
+
581
+ it 'uses the cached query' do
582
+ authorized_collection.find.to_a
583
+ expect(events.length).to eq(1)
584
+ end
585
+ end
586
+ end
587
+
588
+ context 'when performing bulk write' do
589
+ context 'with insert_one' do
590
+ context 'when inserting and querying from same collection' do
591
+ before do
592
+ authorized_collection.find.to_a
593
+ authorized_collection.bulk_write([ { insert_one: { name: 'bob' } } ])
594
+ end
595
+
596
+ it 'queries again' do
597
+ authorized_collection.find.to_a
598
+ expect(events.length).to eq(2)
599
+ end
600
+ end
601
+
602
+ context 'when inserting and querying from different collection' do
603
+ before do
604
+ authorized_collection.find.to_a
605
+ authorized_client['different_collection'].bulk_write(
606
+ [ { insert_one: { name: 'bob' } } ]
607
+ )
608
+ end
609
+
610
+ it 'uses the cached query' do
611
+ authorized_collection.find.to_a
612
+ expect(events.length).to eq(1)
613
+ end
614
+ end
615
+ end
616
+
617
+ [:update_one, :update_many].each do |method|
618
+ context "with #{method}" do
619
+ context 'when updating and querying from same collection' do
620
+ before do
621
+ authorized_collection.find.to_a
622
+ authorized_collection.bulk_write([
623
+ {
624
+ method => {
625
+ filter: { field: 'value' },
626
+ update: { '$set' => { field: 'new value' } }
627
+ }
628
+ }
629
+ ])
630
+ end
631
+
632
+ it 'queries again' do
633
+ authorized_collection.find.to_a
634
+ expect(events.length).to eq(2)
635
+ end
636
+ end
637
+
638
+ context 'when updating and querying from different collection' do
639
+ before do
640
+ authorized_collection.find.to_a
641
+ authorized_client['different_collection'].bulk_write([
642
+ {
643
+ method => {
644
+ filter: { field: 'value' },
645
+ update: { '$set' => { field: 'new value' } }
646
+ }
647
+ }
648
+ ])
649
+ end
650
+
651
+ it 'uses the cached query' do
652
+ authorized_collection.find.to_a
653
+ expect(events.length).to eq(1)
654
+ end
655
+ end
656
+ end
657
+ end
658
+
659
+ [:delete_one, :delete_many].each do |method|
660
+ context "with #{method}" do
661
+ context 'when delete and querying from same collection' do
662
+ before do
663
+ authorized_collection.find.to_a
664
+ authorized_collection.bulk_write([
665
+ {
666
+ method => {
667
+ filter: { field: 'value' },
668
+ }
669
+ }
670
+ ])
671
+ end
672
+
673
+ it 'queries again' do
674
+ authorized_collection.find.to_a
675
+ expect(events.length).to eq(2)
676
+ end
677
+ end
678
+
679
+ context 'when delete and querying from different collection' do
680
+ before do
681
+ authorized_collection.find.to_a
682
+ authorized_client['different_collection'].bulk_write([
683
+ {
684
+ method => {
685
+ filter: { field: 'value' },
686
+ }
687
+ }
688
+ ])
689
+ end
690
+
691
+ it 'uses the cached query' do
692
+ authorized_collection.find.to_a
693
+ expect(events.length).to eq(1)
694
+ end
695
+ end
696
+ end
697
+ end
698
+
699
+ context 'with replace_one' do
700
+ context 'when replacing and querying from same collection' do
701
+ before do
702
+ authorized_collection.find.to_a
703
+ authorized_collection.bulk_write([
704
+ {
705
+ replace_one: {
706
+ filter: { field: 'value' },
707
+ replacement: { field: 'new value' }
708
+ }
709
+ }
710
+ ])
711
+ end
712
+
713
+ it 'queries again' do
714
+ authorized_collection.find.to_a
715
+ expect(events.length).to eq(2)
716
+ end
717
+ end
718
+
719
+ context 'when replacing and querying from different collection' do
720
+ before do
721
+ authorized_collection.find.to_a
722
+ authorized_client['different_collection'].bulk_write([
723
+ {
724
+ replace_one: {
725
+ filter: { field: 'value' },
726
+ replacement: { field: 'new value' }
727
+ }
728
+ }
729
+ ])
730
+ end
731
+
732
+ it 'uses the cached query' do
733
+ authorized_collection.find.to_a
734
+ expect(events.length).to eq(1)
735
+ end
736
+ end
737
+ end
738
+
739
+ context 'when query occurs between bulk write creation and execution' do
740
+ before do
741
+ authorized_collection.delete_many
742
+ end
743
+
744
+ it 'queries again' do
745
+ bulk_write = Mongo::BulkWrite.new(
746
+ authorized_collection,
747
+ [{ insert_one: { test: 1 } }]
748
+ )
749
+
750
+ expect(authorized_collection.find(test: 1).to_a.length).to eq(0)
751
+ bulk_write.execute
752
+ expect(authorized_collection.find(test: 1).to_a.length).to eq(1)
753
+ expect(events.length).to eq(2)
754
+ end
755
+ end
756
+ end
757
+
758
+ context 'when aggregating with $out' do
759
+ before do
760
+ authorized_collection.find.to_a
761
+ authorized_collection.aggregate([
762
+ { '$match' => { test: 1 } },
763
+ { '$out' => { coll: 'new_coll' } }
764
+ ])
765
+ end
766
+
767
+ it 'queries again' do
768
+ authorized_collection.find.to_a
769
+ expect(events.length).to eq(2)
770
+ end
771
+
772
+ it 'clears the cache' do
773
+ expect(Mongo::QueryCache.send(:cache_table)).to be_empty
774
+ end
775
+ end
776
+
777
+ context 'when aggregating with $merge' do
778
+ min_server_fcv '4.2'
779
+
780
+ before do
781
+ authorized_collection.delete_many
782
+ authorized_collection.find.to_a
783
+ authorized_collection.aggregate([
784
+ { '$match' => { 'test' => 1 } },
785
+ { '$merge' => {
786
+ into: {
787
+ db: SpecConfig.instance.test_db,
788
+ coll: 'new_coll',
789
+ },
790
+ on: "_id",
791
+ whenMatched: "replace",
792
+ whenNotMatched: "insert",
793
+ }
794
+ }
795
+ ])
796
+ end
797
+
798
+ it 'queries again' do
799
+ authorized_collection.find.to_a
800
+ expect(events.length).to eq(2)
801
+ end
802
+
803
+ it 'clears the cache' do
804
+ expect(Mongo::QueryCache.send(:cache_table)).to be_empty
805
+ end
806
+ end
807
+ end
808
+
809
+ context 'when aggregating' do
810
+ before do
811
+ 3.times { authorized_collection.insert_one(test: 1) }
812
+ end
813
+
814
+ let(:events) do
815
+ subscriber.command_started_events('aggregate')
816
+ end
817
+
818
+ let(:aggregation) do
819
+ authorized_collection.aggregate([ { '$match' => { test: 1 } } ])
820
+ end
821
+
822
+ it 'caches the aggregation' do
823
+ expect(aggregation.to_a.length).to eq(3)
824
+ expect(aggregation.to_a.length).to eq(3)
825
+ expect(events.length).to eq(1)
826
+ end
827
+
828
+ context 'with read concern' do
829
+ require_wired_tiger
830
+ min_server_fcv '3.6'
831
+
832
+ let(:aggregation_read_concern) do
833
+ authorized_client['collection_spec', { read_concern: { level: :local } }]
834
+ .aggregate([ { '$match' => { test: 1 } } ])
835
+ end
836
+
837
+ it 'queries twice' do
838
+ expect(aggregation.to_a.length).to eq(3)
839
+ expect(aggregation_read_concern.to_a.length).to eq(3)
840
+ expect(events.length).to eq(2)
841
+ end
842
+ end
843
+
844
+ context 'with read preference' do
845
+ let(:aggregation_read_preference) do
846
+ authorized_client['collection_spec', { read: { mode: :primary } }]
847
+ .aggregate([ { '$match' => { test: 1 } } ])
848
+ end
849
+
850
+ it 'queries twice' do
851
+ expect(aggregation.to_a.length).to eq(3)
852
+ expect(aggregation_read_preference.to_a.length).to eq(3)
853
+ expect(events.length).to eq(2)
854
+ end
855
+ end
856
+
857
+ context 'when collation is specified' do
858
+ min_server_fcv '3.4'
859
+
860
+ let(:aggregation_collation) do
861
+ authorized_collection.aggregate(
862
+ [ { '$match' => { test: 1 } } ],
863
+ { collation: { locale: 'fr' } }
864
+ )
865
+ end
866
+
867
+ it 'queries twice' do
868
+ expect(aggregation.to_a.length).to eq(3)
869
+ expect(aggregation_collation.to_a.length).to eq(3)
870
+ expect(events.length).to eq(2)
871
+ end
872
+ end
873
+
874
+ context 'when insert_one is performed on another collection' do
875
+ before do
876
+ aggregation.to_a
877
+ authorized_client['different_collection'].insert_one(name: 'bob')
878
+ aggregation.to_a
879
+ end
880
+
881
+ it 'queries again' do
882
+ expect(events.length).to eq(2)
883
+ end
884
+ end
885
+
886
+ context 'when insert_many is performed on another collection' do
887
+ before do
888
+ aggregation.to_a
889
+ authorized_client['different_collection'].insert_many([name: 'bob'])
890
+ aggregation.to_a
891
+ end
892
+
893
+ it 'queries again' do
894
+ expect(events.length).to eq(2)
895
+ end
896
+ end
897
+
898
+ [:delete_many, :delete_one].each do |method|
899
+ context "when #{method} is performed on another collection" do
900
+ before do
901
+ aggregation.to_a
902
+ authorized_client['different_collection'].send(method)
903
+ aggregation.to_a
904
+ end
905
+
906
+ it 'queries again' do
907
+ expect(events.length).to eq(2)
908
+ end
909
+ end
910
+ end
911
+
912
+ [:find_one_and_delete, :find_one_and_replace, :find_one_and_update,
913
+ :update_one, :replace_one].each do |method|
914
+ context "when #{method} is performed on another collection" do
915
+ before do
916
+ aggregation.to_a
917
+ authorized_client['different_collection'].send(method, { field: 'value' }, { field: 'new value' })
918
+ aggregation.to_a
919
+ end
920
+
921
+ it 'queries again' do
922
+ expect(events.length).to eq(2)
923
+ end
924
+ end
925
+ end
926
+
927
+ context 'when update_many is performed on another collection' do
928
+ before do
929
+ aggregation.to_a
930
+ authorized_client['different_collection'].update_many({ field: 'value' }, { "$inc" => { :field => 1 } })
931
+ aggregation.to_a
932
+ end
933
+
934
+ it 'queries again' do
935
+ expect(events.length).to eq(2)
936
+ end
937
+ end
938
+
939
+ context '#count_documents' do
940
+ context 'on same collection' do
941
+ it 'caches the query' do
942
+ expect(authorized_collection.count_documents(test: 1)).to eq(3)
943
+ expect(authorized_collection.count_documents(test: 1)).to eq(3)
944
+
945
+ expect(events.length).to eq(1)
946
+ end
947
+ end
948
+
949
+ context 'on different collections' do
950
+ let(:other_collection) { authorized_client['other_collection'] }
951
+
952
+ before do
953
+ other_collection.drop
954
+ 6.times { other_collection.insert_one(test: 1) }
955
+ end
956
+
957
+ it 'caches the query' do
958
+ expect(authorized_collection.count_documents(test: 1)).to eq(3)
959
+ expect(other_collection.count_documents(test: 1)).to eq(6)
960
+
961
+ expect(events.length).to eq(2)
962
+ end
963
+ end
964
+ end
965
+ end
966
+
967
+ context 'when find command fails and retries' do
968
+ require_fail_command
969
+ require_no_multi_shard
970
+ require_warning_clean
971
+
972
+ before do
973
+ 5.times do |i|
974
+ authorized_collection.insert_one(test: i)
975
+ end
976
+ end
977
+
978
+ before do
979
+ client.use('admin').command(
980
+ configureFailPoint: 'failCommand',
981
+ mode: { times: 1 },
982
+ data: {
983
+ failCommands: ['find'],
984
+ closeConnection: true
985
+ }
986
+ )
987
+ end
988
+
989
+ let(:command_name) { 'find' }
990
+
991
+ it 'uses modern retryable reads when using query cache' do
992
+ expect(Mongo::QueryCache.enabled?).to be(true)
993
+
994
+ expect(Mongo::Logger.logger).to receive(:warn).once.with(/modern.*attempt 1/).and_call_original
995
+ authorized_collection.find(test: 1).to_a
996
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
997
+ expect(subscriber.command_started_events('find').length).to eq(2)
998
+
999
+ authorized_collection.find(test: 1).to_a
1000
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(1)
1001
+ expect(subscriber.command_started_events('find').length).to eq(2)
1002
+ end
1003
+ end
1004
+
1005
+ context 'when querying in a different collection' do
1006
+
1007
+ let(:database) { client.database }
1008
+
1009
+ let(:new_collection) do
1010
+ Mongo::Collection.new(database, 'foo')
1011
+ end
1012
+
1013
+ before do
1014
+ authorized_collection.find.to_a
1015
+ end
1016
+
1017
+ it 'queries again' do
1018
+ new_collection.find.to_a
1019
+ expect(Mongo::QueryCache.send(:cache_table).length).to eq(2)
1020
+ expect(events.length).to eq(2)
1021
+ end
1022
+ end
1023
+
1024
+ context 'with system collection' do
1025
+ let(:client) do
1026
+ ClientRegistry.instance.global_client('root_authorized').tap do |client|
1027
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
1028
+ end
1029
+ end
1030
+
1031
+ before do
1032
+ begin
1033
+ client.database.users.remove('alanturing')
1034
+ rescue Mongo::Error::OperationFailure
1035
+ # can be user not found, ignore
1036
+ end
1037
+ end
1038
+
1039
+ it 'does not use the query cache' do
1040
+ client['system.users'].find.to_a
1041
+ client['system.users'].find.to_a
1042
+ expect(events.length).to eq(2)
1043
+ end
1044
+ end
1045
+ end