Skip to content

Commit e915e84

Browse files
RUBY-3372 Add CSOT for CRUD commands (#2849)
1 parent 8ddd727 commit e915e84

File tree

14 files changed

+477
-57
lines changed

14 files changed

+477
-57
lines changed

lib/mongo/client.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,7 @@ def encrypted_fields_map
11881188
@encrypted_fields_map ||= @options.fetch(:auto_encryption_options, {})[:encrypted_fields_map]
11891189
end
11901190

1191+
# @return [ Integer | nil ] Value of timeout_ms option if set.
11911192
# @api private
11921193
def timeout_ms
11931194
@options[:timeout_ms]

lib/mongo/collection.rb

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,10 @@ def ==(other)
134134
# and *:nearest*.
135135
# - *:tag_sets* -- an array of hashes.
136136
# - *:local_threshold*.
137-
# @option opts [ Session ] :session The session to use for the operation.
138-
# @option opts [ Integer ] :size The size of the capped collection.
137+
# @option options [ Session ] :session The session to use for the operation.
138+
# @option options [ Integer ] :size The size of the capped collection.
139+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
140+
# Must a positive integer. The default value is unset which means infinite.
139141
# @option opts [ Hash ] :time_series Create a time-series collection.
140142
# The hash may have the following items:
141143
# - *:timeField* -- The name of the field which contains the date in each
@@ -502,6 +504,8 @@ def drop(opts = {})
502504
# @option options [ Integer ] :skip The number of docs to skip before returning results.
503505
# @option options [ Hash ] :sort The key and direction pairs by which the result set
504506
# will be sorted.
507+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
508+
# Must a positive integer. The default value is unset which means infinite.
505509
# @option options [ Hash ] :let Mapping of variables to use in the command.
506510
# See the server documentation for details.
507511
#
@@ -539,6 +543,8 @@ def find(filter = nil, options = {})
539543
# as of server version 3.6, aggregations always provide results using a
540544
# cursor and this option is therefore not valid.
541545
# @option options [ Session ] :session The session to use.
546+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
547+
# Must a positive integer. The default value is unset which means infinite.
542548
#
543549
# @return [ View::Aggregation ] The aggregation object.
544550
#
@@ -637,6 +643,8 @@ def watch(pipeline = [], options = {})
637643
# @option options [ Session ] :session The session to use.
638644
# @option options [ Object ] :comment A user-provided
639645
# comment to attach to this command.
646+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
647+
# Must a positive integer. The default value is unset which means infinite.
640648
#
641649
# @return [ Integer ] The document count.
642650
#
@@ -673,6 +681,8 @@ def count(filter = nil, options = {})
673681
# @option options [ Session ] :session The session to use.
674682
# @option options [ Object ] :comment A user-provided
675683
# comment to attach to this command.
684+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
685+
# Must a positive integer. The default value is unset which means infinite.
676686
#
677687
# @return [ Integer ] The document count.
678688
#
@@ -694,6 +704,8 @@ def count_documents(filter = {}, options = {})
694704
# @option options [ Hash ] :read The read preference options.
695705
# @option options [ Object ] :comment A user-provided
696706
# comment to attach to this command.
707+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
708+
# Must a positive integer. The default value is unset which means infinite.
697709
#
698710
# @return [ Integer ] The document count.
699711
#
@@ -715,6 +727,8 @@ def estimated_document_count(options = {})
715727
# @option options [ Hash ] :read The read preference options.
716728
# @option options [ Hash ] :collation The collation to use.
717729
# @option options [ Session ] :session The session to use.
730+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
731+
# Must a positive integer. The default value is unset which means infinite.
718732
#
719733
# @return [ Array<Object> ] The list of distinct values.
720734
#
@@ -787,6 +801,8 @@ def inspect
787801
# @option opts [ Object ] :comment A user-provided comment to attach to
788802
# this command.
789803
# @option opts [ Session ] :session The session to use for the operation.
804+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
805+
# Must a positive integer. The default value is unset which means infinite.
790806
# @option opts [ Hash ] :write_concern The write concern options.
791807
# Can be :w => Integer, :fsync => Boolean, :j => Boolean.
792808
#
@@ -844,6 +860,8 @@ def insert_one(document, opts = {})
844860
# @option options [ true | false ] :ordered Whether the operations
845861
# should be executed in order.
846862
# @option options [ Session ] :session The session to use for the operation.
863+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
864+
# Must a positive integer. The default value is unset which means infinite.
847865
# @option options [ Hash ] :write_concern The write concern options.
848866
# Can be :w => Integer, :fsync => Boolean, :j => Boolean.
849867
#
@@ -872,6 +890,8 @@ def insert_many(documents, options = {})
872890
# @option options [ true | false ] :bypass_document_validation Whether or
873891
# not to skip document level validation.
874892
# @option options [ Session ] :session The session to use for the set of operations.
893+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
894+
# Must a positive integer. The default value is unset which means infinite.
875895
# @option options [ Hash ] :let Mapping of variables to use in the command.
876896
# See the server documentation for details.
877897
#
@@ -894,6 +914,8 @@ def bulk_write(requests, options = {})
894914
# @option options [ Session ] :session The session to use.
895915
# @option options [ Hash | String ] :hint The index to use for this operation.
896916
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
917+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
918+
# Must a positive integer. The default value is unset which means infinite.
897919
# @option options [ Hash ] :let Mapping of variables to use in the command.
898920
# See the server documentation for details.
899921
#
@@ -916,6 +938,8 @@ def delete_one(filter = nil, options = {})
916938
# @option options [ Session ] :session The session to use.
917939
# @option options [ Hash | String ] :hint The index to use for this operation.
918940
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
941+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
942+
# Must a positive integer. The default value is unset which means infinite.
919943
# @option options [ Hash ] :let Mapping of variables to use in the command.
920944
# See the server documentation for details.
921945
#
@@ -964,6 +988,8 @@ def parallel_scan(cursor_count, options = {})
964988
# not to skip document level validation.
965989
# @option options [ Hash ] :collation The collation to use.
966990
# @option options [ Session ] :session The session to use.
991+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
992+
# Must a positive integer. The default value is unset which means infinite.
967993
# @option options [ Hash | String ] :hint The index to use for this operation.
968994
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
969995
# @option options [ Hash ] :let Mapping of variables to use in the command.
@@ -993,6 +1019,8 @@ def replace_one(filter, replacement, options = {})
9931019
# @option options [ Array ] :array_filters A set of filters specifying to which array elements
9941020
# an update should apply.
9951021
# @option options [ Session ] :session The session to use.
1022+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
1023+
# Must a positive integer. The default value is unset which means infinite.
9961024
# @option options [ Hash | String ] :hint The index to use for this operation.
9971025
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
9981026
# @option options [ Hash ] :let Mapping of variables to use in the command.
@@ -1022,6 +1050,8 @@ def update_many(filter, update, options = {})
10221050
# @option options [ Array ] :array_filters A set of filters specifying to which array elements
10231051
# an update should apply.
10241052
# @option options [ Session ] :session The session to use.
1053+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
1054+
# Must a positive integer. The default value is unset which means infinite.
10251055
# @option options [ Hash | String ] :hint The index to use for this operation.
10261056
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
10271057
# @option options [ Hash ] :let Mapping of variables to use in the command.
@@ -1052,6 +1082,8 @@ def update_one(filter, update, options = {})
10521082
# Defaults to the collection's write concern.
10531083
# @option options [ Hash ] :collation The collation to use.
10541084
# @option options [ Session ] :session The session to use.
1085+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
1086+
# Must a positive integer. The default value is unset which means infinite.
10551087
# @option options [ Hash | String ] :hint The index to use for this operation.
10561088
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
10571089
# @option options [ Hash ] :let Mapping of variables to use in the command.
@@ -1096,6 +1128,8 @@ def find_one_and_delete(filter, options = {})
10961128
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
10971129
# @option options [ Hash ] :let Mapping of variables to use in the command.
10981130
# See the server documentation for details.
1131+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
1132+
# Must a positive integer. The default value is unset which means infinite.
10991133
#
11001134
# @return [ BSON::Document ] The document.
11011135
#
@@ -1132,6 +1166,8 @@ def find_one_and_update(filter, update, options = {})
11321166
# @option options [ Session ] :session The session to use.
11331167
# @option options [ Hash | String ] :hint The index to use for this operation.
11341168
# May be specified as a Hash (e.g. { _id: 1 }) or a String (e.g. "_id_").
1169+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
1170+
# Must a positive integer. The default value is unset which means infinite.
11351171
# @option options [ Hash ] :let Mapping of variables to use in the command.
11361172
# See the server documentation for details.
11371173
#
@@ -1164,10 +1200,10 @@ def system_collection?
11641200
end
11651201

11661202
def timeout_ms(opts = {})
1167-
if opts.key?(:timeout_ms)
1168-
opts.delete(:timeout_ms)
1203+
if opts[:timeout_ms].nil?
1204+
options[:timeout_ms] || database.timeout_ms
11691205
else
1170-
options.fetch(:timeout_ms) { database.timeout_ms }
1206+
opts.delete(:timeout_ms)
11711207
end
11721208
end
11731209
end

lib/mongo/collection/view.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ def hash
152152
# document more than once. Deprecated as of MongoDB server version 4.0.
153153
# @option options [ Hash ] :sort The key and direction pairs used to sort
154154
# the results.
155+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
156+
# Must a positive integer. The default value is unset which means infinite.
155157
#
156158
# @since 2.0.0
157159
def initialize(collection, filter = {}, options = {})
@@ -214,6 +216,14 @@ def view; self; end
214216
def with_session(opts = {}, &block)
215217
client.send(:with_session, @options.merge(opts), &block)
216218
end
219+
220+
def timeout_ms(opts = {})
221+
if opts[:timeout_ms].nil?
222+
options[:timeout_ms] || database.timeout_ms
223+
else
224+
opts.delete(:timeout_ms)
225+
end
226+
end
217227
end
218228
end
219229
end

lib/mongo/collection/view/aggregation.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def allow_disk_use(value = nil)
9292
# as of server version 3.6, aggregations always provide results using a
9393
# cursor and this option is therefore not valid.
9494
# @option options [ Session ] :session The session to use.
95+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
96+
# Must a positive integer. The default value is unset which means infinite.
9597
#
9698
# @since 2.0.0
9799
def initialize(view, pipeline, options = {})
@@ -181,13 +183,18 @@ def effective_read_preference(connection)
181183
end
182184

183185
def send_initial_query(server, session)
186+
context = Operation::Context.new(
187+
client: client,
188+
session: session,
189+
timeout_ms: timeout_ms
190+
)
184191
server.with_connection do |connection|
185192
initial_query_op(
186193
session,
187194
effective_read_preference(connection)
188195
).execute_with_connection(
189196
connection,
190-
context: Operation::Context.new(client: client, session: session)
197+
context: context
191198
)
192199
end
193200
end
@@ -206,6 +213,14 @@ def cache_options
206213
multi_collection: true,
207214
}
208215
end
216+
217+
def timeout_ms(opts = {})
218+
if opts[:timeout_ms].nil?
219+
options[:timeout_ms] || database.timeout_ms
220+
else
221+
opts.delete(:timeout_ms)
222+
end
223+
end
209224
end
210225
end
211226
end

lib/mongo/collection/view/iterable.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,12 @@ def initial_query_op(session)
197197
end
198198

199199
def send_initial_query(server, session = nil)
200-
initial_query_op(session).execute(server, context: Operation::Context.new(client: client, session: session))
200+
context = Operation::Context.new(
201+
client: client,
202+
session: session,
203+
timeout_ms: timeout_ms
204+
)
205+
initial_query_op(session).execute(server, context: context)
201206
end
202207

203208
def use_query_cache?

lib/mongo/collection/view/readable.rb

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ module Readable
5353
# as of server version 3.6, aggregations always provide results using a
5454
# cursor and this option is therefore not valid.
5555
# @option options [ Session ] :session The session to use.
56+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
57+
# Must a positive integer. The default value is unset which means infinite.
5658
#
5759
# @return [ Aggregation ] The aggregation object.
5860
#
@@ -157,6 +159,8 @@ def comment(comment = nil)
157159
# @option opts [ Mongo::Session ] :session The session to use for the operation.
158160
# @option opts [ Object ] :comment A user-provided
159161
# comment to attach to this command.
162+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
163+
# Must a positive integer. The default value is unset which means infinite.
160164
#
161165
# @return [ Integer ] The document count.
162166
#
@@ -193,7 +197,14 @@ def count(opts = {})
193197
# string key. Note that this isn't documented as valid usage.
194198
collation: opts[:collation] || opts['collation'] || collation,
195199
comment: opts[:comment],
196-
).execute(server, context: Operation::Context.new(client: client, session: session))
200+
).execute(
201+
server,
202+
context: Operation::Context.new(
203+
client: client,
204+
session: session,
205+
timeout_ms: timeout_ms(opts)
206+
)
207+
)
197208
end.n.to_i
198209
end
199210
end
@@ -216,6 +227,8 @@ def count(opts = {})
216227
# @option opts [ Mongo::Session ] :session The session to use for the operation.
217228
# @option ops [ Object ] :comment A user-provided
218229
# comment to attach to this command.
230+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
231+
# Must a positive integer. The default value is unset which means infinite.
219232
#
220233
# @return [ Integer ] The document count.
221234
#
@@ -247,6 +260,8 @@ def count_documents(opts = {})
247260
# @option opts [ Hash ] :read The read preference options.
248261
# @option opts [ Object ] :comment A user-provided
249262
# comment to attach to this command.
263+
# @option options [ Integer ] :timeout_ms The per-operation timeout in milliseconds.
264+
# Must a positive integer. The default value is unset which means infinite.
250265
#
251266
# @return [ Integer ] The document count.
252267
#
@@ -268,7 +283,11 @@ def estimated_document_count(opts = {})
268283
selector = ServerSelector.get(read_pref || server_selector)
269284
with_session(opts) do |session|
270285
read_with_retry(session, selector) do |server|
271-
context = Operation::Context.new(client: client, session: session)
286+
context = Operation::Context.new(
287+
client: client,
288+
session: session,
289+
timeout_ms: timeout_ms(opts)
290+
)
272291
cmd = { count: collection.name }
273292
cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
274293
if read_concern
@@ -342,7 +361,14 @@ def distinct(field_name, opts = {})
342361
# For some reason collation was historically accepted as a
343362
# string key. Note that this isn't documented as valid usage.
344363
collation: opts[:collation] || opts['collation'] || collation,
345-
).execute(server, context: Operation::Context.new(client: client, session: session))
364+
).execute(
365+
server,
366+
context: Operation::Context.new(
367+
client: client,
368+
session: session,
369+
timeout_ms: timeout_ms(opts)
370+
)
371+
)
346372
end.first['values']
347373
end
348374
end
@@ -627,6 +653,15 @@ def cursor_type(type = nil)
627653
configure(:cursor_type, type)
628654
end
629655

656+
# The per-operation timeout in milliseconds. Must a positive integer.
657+
#
658+
# @param [ Integer ] timeout_ms Timeout value.
659+
#
660+
# @return [ Integer, View ] Either the timeout_ms value or a new +View+.
661+
def timeout_ms(timeout_ms = nil)
662+
configure(:timeout_ms, timeout_ms)
663+
end
664+
630665
# @api private
631666
def read_concern
632667
if options[:session] && options[:session].in_transaction?

0 commit comments

Comments
 (0)