Skip to content

Commit d46aa16

Browse files
RUBY-3368 Set maxTimeMS if timeoutMS set
1 parent 955d4f5 commit d46aa16

File tree

12 files changed

+154
-15
lines changed

12 files changed

+154
-15
lines changed

lib/mongo/cluster/sdam_flow.rb

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ def server_description_changed
116116
log_warn(
117117
"Server #{updated_desc.address.to_s} has an incorrect replica set name '#{updated_desc.replica_set_name}'; expected '#{topology.replica_set_name}'"
118118
)
119-
@updated_desc = ::Mongo::Server::Description.new(updated_desc.address,
120-
{}, average_round_trip_time: updated_desc.average_round_trip_time)
119+
@updated_desc = ::Mongo::Server::Description.new(
120+
updated_desc.address,
121+
{},
122+
average_round_trip_time: updated_desc.average_round_trip_time,
123+
minimum_round_trip_time: updated_desc.minimum_round_trip_time
124+
)
121125
update_server_descriptions
122126
end
123127
end
@@ -233,8 +237,12 @@ def update_rs_from_primary
233237
end
234238

235239
if stale_primary?
236-
@updated_desc = ::Mongo::Server::Description.new(updated_desc.address,
237-
{}, average_round_trip_time: updated_desc.average_round_trip_time)
240+
@updated_desc = ::Mongo::Server::Description.new(
241+
updated_desc.address,
242+
{},
243+
average_round_trip_time: updated_desc.average_round_trip_time,
244+
minimum_round_trip_time: updated_desc.minimum_round_trip_time
245+
)
238246
update_server_descriptions
239247
check_if_has_primary
240248
return
@@ -270,9 +278,14 @@ def update_rs_from_primary
270278
servers_list.each do |server|
271279
if server.address != updated_desc.address
272280
if server.primary?
273-
server.update_description(::Mongo::Server::Description.new(
274-
server.address, {},
275-
average_round_trip_time: server.description.average_round_trip_time))
281+
server.update_description(
282+
::Mongo::Server::Description.new(
283+
server.address,
284+
{},
285+
average_round_trip_time: server.description.average_round_trip_time,
286+
minimum_round_trip_time: updated_desc.minimum_round_trip_time
287+
)
288+
)
276289
end
277290
end
278291
end

lib/mongo/error.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ def write_concern_error_labels
218218
require 'mongo/error/server_api_not_supported'
219219
require 'mongo/error/server_not_usable'
220220
require 'mongo/error/transactions_not_supported'
221+
require 'mongo/error/timeout_error'
221222
require 'mongo/error/unknown_payload_type'
222223
require 'mongo/error/unmet_dependency'
223224
require 'mongo/error/unsupported_option'

lib/mongo/error/timeout_error.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright (C) 2015-present MongoDB Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
module Mongo
18+
class Error
19+
# Raised when a Client Side Operation Timeout times out.
20+
#
21+
# @since 2.0.0
22+
class TimeoutError < Error
23+
end
24+
end
25+
end

lib/mongo/operation/shared/executable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def build_message(connection, context)
112112
if server_api = context.server_api
113113
msg = msg.maybe_add_server_api(server_api)
114114
end
115+
msg = msg.maybe_add_max_time_ms(connection, context)
115116
msg
116117
end
117118

lib/mongo/protocol/msg.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,22 @@ def maybe_add_server_api(server_api)
301301
Msg.new(@flags, @options, main_document, *@sequences)
302302
end
303303

304+
def maybe_add_max_time_ms(connection, context)
305+
return self if context.remaining_timeout_sec.nil?
306+
307+
max_time_sec = context.remaining_timeout_sec - connection.server.minimum_round_trip_time
308+
if max_time_sec > 0
309+
max_time_ms = (max_time_sec * 1_000).to_i
310+
main_document = @main_document.merge(
311+
maxTimeMS: max_time_ms
312+
)
313+
Msg.new(@flags, @options, main_document, *@sequences)
314+
else
315+
raise Mongo::Error::TimeoutError
316+
end
317+
end
318+
319+
304320
# Returns the number of documents returned from the server.
305321
#
306322
# The Msg instance must be for a server reply and the reply must return

lib/mongo/server.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ def compressor
197197
:max_message_size,
198198
:tags,
199199
:average_round_trip_time,
200+
:minimum_round_trip_time,
200201
:mongos?,
201202
:other?,
202203
:primary?,

lib/mongo/server/connection_base.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ def deliver(message, context, options = {})
169169
raise Error::LintError, "Trying to deliver a message over a disconnected connection (to #{address})"
170170
end
171171
buffer = serialize(message, context)
172+
check_timeout!(context)
172173
ensure_connected do |socket|
173174
operation_id = Monitoring.next_operation_id
174175
started_event = command_started(address, operation_id, message.payload,
@@ -273,6 +274,14 @@ def serialize(message, context, buffer = BSON::ByteBuffer.new)
273274

274275
buffer
275276
end
277+
278+
def check_timeout!(context)
279+
return if context.remaining_timeout_sec.nil?
280+
time_to_execute = context.remaining_timeout_sec - server.minimum_round_trip_time
281+
if time_to_execute <= 0
282+
raise Mongo::Error:TimeoutError
283+
end
284+
end
276285
end
277286
end
278287
end

lib/mongo/server/description.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,23 +209,23 @@ class Description
209209
# @param [ Hash ] config The result of the hello command.
210210
# @param [ Float ] average_round_trip_time The moving average time (sec) the hello
211211
# command took to complete.
212-
# @param [ Float ] average_round_trip_time The moving average time (sec)
213-
# the ismaster call took to complete.
214212
# @param [ true | false ] load_balancer Whether the server is treated as
215213
# a load balancer.
216214
# @param [ true | false ] force_load_balancer Whether the server is
217215
# forced to be a load balancer.
218216
#
219217
# @api private
220218
def initialize(address, config = {}, average_round_trip_time: nil,
221-
load_balancer: false, force_load_balancer: false
219+
minimum_round_trip_time: 0, load_balancer: false,
220+
force_load_balancer: false
222221
)
223222
@address = address
224223
@config = config
225224
@load_balancer = !!load_balancer
226225
@force_load_balancer = !!force_load_balancer
227226
@features = Features.new(wire_versions, me || @address.to_s)
228227
@average_round_trip_time = average_round_trip_time
228+
@minimum_round_trip_time = minimum_round_trip_time
229229
@last_update_time = Time.now.freeze
230230
@last_update_monotime = Utils.monotonic_time
231231

@@ -302,6 +302,10 @@ def features
302302
# @return [ Float ] The moving average time the hello call took to complete.
303303
attr_reader :average_round_trip_time
304304

305+
# @return [ Float ] The minimum time from the ten last hello calls took
306+
# to complete.
307+
attr_reader :minimum_round_trip_time
308+
305309
# Returns whether this server is an arbiter, per the SDAM spec.
306310
#
307311
# @example Is the server an arbiter?

lib/mongo/server/monitor.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,11 @@ def run_sdam_flow(result, awaited: false, scan_error: nil)
237237
@sdam_mutex.synchronize do
238238
old_description = server.description
239239

240-
new_description = Description.new(server.address, result,
241-
average_round_trip_time: server.round_trip_time_calculator.average_round_trip_time
240+
new_description = Description.new(
241+
server.address,
242+
result,
243+
average_round_trip_time: server.round_trip_time_calculator.average_round_trip_time,
244+
minimum_round_trip_time: server.round_trip_time_calculator.minimum_round_trip_time
242245
)
243246

244247
server.cluster.run_sdam_flow(server.description, new_description, awaited: awaited, scan_error: scan_error)

lib/mongo/server/pending_connection.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,11 @@ def handshake!(speculative_auth_doc: nil)
155155
doc['serviceId'] ||= "fake:#{rand(2**32-1)+1}"
156156
end
157157

158-
post_handshake(doc, @server.round_trip_time_calculator.average_round_trip_time)
158+
post_handshake(
159+
doc,
160+
@server.round_trip_time_calculator.average_round_trip_time,
161+
@server.round_trip_time_calculator.minimum_round_trip_time
162+
)
159163

160164
doc
161165
end
@@ -205,7 +209,7 @@ def ensure_connected
205209
#
206210
# @return [ Server::Description ] The server description calculated from
207211
# the handshake response for this particular connection.
208-
def post_handshake(response, average_rtt)
212+
def post_handshake(response, average_rtt, minimum_rtt)
209213
if response["ok"] == 1
210214
# Auth mechanism is entirely dependent on the contents of
211215
# hello response *for this connection*.

0 commit comments

Comments
 (0)