-
Notifications
You must be signed in to change notification settings - Fork 530
RUBY-3701 Apply backpressure in with_transaction #2992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # frozen_string_literal: true | ||
| # rubocop:todo all | ||
|
|
||
| require 'spec_helper' | ||
|
|
||
| describe Mongo::Session do | ||
| require_topology :replica_set | ||
|
|
||
| describe 'transactions convenient API prose tests' do | ||
| let(:client) { authorized_client } | ||
| let(:admin_client) { authorized_client.use('admin') } | ||
| let(:collection) { client['session-transaction-prose-test'] } | ||
|
|
||
| before do | ||
| collection.delete_many | ||
| end | ||
|
|
||
| after do | ||
| disable_fail_command | ||
| end | ||
|
|
||
| # Prose test from: | ||
| # specifications/source/transactions-convenient-api/tests/README.md | ||
| # ### Retry Backoff is Enforced | ||
| it 'adds measurable delay when jitter is enabled' do | ||
| skip 'failCommand fail point is not available' unless fail_command_available? | ||
|
|
||
| no_backoff_time = with_fixed_jitter(0) do | ||
| with_commit_failures(13) do | ||
| measure_with_transaction_time do |session| | ||
| collection.insert_one({}, session: session) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| with_backoff_time = with_fixed_jitter(1) do | ||
| with_commit_failures(13) do | ||
| measure_with_transaction_time do |session| | ||
| collection.insert_one({}, session: session) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| # Sum of 13 backoffs per spec is approximately 1.8 seconds. | ||
| expect(with_backoff_time).to be_within(0.5).of(no_backoff_time + 1.8) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def measure_with_transaction_time | ||
| start_time = Mongo::Utils.monotonic_time | ||
| client.start_session do |session| | ||
| session.with_transaction do | ||
| yield(session) | ||
| end | ||
| end | ||
| Mongo::Utils.monotonic_time - start_time | ||
| end | ||
|
|
||
| def with_fixed_jitter(value) | ||
| allow(Random).to receive(:rand).and_return(value) | ||
| yield | ||
| end | ||
|
|
||
| def with_commit_failures(times) | ||
| admin_client.command( | ||
| configureFailPoint: 'failCommand', | ||
| mode: { times: times }, | ||
| data: { | ||
| failCommands: ['commitTransaction'], | ||
| errorCode: 251, | ||
|
comandeo-mongo marked this conversation as resolved.
|
||
| }, | ||
| ) | ||
| yield | ||
| ensure | ||
| disable_fail_command | ||
| end | ||
|
|
||
| def disable_fail_command | ||
| admin_client.command(configureFailPoint: 'failCommand', mode: 'off') | ||
| rescue Mongo::Error | ||
| # Ignore cleanup failures. | ||
| end | ||
|
|
||
| def fail_command_available? | ||
| admin_client.command(configureFailPoint: 'failCommand', mode: 'off') | ||
| true | ||
| rescue Mongo::Error | ||
| false | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -264,5 +264,45 @@ class SessionTransactionSpecError < StandardError; end | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| context 'backoff calculation' do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require_topology :replica_set | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it 'calculates exponential backoff correctly' do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test backoff formula: jitter * min(BACKOFF_INITIAL * 1.5^(attempt-1), BACKOFF_MAX) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backoff_initial = Mongo::Session::BACKOFF_INITIAL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backoff_max = Mongo::Session::BACKOFF_MAX | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test attempt 1: 1.5^0 = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expected_attempt_1 = backoff_initial * (1.5 ** 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(expected_attempt_1).to eq(0.005) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test attempt 2: 1.5^1 = 1.5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expected_attempt_2 = backoff_initial * (1.5 ** 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(expected_attempt_2).to eq(0.0075) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test attempt 3: 1.5^2 = 2.25 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expected_attempt_3 = backoff_initial * (1.5 ** 2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(expected_attempt_3).to eq(0.01125) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test cap at BACKOFF_MAX | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expected_attempt_large = [backoff_initial * (1.5 ** 20), backoff_max].min | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(expected_attempt_large).to eq(backoff_max) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it 'applies jitter to backoff' do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Jitter should be a random value between 0 and 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # When multiplied with backoff, it should reduce the actual sleep time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backoff = 0.100 # 100ms | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jitter_min = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jitter_max = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actual_min = jitter_min * backoff | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actual_max = jitter_max * backoff | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(actual_min).to eq(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(actual_max).to eq(0.100) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test backoff formula: jitter * min(BACKOFF_INITIAL * 1.5^(attempt-1), BACKOFF_MAX) | |
| backoff_initial = Mongo::Session::BACKOFF_INITIAL | |
| backoff_max = Mongo::Session::BACKOFF_MAX | |
| # Test attempt 1: 1.5^0 = 1 | |
| expected_attempt_1 = backoff_initial * (1.5 ** 0) | |
| expect(expected_attempt_1).to eq(0.005) | |
| # Test attempt 2: 1.5^1 = 1.5 | |
| expected_attempt_2 = backoff_initial * (1.5 ** 1) | |
| expect(expected_attempt_2).to eq(0.0075) | |
| # Test attempt 3: 1.5^2 = 2.25 | |
| expected_attempt_3 = backoff_initial * (1.5 ** 2) | |
| expect(expected_attempt_3).to eq(0.01125) | |
| # Test cap at BACKOFF_MAX | |
| expected_attempt_large = [backoff_initial * (1.5 ** 20), backoff_max].min | |
| expect(expected_attempt_large).to eq(backoff_max) | |
| end | |
| it 'applies jitter to backoff' do | |
| # Jitter should be a random value between 0 and 1 | |
| # When multiplied with backoff, it should reduce the actual sleep time | |
| backoff = 0.100 # 100ms | |
| jitter_min = 0 | |
| jitter_max = 1 | |
| actual_min = jitter_min * backoff | |
| actual_max = jitter_max * backoff | |
| expect(actual_min).to eq(0) | |
| expect(actual_max).to eq(0.100) | |
| backoff_initial = Mongo::Session::BACKOFF_INITIAL | |
| backoff_max = Mongo::Session::BACKOFF_MAX | |
| # Stub jitter to 1.0 so that backoff is equal to the base value | |
| allow(Random).to receive(:rand).and_return(1.0) | |
| # Attempt 1: 1.5^0 = 1 | |
| backoff_attempt_1 = session.send(:backoff_seconds_for_retry, 1) | |
| expected_attempt_1 = [backoff_initial * (1.5 ** 0), backoff_max].min | |
| expect(backoff_attempt_1).to be_within(1e-9).of(expected_attempt_1) | |
| # Attempt 2: 1.5^1 = 1.5 | |
| backoff_attempt_2 = session.send(:backoff_seconds_for_retry, 2) | |
| expected_attempt_2 = [backoff_initial * (1.5 ** 1), backoff_max].min | |
| expect(backoff_attempt_2).to be_within(1e-9).of(expected_attempt_2) | |
| # Attempt 3: 1.5^2 = 2.25 | |
| backoff_attempt_3 = session.send(:backoff_seconds_for_retry, 3) | |
| expected_attempt_3 = [backoff_initial * (1.5 ** 2), backoff_max].min | |
| expect(backoff_attempt_3).to be_within(1e-9).of(expected_attempt_3) | |
| # Large attempt should be capped at BACKOFF_MAX | |
| backoff_attempt_large = session.send(:backoff_seconds_for_retry, 20) | |
| expected_attempt_large = [backoff_initial * (1.5 ** 19), backoff_max].min | |
| expect(backoff_attempt_large).to be_within(1e-9).of(expected_attempt_large) | |
| end | |
| it 'applies jitter to backoff' do | |
| backoff_initial = Mongo::Session::BACKOFF_INITIAL | |
| attempt = 2 | |
| base_backoff = [backoff_initial * (1.5 ** (attempt - 1)), Mongo::Session::BACKOFF_MAX].min | |
| # First call: jitter 0.25 | |
| # Second call: jitter 0.75 | |
| allow(Random).to receive(:rand).and_return(0.25, 0.75) | |
| backoff_low_jitter = session.send(:backoff_seconds_for_retry, attempt) | |
| backoff_high_jitter = session.send(:backoff_seconds_for_retry, attempt) | |
| expect(backoff_low_jitter).to be_within(1e-9).of(base_backoff * 0.25) | |
| expect(backoff_high_jitter).to be_within(1e-9).of(base_backoff * 0.75) | |
| expect(backoff_high_jitter).to be > backoff_low_jitter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree with copilot here -- these tests would be far more meaningful if they tested the code that performs the calculations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point; removed the unnecessary tests.
Uh oh!
There was an error while loading. Please reload this page.