Skip to content

Commit b7de2ca

Browse files
authored
Wait for 2 secs before scaling out for SB ScaleMonitor (Azure#50923)
* Wait for 2 secs before scaling out * Change to TimeSpan * Update changlog
1 parent c5b65d6 commit b7de2ca

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
### Bugs Fixed
1010

11+
- Wait for 2 secs before scaling out for ScaleMonitor to avoid aggressive scaling (matches with ScaleControlelr V2) - AB#32302750
12+
1113
### Other Changes
1214

1315
## 5.17.0 (2025-06-20)

sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusScaleMonitor.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ internal class ServiceBusScaleMonitor : IScaleMonitor<ServiceBusTriggerMetrics>
2323
private readonly ILogger<ServiceBusScaleMonitor> _logger;
2424
private readonly ServiceBusMetricsProvider _serviceBusMetricsProvider;
2525

26+
// to avoid frequent scaling out when queue time is increasing, we wait for at least 2s before scaling out (backward compatibility with SCV2).
27+
public static TimeSpan MinimumLastQueueMessageInSecondsThreshold = TimeSpan.FromSeconds(2.0);
28+
2629
public ServiceBusScaleMonitor(
2730
string functionId,
2831
string entityPath,
@@ -137,7 +140,9 @@ private ScaleStatus GetScaleStatusCore(int workerCount, ServiceBusTriggerMetrics
137140
}
138141
}
139142

140-
if (metrics[0].QueueTime > TimeSpan.Zero && metrics[0].QueueTime < metrics[NumberOfSamplesToConsider - 1].QueueTime)
143+
var lastSampleQueueTime = metrics[NumberOfSamplesToConsider - 1].QueueTime;
144+
145+
if (metrics[0].QueueTime > TimeSpan.Zero && metrics[0].QueueTime < lastSampleQueueTime)
141146
{
142147
bool queueTimeIncreasing =
143148
IsTrueForLastN(
@@ -146,9 +151,19 @@ private ScaleStatus GetScaleStatusCore(int workerCount, ServiceBusTriggerMetrics
146151
(prev, next) => prev.QueueTime <= next.QueueTime);
147152
if (queueTimeIncreasing)
148153
{
149-
status.Vote = ScaleVote.ScaleOut;
150-
_logger.LogInformation($"Queue time is increasing for '{_entityPath}'.");
151-
return status;
154+
// to avoid frequent scaling out when queue time is increasing, we wait for at least 2s (backward compatibility with SCV2).
155+
if (lastSampleQueueTime >= MinimumLastQueueMessageInSecondsThreshold)
156+
{
157+
status.Vote = ScaleVote.ScaleOut;
158+
_logger.LogInformation($"Queue time is increasing for '{_entityPath}'.");
159+
return status;
160+
}
161+
else
162+
{
163+
status.Vote = ScaleVote.None;
164+
_logger.LogInformation($"Queue time is increasing for '{_entityPath}' but we do not scale out unless queue latency is greater than {MinimumLastQueueMessageInSecondsThreshold.TotalSeconds}s. Current queue latency is {lastSampleQueueTime.TotalSeconds}s.");
165+
return status;
166+
}
152167
}
153168
}
154169

sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusScaleMonitorTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,33 @@ public void GetScaleStatus_QueueTimeIncreasing_ReturnsVote_ScaleOut()
697697
Assert.AreEqual($"Queue time is increasing for '{_entityPath}'.", log.FormattedMessage);
698698
}
699699

700+
[Test]
701+
public void GetScaleStatus_QueueTimeIncreasingLessThanMinimumWaitTime_ReturnsScaleVoteNone()
702+
{
703+
var context = new ScaleStatusContext<ServiceBusTriggerMetrics>
704+
{
705+
WorkerCount = 1
706+
};
707+
var timestamp = DateTime.UtcNow;
708+
var lastQueueTime = 0.5;
709+
context.Metrics = new List<ServiceBusTriggerMetrics>
710+
{
711+
new ServiceBusTriggerMetrics { MessageCount = 100, PartitionCount = 0, QueueTime = TimeSpan.FromSeconds(0.1), Timestamp = timestamp.AddSeconds(15) },
712+
new ServiceBusTriggerMetrics { MessageCount = 100, PartitionCount = 0, QueueTime = TimeSpan.FromSeconds(0.2), Timestamp = timestamp.AddSeconds(15) },
713+
new ServiceBusTriggerMetrics { MessageCount = 100, PartitionCount = 0, QueueTime = TimeSpan.FromSeconds(0.3), Timestamp = timestamp.AddSeconds(15) },
714+
new ServiceBusTriggerMetrics { MessageCount = 100, PartitionCount = 0, QueueTime = TimeSpan.FromSeconds(0.4), Timestamp = timestamp.AddSeconds(15) },
715+
new ServiceBusTriggerMetrics { MessageCount = 100, PartitionCount = 0, QueueTime = TimeSpan.FromSeconds(lastQueueTime), Timestamp = timestamp.AddSeconds(15) },
716+
};
717+
718+
var status = _scaleMonitor.GetScaleStatus(context);
719+
Assert.AreEqual(ScaleVote.None, status.Vote);
720+
721+
var logs = _loggerProvider.GetAllLogMessages().ToArray();
722+
var log = logs[0];
723+
Assert.AreEqual(LogLevel.Information, log.Level);
724+
Assert.AreEqual($"Queue time is increasing for '{_entityPath}' but we do not scale out unless queue latency is greater than {ServiceBusScaleMonitor.MinimumLastQueueMessageInSecondsThreshold.TotalSeconds}s. Current queue latency is {lastQueueTime}s.", log.FormattedMessage);
725+
}
726+
700727
[Test]
701728
public void GetScaleStatus_QueueLengthDecreasing_ReturnsVote_ScaleIn()
702729
{

0 commit comments

Comments
 (0)