require_relative '../helper' require 'fluent/plugin_helper/retry_state' require 'fluent/plugin/base' require 'time' class RetryStateHelperTest < Test::Unit::TestCase def override_current_time(state, time) mojule = Module.new do define_method(:current_time){ time } end state.singleton_class.module_eval do prepend mojule end end class Dummy < Fluent::Plugin::TestBase helpers :retry_state end class RetryRecord attr_reader :retry_count, :elapsed_sec, :is_secondary def initialize(retry_count, elapsed_sec, is_secondary) @retry_count = retry_count # This is Nth retryment @elapsed_sec = elapsed_sec @is_secondary = is_secondary end def ==(obj) @retry_count == obj.retry_count && @elapsed_sec == obj.elapsed_sec && @is_secondary == obj.is_secondary end end setup do @d = Dummy.new end test 'randomize can generate value within specified +/- range' do s = @d.retry_state_create(:t1, :exponential_backoff, 0.1, 30) # default enabled w/ 0.125 500.times do r = s.randomize(1000) assert{ r >= 875 && r < 1125 } end s = @d.retry_state_create(:t1, :exponential_backoff, 0.1, 30, randomize_width: 0.25) 500.times do r = s.randomize(1000) assert{ r >= 750 && r < 1250 } end end test 'plugin can create retry_state machine' do s = @d.retry_state_create(:t1, :exponential_backoff, 0.1, 30) # attr_reader :title, :start, :steps, :next_time, :timeout_at, :current, :secondary_transition_at, :secondary_transition_times assert_equal :t1, s.title start_time = s.start assert_equal 0, s.steps assert_equal (start_time + 0.1).to_i, s.next_time.to_i assert_equal (start_time + 0.1).nsec, s.next_time.nsec assert_equal (start_time + 30), s.timeout_at assert_equal :primary, s.current assert{ s.is_a? Fluent::PluginHelper::RetryState::ExponentialBackOffRetry } end test 'periodic retries' do s = @d.retry_state_create(:t2, :periodic, 3, 29, randomize: false) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 29), s.timeout_at assert_equal (dummy_current_time + 3), s.next_time i = 1 while i < 9 override_current_time(s, s.next_time) s.step assert_equal i, s.steps assert_equal (s.current_time + 3), s.next_time assert !s.limit? i += 1 end assert_equal 9, i override_current_time(s, s.next_time) s.step assert_equal s.timeout_at, s.next_time s.step assert s.limit? end test 'periodic retries with max_steps' do s = @d.retry_state_create(:t2, :periodic, 3, 29, randomize: false, max_steps: 5) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 29), s.timeout_at assert_equal (dummy_current_time + 3), s.next_time i = 1 while i < 5 override_current_time(s, s.next_time) s.step assert_equal i, s.steps assert_equal (s.current_time + 3), s.next_time assert !s.limit? i += 1 end assert_equal 5, i override_current_time(s, s.next_time) s.step assert s.limit? end test 'periodic retries with secondary' do s = @d.retry_state_create(:t3, :periodic, 3, 100, randomize: false, secondary: true) # threshold 0.8 dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 100), s.timeout_at assert_equal (dummy_current_time + 100 * 0.8), s.secondary_transition_at assert_equal (dummy_current_time + 3), s.next_time assert !s.secondary? i = 1 while i < 26 override_current_time(s, s.next_time) assert !s.secondary? s.step assert_equal i, s.steps assert_equal (s.current_time + 3), s.next_time assert !s.limit? i += 1 end assert_equal 26, i override_current_time(s, s.next_time) # 78 assert !s.secondary? s.step assert_equal 26, s.steps assert_equal s.secondary_transition_at, s.next_time assert !s.limit? i += 1 assert_equal 27, i override_current_time(s, s.next_time) # 80 assert s.secondary? s.step assert_equal (s.current_time + 3), s.next_time assert_equal s.steps, s.secondary_transition_steps assert !s.limit? i += 1 while i < 33 override_current_time(s, s.next_time) assert s.secondary? s.step assert_equal (s.current_time + 3), s.next_time assert !s.limit? i += 1 end assert_equal 33, i override_current_time(s, s.next_time) # 98 assert s.secondary? s.step assert_equal s.timeout_at, s.next_time # 100 s.step assert s.limit? end test 'periodic retries with secondary and specified threshold' do s = @d.retry_state_create(:t3, :periodic, 3, 100, randomize: false, secondary: true, secondary_threshold: 0.75) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 100), s.timeout_at assert_equal (dummy_current_time + 100 * 0.75), s.secondary_transition_at end test 'periodic retries with secondary and max_steps' do s = @d.retry_state_create(:t3, :periodic, 3, 100, max_steps: 5, randomize: false, secondary: true) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 100), s.timeout_at assert_equal (dummy_current_time + 3 * 5 * 0.8), s.secondary_transition_at end test 'exponential backoff forever without randomization' do s = @d.retry_state_create(:t11, :exponential_backoff, 0.1, 300, randomize: false, forever: true, backoff_base: 2) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal 0, s.steps assert_equal (dummy_current_time + 0.1), s.next_time i = 1 while i < 300 s.step assert_equal i, s.steps assert_equal (dummy_current_time + 0.1 * (2 ** i)), s.next_time assert !s.limit? i += 1 end end test 'exponential backoff with max_interval' do s = @d.retry_state_create(:t12, :exponential_backoff, 0.1, 300, randomize: false, forever: true, backoff_base: 2, max_interval: 100) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal 0, s.steps assert_equal (dummy_current_time + 0.1), s.next_time # 0.1 * 2 ** 9 == 51.2 # 0.1 * 2 ** 10 == 102.4 i = 1 while i < 10 s.step assert_equal i, s.steps assert_equal (dummy_current_time + 0.1 * (2 ** i)), s.next_time, "start:#{dummy_current_time}, i:#{i}" i += 1 end s.step assert_equal 10, s.steps assert_equal (dummy_current_time + 100), s.next_time s.step assert_equal 11, s.steps assert_equal (dummy_current_time + 100), s.next_time end test 'exponential backoff with shorter timeout' do s = @d.retry_state_create(:t13, :exponential_backoff, 1, 12, randomize: false, backoff_base: 2, max_interval: 10) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 12), s.timeout_at assert_equal 0, s.steps assert_equal (dummy_current_time + 1), s.next_time # 1 + 2 + 4 (=7) override_current_time(s, s.next_time) s.step assert_equal 1, s.steps assert_equal (s.current_time + 2), s.next_time override_current_time(s, s.next_time) s.step assert_equal 2, s.steps assert_equal (s.current_time + 4), s.next_time assert !s.limit? # + 8 (=15) > 12 override_current_time(s, s.next_time) s.step assert_equal 3, s.steps assert_equal s.timeout_at, s.next_time s.step assert s.limit? end test 'exponential backoff with max_steps' do s = @d.retry_state_create(:t14, :exponential_backoff, 1, 120, randomize: false, backoff_base: 2, max_interval: 10, max_steps: 6) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 120), s.timeout_at assert_equal 0, s.steps assert_equal (dummy_current_time + 1), s.next_time override_current_time(s, s.next_time) s.step assert_equal 1, s.steps assert_equal (s.current_time + 2), s.next_time override_current_time(s, s.next_time) s.step assert_equal 2, s.steps assert_equal (s.current_time + 4), s.next_time override_current_time(s, s.next_time) s.step assert_equal 3, s.steps assert_equal (s.current_time + 8), s.next_time assert !s.limit? override_current_time(s, s.next_time) s.step assert_equal 4, s.steps assert_equal (s.current_time + 10), s.next_time assert !s.limit? override_current_time(s, s.next_time) s.step assert_equal 5, s.steps assert_equal (s.current_time + 10), s.next_time assert !s.limit? override_current_time(s, s.next_time) s.step assert_equal 6, s.steps assert s.limit? end test 'exponential backoff retries with secondary' do s = @d.retry_state_create(:t15, :exponential_backoff, 1, 100, randomize: false, backoff_base: 2, secondary: true) # threshold 0.8 dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 100), s.timeout_at assert_equal (dummy_current_time + 100 * 0.8), s.secondary_transition_at assert_equal (dummy_current_time + 1), s.next_time assert !s.secondary? # primary: 3, 7, 15, 31, 63, 80 (timeout * threashold) # secondary: 81, 83, 87, 95, 100 i = 1 while i < 6 override_current_time(s, s.next_time) assert !s.secondary? s.step assert_equal i, s.steps assert_equal (s.current_time + 1 * (2 ** i)), s.next_time assert !s.limit? i += 1 end assert_equal 6, i override_current_time(s, s.next_time) # 63 assert !s.secondary? s.step assert_equal 6, s.steps assert_equal s.secondary_transition_at, s.next_time assert !s.limit? i += 1 assert_equal 7, i override_current_time(s, s.next_time) # 80 assert s.secondary? s.step assert_equal 7, s.steps assert_equal s.steps, s.secondary_transition_steps assert_equal (s.secondary_transition_at + 1.0), s.next_time # 81 assert !s.limit? assert_equal :secondary, s.current # 83, 87, 95, 100 j = 1 while j < 4 override_current_time(s, s.next_time) assert s.secondary? assert_equal :secondary, s.current s.step assert_equal (7 + j), s.steps assert_equal (s.current_time + (1 * (2 ** j))), s.next_time assert !s.limit?, "j:#{j}" j += 1 end assert_equal 4, j override_current_time(s, s.next_time) # 95 assert s.secondary? s.step assert_equal s.timeout_at, s.next_time # 100 s.step assert s.limit? end test 'exponential backoff retries with secondary and specified threshold' do s = @d.retry_state_create(:t16, :exponential_backoff, 1, 100, randomize: false, secondary: true, backoff_base: 2, secondary_threshold: 0.75) dummy_current_time = s.start override_current_time(s, dummy_current_time) assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 100), s.timeout_at assert_equal (dummy_current_time + 100 * 0.75), s.secondary_transition_at end test 'exponential backoff retries with secondary and max_steps' do s = @d.retry_state_create(:t15, :exponential_backoff, 1, 100, randomize: false, max_steps: 5, backoff_base: 2, secondary: true) # threshold 0.8 dummy_current_time = s.start override_current_time(s, dummy_current_time) timeout = 0 5.times { |i| timeout += 1.0 * (2 ** i) } assert_equal dummy_current_time, s.current_time assert_equal (dummy_current_time + 100), s.timeout_at assert_equal (dummy_current_time + timeout * 0.8), s.secondary_transition_at end sub_test_case 'exponential backoff' do test 'too big steps(check inf handling)' do s = @d.retry_state_create(:t11, :exponential_backoff, 1, 300, randomize: false, forever: true, backoff_base: 2) dummy_current_time = s.start override_current_time(s, dummy_current_time) i = 1 while i < 1027 if i >= 1025 # With this setting, 1025+ number causes inf in `calc_interval`, so 1024 value is used for next_time assert_nothing_raised(FloatDomainError) { s.step } assert_equal (dummy_current_time + (2 ** (1024 - 1))), s.next_time else s.step end i += 1 end end end sub_test_case "ExponentialBackOff_ScenarioTests" do data("Simple timeout", { timeout: 100, max_steps: nil, max_interval: nil, use_sec: false, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 100, false), ], }) data("Simple timeout with secondary", { timeout: 100, max_steps: nil, max_interval: nil, use_sec: true, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 80, true), RetryRecord.new(8, 81, true), RetryRecord.new(9, 83, true), RetryRecord.new(10, 87, true), RetryRecord.new(11, 95, true), RetryRecord.new(12, 100, true), ], }) data("Simple timeout with custom wait and backoff_base", { timeout: 1000, max_steps: nil, max_interval: nil, use_sec: false, sec_thres: 0.8, wait: 2, backoff_base: 3, expected: [ RetryRecord.new(1, 2, false), RetryRecord.new(2, 8, false), RetryRecord.new(3, 26, false), RetryRecord.new(4, 80, false), RetryRecord.new(5, 242, false), RetryRecord.new(6, 728, false), RetryRecord.new(7, 1000, false), ], }) data("Simple timeout with custom wait and backoff_base and secondary", { timeout: 1000, max_steps: nil, max_interval: nil, use_sec: true, sec_thres: 0.8, wait: 2, backoff_base: 3, expected: [ RetryRecord.new(1, 2, false), RetryRecord.new(2, 8, false), RetryRecord.new(3, 26, false), RetryRecord.new(4, 80, false), RetryRecord.new(5, 242, false), RetryRecord.new(6, 728, false), RetryRecord.new(7, 800, true), RetryRecord.new(8, 802, true), RetryRecord.new(9, 808, true), RetryRecord.new(10, 826, true), RetryRecord.new(11, 880, true), RetryRecord.new(12, 1000, true), ], }) data("Default timeout", { timeout: 72*3600, max_steps: nil, max_interval: nil, use_sec: false, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 8191, false), RetryRecord.new(14, 16383, false), RetryRecord.new(15, 32767, false), RetryRecord.new(16, 65535, false), RetryRecord.new(17, 131071, false), RetryRecord.new(18, 259200, false), ], }) data("Default timeout with secondary", { timeout: 72*3600, max_steps: nil, max_interval: nil, use_sec: true, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 8191, false), RetryRecord.new(14, 16383, false), RetryRecord.new(15, 32767, false), RetryRecord.new(16, 65535, false), RetryRecord.new(17, 131071, false), RetryRecord.new(18, 207360, true), RetryRecord.new(19, 207361, true), RetryRecord.new(20, 207363, true), RetryRecord.new(21, 207367, true), RetryRecord.new(22, 207375, true), RetryRecord.new(23, 207391, true), RetryRecord.new(24, 207423, true), RetryRecord.new(25, 207487, true), RetryRecord.new(26, 207615, true), RetryRecord.new(27, 207871, true), RetryRecord.new(28, 208383, true), RetryRecord.new(29, 209407, true), RetryRecord.new(30, 211455, true), RetryRecord.new(31, 215551, true), RetryRecord.new(32, 223743, true), RetryRecord.new(33, 240127, true), RetryRecord.new(34, 259200, true), ], }) data("Default timeout with secondary and custom threshold", { timeout: 72*3600, max_steps: nil, max_interval: nil, use_sec: true, sec_thres: 0.5, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 8191, false), RetryRecord.new(14, 16383, false), RetryRecord.new(15, 32767, false), RetryRecord.new(16, 65535, false), RetryRecord.new(17, 129600, true), RetryRecord.new(18, 129601, true), RetryRecord.new(19, 129603, true), RetryRecord.new(20, 129607, true), RetryRecord.new(21, 129615, true), RetryRecord.new(22, 129631, true), RetryRecord.new(23, 129663, true), RetryRecord.new(24, 129727, true), RetryRecord.new(25, 129855, true), RetryRecord.new(26, 130111, true), RetryRecord.new(27, 130623, true), RetryRecord.new(28, 131647, true), RetryRecord.new(29, 133695, true), RetryRecord.new(30, 137791, true), RetryRecord.new(31, 145983, true), RetryRecord.new(32, 162367, true), RetryRecord.new(33, 195135, true), RetryRecord.new(34, 259200, true), ], }) data("Simple max_steps", { timeout: 72*3600, max_steps: 10, max_interval: nil, use_sec: false, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), ], }) data("Simple max_steps with secondary", { timeout: 72*3600, max_steps: 10, max_interval: nil, use_sec: true, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 818, true), ], }) data("Simple interval", { timeout: 72*3600, max_steps: nil, max_interval: 3600, use_sec: false, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 7695, false), RetryRecord.new(14, 11295, false), RetryRecord.new(15, 14895, false), RetryRecord.new(16, 18495, false), RetryRecord.new(17, 22095, false), RetryRecord.new(18, 25695, false), RetryRecord.new(19, 29295, false), RetryRecord.new(20, 32895, false), RetryRecord.new(21, 36495, false), RetryRecord.new(22, 40095, false), RetryRecord.new(23, 43695, false), RetryRecord.new(24, 47295, false), RetryRecord.new(25, 50895, false), RetryRecord.new(26, 54495, false), RetryRecord.new(27, 58095, false), RetryRecord.new(28, 61695, false), RetryRecord.new(29, 65295, false), RetryRecord.new(30, 68895, false), RetryRecord.new(31, 72495, false), RetryRecord.new(32, 76095, false), RetryRecord.new(33, 79695, false), RetryRecord.new(34, 83295, false), RetryRecord.new(35, 86895, false), RetryRecord.new(36, 90495, false), RetryRecord.new(37, 94095, false), RetryRecord.new(38, 97695, false), RetryRecord.new(39, 101295, false), RetryRecord.new(40, 104895, false), RetryRecord.new(41, 108495, false), RetryRecord.new(42, 112095, false), RetryRecord.new(43, 115695, false), RetryRecord.new(44, 119295, false), RetryRecord.new(45, 122895, false), RetryRecord.new(46, 126495, false), RetryRecord.new(47, 130095, false), RetryRecord.new(48, 133695, false), RetryRecord.new(49, 137295, false), RetryRecord.new(50, 140895, false), RetryRecord.new(51, 144495, false), RetryRecord.new(52, 148095, false), RetryRecord.new(53, 151695, false), RetryRecord.new(54, 155295, false), RetryRecord.new(55, 158895, false), RetryRecord.new(56, 162495, false), RetryRecord.new(57, 166095, false), RetryRecord.new(58, 169695, false), RetryRecord.new(59, 173295, false), RetryRecord.new(60, 176895, false), RetryRecord.new(61, 180495, false), RetryRecord.new(62, 184095, false), RetryRecord.new(63, 187695, false), RetryRecord.new(64, 191295, false), RetryRecord.new(65, 194895, false), RetryRecord.new(66, 198495, false), RetryRecord.new(67, 202095, false), RetryRecord.new(68, 205695, false), RetryRecord.new(69, 209295, false), RetryRecord.new(70, 212895, false), RetryRecord.new(71, 216495, false), RetryRecord.new(72, 220095, false), RetryRecord.new(73, 223695, false), RetryRecord.new(74, 227295, false), RetryRecord.new(75, 230895, false), RetryRecord.new(76, 234495, false), RetryRecord.new(77, 238095, false), RetryRecord.new(78, 241695, false), RetryRecord.new(79, 245295, false), RetryRecord.new(80, 248895, false), RetryRecord.new(81, 252495, false), RetryRecord.new(82, 256095, false), RetryRecord.new(83, 259200, false), ], }) data("Simple interval with secondary", { timeout: 72*3600, max_steps: nil, max_interval: 3600, use_sec: true, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 7695, false), RetryRecord.new(14, 11295, false), RetryRecord.new(15, 14895, false), RetryRecord.new(16, 18495, false), RetryRecord.new(17, 22095, false), RetryRecord.new(18, 25695, false), RetryRecord.new(19, 29295, false), RetryRecord.new(20, 32895, false), RetryRecord.new(21, 36495, false), RetryRecord.new(22, 40095, false), RetryRecord.new(23, 43695, false), RetryRecord.new(24, 47295, false), RetryRecord.new(25, 50895, false), RetryRecord.new(26, 54495, false), RetryRecord.new(27, 58095, false), RetryRecord.new(28, 61695, false), RetryRecord.new(29, 65295, false), RetryRecord.new(30, 68895, false), RetryRecord.new(31, 72495, false), RetryRecord.new(32, 76095, false), RetryRecord.new(33, 79695, false), RetryRecord.new(34, 83295, false), RetryRecord.new(35, 86895, false), RetryRecord.new(36, 90495, false), RetryRecord.new(37, 94095, false), RetryRecord.new(38, 97695, false), RetryRecord.new(39, 101295, false), RetryRecord.new(40, 104895, false), RetryRecord.new(41, 108495, false), RetryRecord.new(42, 112095, false), RetryRecord.new(43, 115695, false), RetryRecord.new(44, 119295, false), RetryRecord.new(45, 122895, false), RetryRecord.new(46, 126495, false), RetryRecord.new(47, 130095, false), RetryRecord.new(48, 133695, false), RetryRecord.new(49, 137295, false), RetryRecord.new(50, 140895, false), RetryRecord.new(51, 144495, false), RetryRecord.new(52, 148095, false), RetryRecord.new(53, 151695, false), RetryRecord.new(54, 155295, false), RetryRecord.new(55, 158895, false), RetryRecord.new(56, 162495, false), RetryRecord.new(57, 166095, false), RetryRecord.new(58, 169695, false), RetryRecord.new(59, 173295, false), RetryRecord.new(60, 176895, false), RetryRecord.new(61, 180495, false), RetryRecord.new(62, 184095, false), RetryRecord.new(63, 187695, false), RetryRecord.new(64, 191295, false), RetryRecord.new(65, 194895, false), RetryRecord.new(66, 198495, false), RetryRecord.new(67, 202095, false), RetryRecord.new(68, 205695, false), RetryRecord.new(69, 207360, true), RetryRecord.new(70, 207361, true), RetryRecord.new(71, 207363, true), RetryRecord.new(72, 207367, true), RetryRecord.new(73, 207375, true), RetryRecord.new(74, 207391, true), RetryRecord.new(75, 207423, true), RetryRecord.new(76, 207487, true), RetryRecord.new(77, 207615, true), RetryRecord.new(78, 207871, true), RetryRecord.new(79, 208383, true), RetryRecord.new(80, 209407, true), RetryRecord.new(81, 211455, true), RetryRecord.new(82, 215055, true), RetryRecord.new(83, 218655, true), RetryRecord.new(84, 222255, true), RetryRecord.new(85, 225855, true), RetryRecord.new(86, 229455, true), RetryRecord.new(87, 233055, true), RetryRecord.new(88, 236655, true), RetryRecord.new(89, 240255, true), RetryRecord.new(90, 243855, true), RetryRecord.new(91, 247455, true), RetryRecord.new(92, 251055, true), RetryRecord.new(93, 254655, true), RetryRecord.new(94, 258255, true), RetryRecord.new(95, 259200, true), ], }) data("Max_steps and max_interval", { timeout: 72*3600, max_steps: 30, max_interval: 3600, use_sec: false, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 7695, false), RetryRecord.new(14, 11295, false), RetryRecord.new(15, 14895, false), RetryRecord.new(16, 18495, false), RetryRecord.new(17, 22095, false), RetryRecord.new(18, 25695, false), RetryRecord.new(19, 29295, false), RetryRecord.new(20, 32895, false), RetryRecord.new(21, 36495, false), RetryRecord.new(22, 40095, false), RetryRecord.new(23, 43695, false), RetryRecord.new(24, 47295, false), RetryRecord.new(25, 50895, false), RetryRecord.new(26, 54495, false), RetryRecord.new(27, 58095, false), RetryRecord.new(28, 61695, false), RetryRecord.new(29, 65295, false), RetryRecord.new(30, 68895, false), ], }) data("Max_steps and max_interval with secondary", { timeout: 72*3600, max_steps: 30, max_interval: 3600, use_sec: true, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2047, false), RetryRecord.new(12, 4095, false), RetryRecord.new(13, 7695, false), RetryRecord.new(14, 11295, false), RetryRecord.new(15, 14895, false), RetryRecord.new(16, 18495, false), RetryRecord.new(17, 22095, false), RetryRecord.new(18, 25695, false), RetryRecord.new(19, 29295, false), RetryRecord.new(20, 32895, false), RetryRecord.new(21, 36495, false), RetryRecord.new(22, 40095, false), RetryRecord.new(23, 43695, false), RetryRecord.new(24, 47295, false), RetryRecord.new(25, 50895, false), RetryRecord.new(26, 54495, false), RetryRecord.new(27, 55116, true), RetryRecord.new(28, 55117, true), RetryRecord.new(29, 55119, true), RetryRecord.new(30, 55123, true), ], }) data("Max_steps and max_interval with timeout", { timeout: 10000, max_steps: 30, max_interval: 1000, use_sec: false, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2023, false), RetryRecord.new(12, 3023, false), RetryRecord.new(13, 4023, false), RetryRecord.new(14, 5023, false), RetryRecord.new(15, 6023, false), RetryRecord.new(16, 7023, false), RetryRecord.new(17, 8023, false), RetryRecord.new(18, 9023, false), RetryRecord.new(19, 10000, false), ], }) data("Max_steps and max_interval with timeout and secondary", { timeout: 10000, max_steps: 30, max_interval: 1000, use_sec: true, sec_thres: 0.8, wait: 1, backoff_base: 2, expected: [ RetryRecord.new(1, 1, false), RetryRecord.new(2, 3, false), RetryRecord.new(3, 7, false), RetryRecord.new(4, 15, false), RetryRecord.new(5, 31, false), RetryRecord.new(6, 63, false), RetryRecord.new(7, 127, false), RetryRecord.new(8, 255, false), RetryRecord.new(9, 511, false), RetryRecord.new(10, 1023, false), RetryRecord.new(11, 2023, false), RetryRecord.new(12, 3023, false), RetryRecord.new(13, 4023, false), RetryRecord.new(14, 5023, false), RetryRecord.new(15, 6023, false), RetryRecord.new(16, 7023, false), RetryRecord.new(17, 8000, true), RetryRecord.new(18, 8001, true), RetryRecord.new(19, 8003, true), RetryRecord.new(20, 8007, true), RetryRecord.new(21, 8015, true), RetryRecord.new(22, 8031, true), RetryRecord.new(23, 8063, true), RetryRecord.new(24, 8127, true), RetryRecord.new(25, 8255, true), RetryRecord.new(26, 8511, true), RetryRecord.new(27, 9023, true), RetryRecord.new(28, 10000, true), ], }) test "exponential backoff with senario" do |data| print_for_debug = false # change this value true if need to see msg always. trying_count = 1000 # just for avoiding infinite loop retry_records = [] msg = "" s = @d.retry_state_create( :t15, :exponential_backoff, data[:wait], data[:timeout], max_steps: data[:max_steps], max_interval: data[:max_interval], secondary: data[:use_sec], secondary_threshold: data[:sec_thres], backoff_base: data[:backoff_base], randomize: false ) override_current_time(s, s.start) retry_count = 0 trying_count.times do next_elapsed = (s.next_time - s.start).to_i msg << "step: #{s.steps}, next: #{next_elapsed}s (#{next_elapsed / 3600}h)\n" # Wait until next time to trigger the next retry override_current_time(s, s.next_time) # Retry will be triggered at this point. retry_count += 1 rec = RetryRecord.new(retry_count, next_elapsed, s.secondary?) retry_records.append(rec) msg << "[#{next_elapsed}s elapsed point] #{retry_count}th-Retry(#{s.secondary? ? "SEC" : "PRI"}) is triggered.\n" # Update retry statement s.step if s.limit? msg << "--- Reach limit. ---\n" break end end assert_equal(data[:expected], retry_records, msg) print(msg) if print_for_debug end end end