Skip to content

Commit 92e98bf

Browse files
committed
fixing backoff overflow
1 parent 8fc8f9f commit 92e98bf

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

src/WebJobs.Script/Utility.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,16 @@ public static string GetSettingFromConfigOrEnvironment(string settingName)
6868
/// <returns>A <see cref="Task"/> representing the computed backoff interval.</returns>
6969
public static async Task DelayWithBackoffAsync(int exponent, CancellationToken cancellationToken, TimeSpan? unit = null, TimeSpan? min = null, TimeSpan? max = null)
7070
{
71-
TimeSpan delay = ComputeBackoff(exponent, unit, min, max);
71+
TimeSpan delay = TimeSpan.FromSeconds(5);
72+
73+
try
74+
{
75+
delay = ComputeBackoff(exponent, unit, min, max);
76+
}
77+
catch (Exception)
78+
{
79+
// Use the default if there is any problem computing backoff
80+
}
7281

7382
if (delay.TotalMilliseconds > 0)
7483
{
@@ -85,12 +94,27 @@ public static async Task DelayWithBackoffAsync(int exponent, CancellationToken c
8594

8695
internal static TimeSpan ComputeBackoff(int exponent, TimeSpan? unit = null, TimeSpan? min = null, TimeSpan? max = null)
8796
{
97+
TimeSpan maxValue = max ?? TimeSpan.MaxValue;
98+
99+
// prevent an OverflowException
100+
if (exponent >= 64)
101+
{
102+
return maxValue;
103+
}
104+
88105
// determine the exponential backoff factor
89106
long backoffFactor = Convert.ToInt64((Math.Pow(2, exponent) - 1) / 2);
90107

91108
// compute the backoff delay
92109
unit = unit ?? TimeSpan.FromSeconds(1);
93110
long totalDelayTicks = backoffFactor * unit.Value.Ticks;
111+
112+
// If we've overflowed long, return max.
113+
if (backoffFactor > 0 && totalDelayTicks <= 0)
114+
{
115+
return maxValue;
116+
}
117+
94118
TimeSpan delay = TimeSpan.FromTicks(totalDelayTicks);
95119

96120
// apply minimum restriction
@@ -100,9 +124,9 @@ internal static TimeSpan ComputeBackoff(int exponent, TimeSpan? unit = null, Tim
100124
}
101125

102126
// apply maximum restriction
103-
if (max.HasValue && delay > max)
127+
if (delay > maxValue)
104128
{
105-
delay = max.Value;
129+
delay = maxValue;
106130
}
107131

108132
return delay;

test/WebJobs.Script.Tests/UtilityTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,29 @@ public void ComputeBackoff_ReturnsExpectedValue(int exponent, string unitValue,
131131
Assert.Equal(expectedTimespan, result);
132132
}
133133

134+
[Theory]
135+
[InlineData("00:02:00", "00:02:00")]
136+
[InlineData(null, "10675199.02:48:05.4775807")]
137+
public void ComputeBackoff_Overflow(string maxValue, string expected)
138+
{
139+
TimeSpan? max = null;
140+
if (maxValue != null)
141+
{
142+
max = TimeSpan.Parse(maxValue);
143+
}
144+
145+
TimeSpan expectedValue = TimeSpan.Parse(expected);
146+
147+
// Catches two overflow bugs:
148+
// 1. Computed ticks would fluctuate between positive and negative, resulting in min-and-max alternating.
149+
// 2. At 64+ we'd throw an OverflowException.
150+
for (int i = 60; i < 70; i++)
151+
{
152+
TimeSpan result = Utility.ComputeBackoff(i, min: TimeSpan.FromSeconds(1), max: max);
153+
Assert.Equal(expectedValue, result);
154+
}
155+
}
156+
134157
[Theory]
135158
[InlineData(typeof(TestPoco), true)]
136159
[InlineData(typeof(TestStruct), true)]

0 commit comments

Comments
 (0)