mongo 2.5.0.beta → 2.5.0

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 (172) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/address.rb +1 -1
  5. data/lib/mongo/address/unix.rb +1 -1
  6. data/lib/mongo/auth/user.rb +0 -5
  7. data/lib/mongo/auth/user/view.rb +4 -4
  8. data/lib/mongo/bulk_write.rb +60 -32
  9. data/lib/mongo/client.rb +44 -8
  10. data/lib/mongo/cluster.rb +14 -12
  11. data/lib/mongo/cluster/periodic_executor.rb +106 -0
  12. data/lib/mongo/cluster/{cursor_reaper.rb → reapers/cursor_reaper.rb} +5 -37
  13. data/lib/mongo/cluster/reapers/socket_reaper.rb +59 -0
  14. data/lib/mongo/collection.rb +9 -6
  15. data/lib/mongo/collection/view.rb +2 -2
  16. data/lib/mongo/collection/view/builder/aggregation.rb +2 -1
  17. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  18. data/lib/mongo/collection/view/change_stream.rb +14 -1
  19. data/lib/mongo/collection/view/map_reduce.rb +30 -13
  20. data/lib/mongo/collection/view/readable.rb +5 -5
  21. data/lib/mongo/collection/view/writable.rb +98 -51
  22. data/lib/mongo/error.rb +3 -0
  23. data/lib/mongo/error/invalid_txt_record.rb +27 -0
  24. data/lib/mongo/error/invalid_uri.rb +7 -6
  25. data/lib/mongo/error/mismatched_domain.rb +27 -0
  26. data/lib/mongo/error/no_srv_records.rb +26 -0
  27. data/lib/mongo/error/unsupported_features.rb +0 -18
  28. data/lib/mongo/index/view.rb +2 -2
  29. data/lib/mongo/operation.rb +1 -0
  30. data/lib/mongo/operation/causally_consistent.rb +33 -0
  31. data/lib/mongo/operation/commands.rb +2 -1
  32. data/lib/mongo/operation/commands/aggregate.rb +2 -7
  33. data/lib/mongo/operation/commands/count.rb +27 -0
  34. data/lib/mongo/operation/commands/distinct.rb +27 -0
  35. data/lib/mongo/operation/commands/find.rb +3 -1
  36. data/lib/mongo/operation/commands/map_reduce.rb +1 -0
  37. data/lib/mongo/operation/commands/parallel_scan.rb +1 -0
  38. data/lib/mongo/operation/specifiable.rb +12 -0
  39. data/lib/mongo/operation/uses_command_op_msg.rb +36 -5
  40. data/lib/mongo/operation/write.rb +0 -5
  41. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -8
  42. data/lib/mongo/operation/write/bulk/mergable.rb +2 -0
  43. data/lib/mongo/operation/write/command/create_index.rb +19 -0
  44. data/lib/mongo/operation/write/command/create_user.rb +19 -0
  45. data/lib/mongo/operation/write/command/delete.rb +1 -2
  46. data/lib/mongo/operation/write/command/drop_index.rb +19 -0
  47. data/lib/mongo/operation/write/command/insert.rb +1 -2
  48. data/lib/mongo/operation/write/command/remove_user.rb +19 -0
  49. data/lib/mongo/operation/write/command/update.rb +1 -2
  50. data/lib/mongo/operation/write/command/update_user.rb +19 -0
  51. data/lib/mongo/operation/write/write_command_enabled.rb +1 -3
  52. data/lib/mongo/protocol/compressed.rb +2 -1
  53. data/lib/mongo/protocol/serializers.rb +6 -6
  54. data/lib/mongo/retryable.rb +48 -5
  55. data/lib/mongo/server.rb +15 -0
  56. data/lib/mongo/server/connection.rb +21 -1
  57. data/lib/mongo/server/connection_pool.rb +3 -0
  58. data/lib/mongo/server/connection_pool/queue.rb +50 -5
  59. data/lib/mongo/server/description.rb +11 -3
  60. data/lib/mongo/server/description/features.rb +26 -7
  61. data/lib/mongo/session.rb +133 -6
  62. data/lib/mongo/session/server_session.rb +30 -0
  63. data/lib/mongo/session/session_pool.rb +20 -20
  64. data/lib/mongo/uri.rb +88 -44
  65. data/lib/mongo/uri/srv_protocol.rb +158 -0
  66. data/lib/mongo/version.rb +1 -1
  67. data/lib/mongo/write_concern/normalizable.rb +12 -0
  68. data/mongo.gemspec +1 -2
  69. data/spec/mongo/address_spec.rb +12 -0
  70. data/spec/mongo/auth/user/view_spec.rb +1 -5
  71. data/spec/mongo/bulk_write_spec.rb +232 -401
  72. data/spec/mongo/change_stream_examples_spec.rb +150 -0
  73. data/spec/mongo/client_spec.rb +142 -2
  74. data/spec/mongo/cluster/cursor_reaper_spec.rb +0 -70
  75. data/spec/mongo/cluster/socket_reaper_spec.rb +32 -0
  76. data/spec/mongo/cluster_spec.rb +11 -7
  77. data/spec/mongo/collection/view/aggregation_spec.rb +46 -1
  78. data/spec/mongo/collection/view/builder/find_command_spec.rb +15 -0
  79. data/spec/mongo/collection/view/change_stream_spec.rb +79 -12
  80. data/spec/mongo/collection/view/map_reduce_spec.rb +120 -4
  81. data/spec/mongo/collection/view/readable_spec.rb +23 -5
  82. data/spec/mongo/collection_spec.rb +292 -102
  83. data/spec/mongo/command_monitoring_spec.rb +26 -32
  84. data/spec/mongo/crud_spec.rb +1 -1
  85. data/spec/mongo/cursor_spec.rb +2 -3
  86. data/spec/mongo/database_spec.rb +30 -14
  87. data/spec/mongo/dns_seedlist_discovery_spec.rb +94 -0
  88. data/spec/mongo/grid/fs_bucket_spec.rb +1 -1
  89. data/spec/mongo/grid/stream/write_spec.rb +1 -1
  90. data/spec/mongo/index/view_spec.rb +8 -46
  91. data/spec/mongo/operation/write/bulk/delete_spec.rb +2 -2
  92. data/spec/mongo/operation/write/bulk/insert_spec.rb +2 -10
  93. data/spec/mongo/operation/write/{create_index_spec.rb → command/create_index_spec.rb} +2 -6
  94. data/spec/mongo/operation/write/command/delete_spec.rb +35 -7
  95. data/spec/mongo/operation/write/{drop_index_spec.rb → command/drop_index_spec.rb} +1 -1
  96. data/spec/mongo/operation/write/command/insert_spec.rb +37 -6
  97. data/spec/mongo/operation/write/{remove_user_spec.rb → command/remove_user_spec.rb} +2 -6
  98. data/spec/mongo/operation/write/command/update_spec.rb +34 -7
  99. data/spec/mongo/operation/write/{update_user_spec.rb → command/update_user_spec.rb} +1 -1
  100. data/spec/mongo/operation/write/create_user_spec.rb +1 -1
  101. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  102. data/spec/mongo/operation/write/insert_spec.rb +2 -10
  103. data/spec/mongo/operation/write/update_spec.rb +3 -15
  104. data/spec/mongo/retryable_spec.rb +1 -1
  105. data/spec/mongo/retryable_writes_spec.rb +815 -0
  106. data/spec/mongo/server/connection_pool/queue_spec.rb +35 -2
  107. data/spec/mongo/server/connection_pool_spec.rb +234 -1
  108. data/spec/mongo/server/connection_spec.rb +10 -6
  109. data/spec/mongo/server/description/features_spec.rb +51 -37
  110. data/spec/mongo/server/description_spec.rb +6 -3
  111. data/spec/mongo/server_spec.rb +87 -0
  112. data/spec/mongo/session/server_session_spec.rb +43 -0
  113. data/spec/mongo/session/session_pool_spec.rb +63 -27
  114. data/spec/mongo/session_spec.rb +247 -0
  115. data/spec/mongo/shell_examples_spec.rb +2 -2
  116. data/spec/mongo/uri/srv_protocol_spec.rb +933 -0
  117. data/spec/mongo/uri_spec.rb +42 -3
  118. data/spec/mongo/write_concern/acknowledged_spec.rb +11 -0
  119. data/spec/mongo/write_concern/unacknowledged_spec.rb +11 -0
  120. data/spec/spec_helper.rb +11 -25
  121. data/spec/support/authorization.rb +2 -1
  122. data/spec/support/connection_string.rb +8 -4
  123. data/spec/support/crud.rb +38 -24
  124. data/spec/support/crud/write.rb +30 -3
  125. data/spec/support/crud_tests/read/aggregate-out.yml +21 -0
  126. data/spec/support/crud_tests/write/bulkWrite-arrayFilters.yml +44 -0
  127. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +1 -1
  128. data/spec/support/crud_tests/write/insertMany.yml +1 -3
  129. data/spec/support/crud_tests/write/replaceOne.yml +1 -1
  130. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +1 -1
  131. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +1 -1
  132. data/spec/support/dns_seedlist_discovery_tests/longer-parent-in-return.yml +11 -0
  133. data/spec/support/dns_seedlist_discovery_tests/misformatted-option.yml +5 -0
  134. data/spec/support/dns_seedlist_discovery_tests/no-results.yml +5 -0
  135. data/spec/support/dns_seedlist_discovery_tests/not-enough-parts.yml +5 -0
  136. data/spec/support/dns_seedlist_discovery_tests/one-result-default-port.yml +10 -0
  137. data/spec/support/dns_seedlist_discovery_tests/one-txt-record-multiple-strings.yml +10 -0
  138. data/spec/support/dns_seedlist_discovery_tests/one-txt-record.yml +11 -0
  139. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch1.yml +5 -0
  140. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch2.yml +5 -0
  141. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch3.yml +5 -0
  142. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch4.yml +5 -0
  143. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch5.yml +5 -0
  144. data/spec/support/dns_seedlist_discovery_tests/returned-parent-too-short.yml +5 -0
  145. data/spec/support/dns_seedlist_discovery_tests/returned-parent-wrong.yml +5 -0
  146. data/spec/support/dns_seedlist_discovery_tests/two-results-default-port.yml +11 -0
  147. data/spec/support/dns_seedlist_discovery_tests/two-results-nonstandard-port.yml +11 -0
  148. data/spec/support/dns_seedlist_discovery_tests/two-txt-records.yml +5 -0
  149. data/spec/support/dns_seedlist_discovery_tests/txt-record-not-allowed-option.yml +5 -0
  150. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-ssl-option.yml +11 -0
  151. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-uri-option.yml +11 -0
  152. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-unallowed-option.yml +5 -0
  153. data/spec/support/dns_seedlist_discovery_tests/uri-with-port.yml +5 -0
  154. data/spec/support/dns_seedlist_discovery_tests/uri-with-two-hosts.yml +5 -0
  155. data/spec/support/retryable_writes_tests/bulkWrite.yml +305 -0
  156. data/spec/support/retryable_writes_tests/deleteOne.yml +51 -0
  157. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +52 -0
  158. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +57 -0
  159. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +56 -0
  160. data/spec/support/retryable_writes_tests/insertMany.yml +72 -0
  161. data/spec/support/retryable_writes_tests/insertOne.yml +55 -0
  162. data/spec/support/retryable_writes_tests/replaceOne.yml +60 -0
  163. data/spec/support/retryable_writes_tests/updateOne.yml +120 -0
  164. data/spec/support/shared/session.rb +525 -24
  165. metadata +437 -350
  166. metadata.gz.sig +0 -0
  167. data/lib/mongo/operation/commands/user_query.rb +0 -72
  168. data/lib/mongo/operation/write/create_index.rb +0 -67
  169. data/lib/mongo/operation/write/create_user.rb +0 -50
  170. data/lib/mongo/operation/write/drop_index.rb +0 -63
  171. data/lib/mongo/operation/write/remove_user.rb +0 -48
  172. data/lib/mongo/operation/write/update_user.rb +0 -50
@@ -1,6 +1,6 @@
1
1
  shared_examples 'an operation using a session' do
2
2
 
3
- describe 'operation execution', if: sessions_enabled? do
3
+ describe 'operation execution', if: test_sessions? do
4
4
 
5
5
  context 'when the session is created from the same client used for the operation' do
6
6
 
@@ -17,7 +17,7 @@ shared_examples 'an operation using a session' do
17
17
  end
18
18
 
19
19
  let!(:before_operation_time) do
20
- (session.instance_variable_get(:@operation_time) || 0)
20
+ (session.operation_time || 0)
21
21
  end
22
22
 
23
23
  let!(:operation_result) do
@@ -33,7 +33,7 @@ shared_examples 'an operation using a session' do
33
33
  end
34
34
 
35
35
  it 'updates the operation time value' do
36
- expect(session.instance_variable_get(:@operation_time)).not_to eq(before_operation_time)
36
+ expect(session.operation_time).not_to eq(before_operation_time)
37
37
  end
38
38
 
39
39
  it 'does not close the session when the operation completes' do
@@ -44,11 +44,7 @@ shared_examples 'an operation using a session' do
44
44
  context 'when a session from another client is provided' do
45
45
 
46
46
  let(:session) do
47
- client.start_session
48
- end
49
-
50
- let(:client) do
51
- authorized_client.with(read: { mode: :secondary })
47
+ authorized_client.with(read: { mode: :secondary }).start_session
52
48
  end
53
49
 
54
50
  let(:operation_result) do
@@ -87,17 +83,18 @@ end
87
83
 
88
84
  shared_examples 'a failed operation using a session' do
89
85
 
90
- context 'when the operation fails', if: sessions_enabled? do
86
+ context 'when the operation fails', if: test_sessions? do
91
87
 
92
88
  let!(:before_last_use) do
93
89
  session.instance_variable_get(:@server_session).last_use
94
90
  end
95
91
 
96
92
  let!(:before_operation_time) do
97
- (session.instance_variable_get(:@operation_time) || 0)
93
+ (session.operation_time || 0)
98
94
  end
99
95
 
100
96
  let!(:operation_result) do
97
+ sleep 0.2
101
98
  begin; failed_operation; rescue => e; e; end
102
99
  end
103
100
 
@@ -115,7 +112,451 @@ shared_examples 'a failed operation using a session' do
115
112
  end
116
113
 
117
114
  it 'updates the operation time value' do
118
- expect(session.instance_variable_get(:@operation_time)).not_to eq(before_operation_time)
115
+ expect(session.operation_time).not_to eq(before_operation_time)
116
+ end
117
+ end
118
+ end
119
+
120
+ shared_examples 'a causally consistent client session with an unacknowledged write' do
121
+
122
+ context 'when an unacknowledged write is executed in the context of a causally consistent session', if: sessions_enabled? do
123
+
124
+ let(:session) do
125
+ client.start_session(causal_consistency: true)
126
+ end
127
+
128
+ it 'does not update the operation time of the session' do
129
+ operation
130
+ expect(session.operation_time).to be_nil
131
+ end
132
+ end
133
+ end
134
+
135
+ shared_examples 'an operation supporting causally consistent reads' do
136
+
137
+ let(:client) do
138
+ authorized_client.with(heartbeat_frequency: 100).tap do |cl|
139
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
140
+ end
141
+ end
142
+
143
+ let(:subscriber) do
144
+ EventSubscriber.new
145
+ end
146
+
147
+ after do
148
+ client.close
149
+ end
150
+
151
+ context 'when connected to a standalone', if: sessions_enabled? && standalone? do
152
+
153
+ context 'when the collection specifies a read concern' do
154
+
155
+ let(:collection) do
156
+ client[TEST_COLL, read_concern: { level: 'majority' }]
157
+ end
158
+
159
+ context 'when the session has causal_consistency set to true' do
160
+
161
+ let(:session) do
162
+ client.start_session(causal_consistency: true)
163
+ end
164
+
165
+ it 'does not add the afterClusterTime to the read concern in the command' do
166
+ expect(command['readConcern']['afterClusterTime']).to be_nil
167
+ end
168
+ end
169
+
170
+ context 'when the session has causal_consistency set to false' do
171
+
172
+ let(:session) do
173
+ client.start_session(causal_consistency: false)
174
+ end
175
+
176
+ it 'does not add the afterClusterTime to the read concern in the command' do
177
+ expect(command['readConcern']['afterClusterTime']).to be_nil
178
+ end
179
+ end
180
+
181
+ context 'when the session has causal_consistency not set' do
182
+
183
+ let(:session) do
184
+ client.start_session
185
+ end
186
+
187
+ it 'does not add the afterClusterTime to the read concern in the command' do
188
+ expect(command['readConcern']['afterClusterTime']).to be_nil
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'when the collection does not specify a read concern' do
194
+
195
+ let(:collection) do
196
+ client[TEST_COLL]
197
+ end
198
+
199
+ context 'when the session has causal_consistency set to true' do
200
+
201
+ let(:session) do
202
+ client.start_session(causal_consistency: true)
203
+ end
204
+
205
+ it 'does not include the read concern in the command' do
206
+ expect(command['readConcern']).to be_nil
207
+ end
208
+ end
209
+
210
+ context 'when the session has causal_consistency set to false' do
211
+
212
+ let(:session) do
213
+ client.start_session(causal_consistency: false)
214
+ end
215
+
216
+ it 'does not include the read concern in the command' do
217
+ expect(command['readConcern']).to be_nil
218
+ end
219
+ end
220
+
221
+ context 'when the session has causal_consistency not set' do
222
+
223
+ let(:session) do
224
+ client.start_session
225
+ end
226
+
227
+ it 'does not include the read concern in the command' do
228
+ expect(command['readConcern']).to be_nil
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ context 'when connected to replica set or sharded cluster', if: test_sessions? do
235
+
236
+ context 'when the collection specifies a read concern' do
237
+
238
+ let(:collection) do
239
+ client[TEST_COLL, read_concern: { level: 'majority' }]
240
+ end
241
+
242
+ context 'when the session has causal_consistency set to true' do
243
+
244
+ let(:session) do
245
+ client.start_session(causal_consistency: true)
246
+ end
247
+
248
+ context 'when the session has an operation time' do
249
+
250
+ before do
251
+ client.database.command({ ping: 1 }, session: session)
252
+ end
253
+
254
+ let!(:operation_time) do
255
+ session.operation_time
256
+ end
257
+
258
+ let(:expected_read_concern) do
259
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
260
+ end
261
+
262
+ it 'merges the afterClusterTime with the read concern in the command' do
263
+ expect(command['readConcern']).to eq(expected_read_concern)
264
+ end
265
+ end
266
+
267
+ context 'when the session does not have an operation time' do
268
+
269
+ let(:expected_read_concern) do
270
+ BSON::Document.new(level: 'majority')
271
+ end
272
+
273
+ it 'leaves the read concern document unchanged' do
274
+ expect(command['readConcern']).to eq(expected_read_concern)
275
+ end
276
+ end
277
+
278
+ context 'when the operation time is advanced' do
279
+
280
+ before do
281
+ session.advance_operation_time(operation_time)
282
+ end
283
+
284
+ let(:operation_time) do
285
+ BSON::Timestamp.new(0, 1)
286
+ end
287
+
288
+ let(:expected_read_concern) do
289
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
290
+ end
291
+
292
+ it 'merges the afterClusterTime with the new operation time and read concern in the command' do
293
+ expect(command['readConcern']).to eq(expected_read_concern)
294
+ end
295
+ end
296
+ end
297
+
298
+ context 'when the session has causal_consistency set to false' do
299
+
300
+ let(:session) do
301
+ client.start_session(causal_consistency: false)
302
+ end
303
+
304
+ context 'when the session does not have an operation time' do
305
+
306
+ let(:expected_read_concern) do
307
+ BSON::Document.new(level: 'majority')
308
+ end
309
+
310
+ it 'leaves the read concern document unchanged' do
311
+ expect(command['readConcern']).to eq(expected_read_concern)
312
+ end
313
+ end
314
+
315
+ context 'when the session has an operation time' do
316
+
317
+ before do
318
+ client.database.command({ ping: 1 }, session: session)
319
+ end
320
+
321
+ let(:expected_read_concern) do
322
+ BSON::Document.new(level: 'majority')
323
+ end
324
+
325
+ it 'leaves the read concern document unchanged' do
326
+ expect(command['readConcern']).to eq(expected_read_concern)
327
+ end
328
+ end
329
+
330
+ context 'when the operation time is advanced' do
331
+
332
+ before do
333
+ session.advance_operation_time(operation_time)
334
+ end
335
+
336
+ let(:operation_time) do
337
+ BSON::Timestamp.new(0, 1)
338
+ end
339
+
340
+ let(:expected_read_concern) do
341
+ BSON::Document.new(level: 'majority')
342
+ end
343
+
344
+ it 'leaves the read concern document unchanged' do
345
+ expect(command['readConcern']).to eq(expected_read_concern)
346
+ end
347
+ end
348
+ end
349
+
350
+ context 'when the session has causal_consistency not set' do
351
+
352
+ let(:session) do
353
+ client.start_session
354
+ end
355
+
356
+ context 'when the session does not have an operation time' do
357
+
358
+ let(:expected_read_concern) do
359
+ BSON::Document.new(level: 'majority')
360
+ end
361
+
362
+ it 'leaves the read concern document unchanged' do
363
+ expect(command['readConcern']).to eq(expected_read_concern)
364
+ end
365
+ end
366
+
367
+ context 'when the session has an operation time' do
368
+
369
+ before do
370
+ client.database.command({ ping: 1 }, session: session)
371
+ end
372
+
373
+ let!(:operation_time) do
374
+ session.operation_time
375
+ end
376
+
377
+ let(:expected_read_concern) do
378
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
379
+ end
380
+
381
+ it 'merges the afterClusterTime with the new operation time and read concern in the command' do
382
+ expect(command['readConcern']).to eq(expected_read_concern)
383
+ end
384
+ end
385
+
386
+ context 'when the operation time is advanced' do
387
+
388
+ before do
389
+ session.advance_operation_time(operation_time)
390
+ end
391
+
392
+ let(:operation_time) do
393
+ BSON::Timestamp.new(0, 1)
394
+ end
395
+
396
+ let(:expected_read_concern) do
397
+ BSON::Document.new(level: 'majority', afterClusterTime: operation_time)
398
+ end
399
+
400
+ it 'merges the afterClusterTime with the new operation time and read concern in the command' do
401
+ expect(command['readConcern']).to eq(expected_read_concern)
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ context 'when the collection does not specify a read concern' do
408
+
409
+ let(:collection) do
410
+ client[TEST_COLL]
411
+ end
412
+
413
+ context 'when the session has causal_consistency set to true' do
414
+
415
+ let(:session) do
416
+ client.start_session(causal_consistency: true)
417
+ end
418
+
419
+ context 'when the session does not have an operation time' do
420
+
421
+ it 'does not include the read concern in the command' do
422
+ expect(command['readConcern']).to be_nil
423
+ end
424
+ end
425
+
426
+ context 'when the session has an operation time' do
427
+
428
+ before do
429
+ client.database.command({ ping: 1 }, session: session)
430
+ end
431
+
432
+ let!(:operation_time) do
433
+ session.operation_time
434
+ end
435
+
436
+ let(:expected_read_concern) do
437
+ BSON::Document.new(afterClusterTime: operation_time)
438
+ end
439
+
440
+ it 'merges the afterClusterTime with the read concern in the command' do
441
+ expect(command['readConcern']).to eq(expected_read_concern)
442
+ end
443
+ end
444
+
445
+ context 'when the operation time is advanced' do
446
+
447
+ before do
448
+ session.advance_operation_time(operation_time)
449
+ end
450
+
451
+ let(:operation_time) do
452
+ BSON::Timestamp.new(0, 1)
453
+ end
454
+
455
+ let(:expected_read_concern) do
456
+ BSON::Document.new(afterClusterTime: operation_time)
457
+ end
458
+
459
+ it 'merges the afterClusterTime with the new operation time in the command' do
460
+ expect(command['readConcern']).to eq(expected_read_concern)
461
+ end
462
+ end
463
+ end
464
+
465
+ context 'when the session has causal_consistency set to false' do
466
+
467
+ let(:session) do
468
+ client.start_session(causal_consistency: false)
469
+ end
470
+
471
+ context 'when the session does not have an operation time' do
472
+
473
+ it 'does not include the read concern in the command' do
474
+ expect(command['readConcern']).to be_nil
475
+ end
476
+ end
477
+
478
+ context 'when the session has an operation time' do
479
+
480
+ before do
481
+ client.database.command({ ping: 1 }, session: session)
482
+ end
483
+
484
+ it 'does not include the read concern in the command' do
485
+ expect(command['readConcern']).to be_nil
486
+ end
487
+ end
488
+
489
+ context 'when the operation time is advanced' do
490
+
491
+ before do
492
+ session.advance_operation_time(operation_time)
493
+ end
494
+
495
+ let(:operation_time) do
496
+ BSON::Timestamp.new(0, 1)
497
+ end
498
+
499
+ let(:expected_read_concern) do
500
+ BSON::Document.new(afterClusterTime: operation_time)
501
+ end
502
+
503
+ it 'does not include the read concern in the command' do
504
+ expect(command['readConcern']).to be_nil
505
+ end
506
+ end
507
+ end
508
+
509
+ context 'when the session has causal_consistency not set' do
510
+
511
+ let(:session) do
512
+ client.start_session
513
+ end
514
+
515
+ context 'when the session does not have an operation time' do
516
+
517
+ it 'does not include the read concern in the command' do
518
+ expect(command['readConcern']).to be_nil
519
+ end
520
+ end
521
+
522
+ context 'when the session has an operation time' do
523
+
524
+ before do
525
+ client.database.command({ ping: 1 }, session: session)
526
+ end
527
+
528
+ let!(:operation_time) do
529
+ session.operation_time
530
+ end
531
+
532
+ let(:expected_read_concern) do
533
+ BSON::Document.new(afterClusterTime: operation_time)
534
+ end
535
+
536
+ it 'merges the afterClusterTime with the read concern in the command' do
537
+ expect(command['readConcern']).to eq(expected_read_concern)
538
+ end
539
+ end
540
+
541
+ context 'when the operation time is advanced' do
542
+
543
+ before do
544
+ session.advance_operation_time(operation_time)
545
+ end
546
+
547
+ let(:operation_time) do
548
+ BSON::Timestamp.new(0, 1)
549
+ end
550
+
551
+ let(:expected_read_concern) do
552
+ BSON::Document.new(afterClusterTime: operation_time)
553
+ end
554
+
555
+ it 'merges the afterClusterTime with the new operation time in the command' do
556
+ expect(command['readConcern']).to eq(expected_read_concern)
557
+ end
558
+ end
559
+ end
119
560
  end
120
561
  end
121
562
  end
@@ -126,6 +567,10 @@ shared_examples 'an operation updating cluster time' do
126
567
  client.cluster
127
568
  end
128
569
 
570
+ let(:session) do
571
+ client.start_session
572
+ end
573
+
129
574
  let(:client) do
130
575
  authorized_client.with(heartbeat_frequency: 100).tap do |cl|
131
576
  cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
@@ -144,32 +589,40 @@ shared_examples 'an operation updating cluster time' do
144
589
 
145
590
  context 'when the server is version 3.6' do
146
591
 
147
- context 'when the server is a mongos', if: (sharded? && sessions_enabled?) do
592
+ context 'when the cluster is sharded or a replica set', if: test_sessions? do
148
593
 
149
594
  let!(:reply_cluster_time) do
150
- operation
595
+ operation_with_session
151
596
  subscriber.succeeded_events[-1].reply['$clusterTime']
152
597
  end
153
598
 
154
599
  it 'updates the cluster time of the cluster' do
155
600
  expect(cluster.cluster_time).to eq(reply_cluster_time)
156
601
  end
602
+
603
+ it 'updates the cluster time of the session' do
604
+ expect(session.cluster_time).to eq(reply_cluster_time)
605
+ end
157
606
  end
158
607
 
159
- context 'when the server is not a mongos', if: (!sharded? && sessions_enabled?) do
608
+ context 'when the server is a standalone', if: (standalone? && sessions_enabled?) do
160
609
 
161
610
  let(:before_cluster_time) do
162
611
  client.cluster.cluster_time
163
612
  end
164
613
 
165
614
  let!(:reply_cluster_time) do
166
- operation
615
+ operation_with_session
167
616
  subscriber.succeeded_events[-1].reply['$clusterTime']
168
617
  end
169
618
 
170
619
  it 'does not update the cluster time of the cluster' do
171
620
  expect(before_cluster_time).to eq(before_cluster_time)
172
621
  end
622
+
623
+ it 'does not update the cluster time of the session' do
624
+ expect(session.cluster_time).to be_nil
625
+ end
173
626
  end
174
627
  end
175
628
 
@@ -193,28 +646,76 @@ shared_examples 'an operation updating cluster time' do
193
646
  context 'when the command is run twice' do
194
647
 
195
648
  let!(:reply_cluster_time) do
196
- operation
649
+ operation_with_session
197
650
  subscriber.succeeded_events[-1].reply['$clusterTime']
198
651
  end
199
652
 
200
- let(:second_command_cluster_time) do
201
- second_operation
202
- subscriber.started_events[-1].command['$clusterTime']
203
- end
653
+ context 'when the cluster is sharded or a replica set', if: test_sessions? do
654
+
655
+ context 'when the session cluster time is advanced' do
656
+
657
+ before do
658
+ session.advance_cluster_time(advanced_cluster_time)
659
+ end
660
+
661
+ let(:second_command_cluster_time) do
662
+ second_operation
663
+ subscriber.started_events[-1].command['$clusterTime']
664
+ end
204
665
 
205
- context 'when the server is a mongos', if: (sharded? && sessions_enabled?) do
666
+ context 'when the advanced cluster time is greater than the existing cluster time' do
206
667
 
207
- it 'includes the received cluster time in the second command' do
208
- expect(second_command_cluster_time).to eq(reply_cluster_time)
668
+ let(:advanced_cluster_time) do
669
+ new_timestamp = BSON::Timestamp.new(reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].seconds,
670
+ reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].increment + 1)
671
+ new_cluster_time = reply_cluster_time.dup
672
+ new_cluster_time.merge(Mongo::Cluster::CLUSTER_TIME => new_timestamp)
673
+ end
674
+
675
+ it 'includes the advanced cluster time in the second command' do
676
+ expect(second_command_cluster_time).to eq(advanced_cluster_time)
677
+ end
678
+ end
679
+
680
+ context 'when the advanced cluster time is not greater than the existing cluster time' do
681
+
682
+ let(:advanced_cluster_time) do
683
+ new_timestamp = BSON::Timestamp.new(reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].seconds,
684
+ reply_cluster_time[Mongo::Cluster::CLUSTER_TIME].increment - 1)
685
+ new_cluster_time = reply_cluster_time.dup
686
+ new_cluster_time.merge(Mongo::Cluster::CLUSTER_TIME => new_timestamp)
687
+ end
688
+
689
+ it 'does not advance the cluster time' do
690
+ expect(second_command_cluster_time).to eq(reply_cluster_time)
691
+ end
692
+ end
693
+ end
694
+
695
+ context 'when the session cluster time is not advanced' do
696
+
697
+ let(:second_command_cluster_time) do
698
+ second_operation
699
+ subscriber.started_events[-1].command['$clusterTime']
700
+ end
701
+
702
+ it 'includes the received cluster time in the second command' do
703
+ expect(second_command_cluster_time).to eq(reply_cluster_time)
704
+ end
209
705
  end
210
706
  end
211
707
 
212
- context 'when the server is not a mongos', if: (!sharded? && sessions_enabled?) do
708
+ context 'when the server is a standalone', if: (standalone? && sessions_enabled?) do
213
709
 
214
710
  let(:before_cluster_time) do
215
711
  client.cluster.cluster_time
216
712
  end
217
713
 
714
+ let(:second_command_cluster_time) do
715
+ second_operation
716
+ subscriber.started_events[-1].command['$clusterTime']
717
+ end
718
+
218
719
  it 'does not update the cluster time of the cluster' do
219
720
  second_command_cluster_time
220
721
  expect(before_cluster_time).to eq(before_cluster_time)