feat(usage): split throttle thresholds per bucket, add draggable gauge markers
This commit is contained in:
@@ -4,13 +4,11 @@ namespace ClaudeDo.Worker.Tests.Usage;
|
||||
|
||||
public sealed class UsageThrottleTests
|
||||
{
|
||||
private const int Soft = 50;
|
||||
private const int Hard = 65;
|
||||
private const int GateFive = 80;
|
||||
private const int GateSeven = 90;
|
||||
private static readonly UsageThresholds FiveHour = new(SoftPct: 50, HardPct: 65, GatePct: 80);
|
||||
private static readonly UsageThresholds SevenDay = new(SoftPct: 50, HardPct: 65, GatePct: 90);
|
||||
|
||||
private static int Effective(double? five, double? seven, int configured = 3) =>
|
||||
UsageThrottle.EffectiveSlots(configured, five, seven, Soft, Hard, GateFive, GateSeven);
|
||||
UsageThrottle.EffectiveSlots(configured, five, FiveHour, seven, SevenDay);
|
||||
|
||||
[Fact]
|
||||
public void BelowSoftThreshold_ReturnsFullConfiguredSlots()
|
||||
@@ -94,13 +92,19 @@ public sealed class UsageThrottleTests
|
||||
[Fact]
|
||||
public void ZeroSoftAndHardThresholds_NeverThrottleBelowGate()
|
||||
{
|
||||
Assert.Equal(3, UsageThrottle.EffectiveSlots(3, 79, 89, softPct: 0, hardPct: 0, gateFiveHourPct: GateFive, gateSevenDayPct: GateSeven));
|
||||
var five = new UsageThresholds(0, 0, 80);
|
||||
var seven = new UsageThresholds(0, 0, 90);
|
||||
|
||||
Assert.Equal(3, UsageThrottle.EffectiveSlots(3, 79, five, 89, seven));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZeroGateThresholds_NeverHardBlock()
|
||||
{
|
||||
Assert.Equal(1, UsageThrottle.EffectiveSlots(3, 99, 99, softPct: Soft, hardPct: Hard, gateFiveHourPct: 0, gateSevenDayPct: 0));
|
||||
var five = new UsageThresholds(50, 65, 0);
|
||||
var seven = new UsageThresholds(50, 65, 0);
|
||||
|
||||
Assert.Equal(1, UsageThrottle.EffectiveSlots(3, 99, five, 99, seven));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -108,4 +112,46 @@ public sealed class UsageThrottleTests
|
||||
{
|
||||
Assert.Equal(1, Effective(10, 10, configured: 0));
|
||||
}
|
||||
|
||||
// ── Per-bucket thresholds are independent ───────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void PerBucket_SameUtilization_DifferentStagesPerBucket()
|
||||
{
|
||||
// 60% is past the 5h soft (50) but still under the 7d soft (70): the 5h bucket decides.
|
||||
var five = new UsageThresholds(50, 65, 80);
|
||||
var seven = new UsageThresholds(70, 85, 90);
|
||||
|
||||
Assert.Equal(2, UsageThrottle.EffectiveSlots(3, 60, five, 60, seven));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerBucket_LessUtilizedBucketCanStillBeTheStricterOne()
|
||||
{
|
||||
// 7d sits lower (40%) but has the tighter thresholds, so it — not the busier 5h — throttles.
|
||||
var five = new UsageThresholds(90, 95, 99);
|
||||
var seven = new UsageThresholds(20, 35, 90);
|
||||
|
||||
Assert.Equal(1, UsageThrottle.EffectiveSlots(3, 80, five, 40, seven));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerBucket_StrictestStageWins()
|
||||
{
|
||||
// 5h is only in its soft stage (2 slots), 7d is past its hard stage (1 slot) → 1 wins.
|
||||
var five = new UsageThresholds(50, 65, 80);
|
||||
var seven = new UsageThresholds(30, 40, 90);
|
||||
|
||||
Assert.Equal(1, UsageThrottle.EffectiveSlots(3, 55, five, 45, seven));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerBucket_MissingBucketNeverThrottles()
|
||||
{
|
||||
// No 7d reading at all: only the 5h bucket may step parallelism down.
|
||||
var five = new UsageThresholds(50, 65, 80);
|
||||
var seven = new UsageThresholds(1, 2, 3);
|
||||
|
||||
Assert.Equal(2, UsageThrottle.EffectiveSlots(3, 55, five, null, seven));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user