Skip to content

Commit 63b7ae3

Browse files
committed
Gate load shedding contribution rule
1 parent ea0a335 commit 63b7ae3

File tree

3 files changed

+51
-9
lines changed

3 files changed

+51
-9
lines changed

app/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ class Config:
168168
HIGH_WATER_MARK = int(os.getenv("HIGH_WATER_MARK", "26"))
169169
# Throttle services contributing this % or more of total request volume
170170
THROTTLE_CONTRIBUTION_PCT = int(os.getenv("THROTTLE_CONTRIBUTION_PCT", "20"))
171+
# Only apply contribution-based throttling when enough services are active
172+
THROTTLE_CONTRIBUTION_MIN_SERVICES = int(os.getenv("THROTTLE_CONTRIBUTION_MIN_SERVICES", "5"))
173+
# Only apply contribution-based throttling when total volume is above this threshold
174+
THROTTLE_CONTRIBUTION_MIN_VOLUME = int(os.getenv("THROTTLE_CONTRIBUTION_MIN_VOLUME", "50"))
171175
# Throttle services with volume this many times above median
172176
THROTTLE_VOLUME_MEDIAN_MULTIPLE = int(os.getenv("THROTTLE_VOLUME_MEDIAN_MULTIPLE", "10"))
173177

app/load_shedding.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,19 @@ def should_throttle_service(service_id: str) -> bool:
254254

255255
# Check if contributing a significant % of load
256256
throttle_contribution_pct = current_app.config.get("THROTTLE_CONTRIBUTION_PCT", 20)
257-
if contribution_pct >= throttle_contribution_pct:
258-
current_app.logger.info(
259-
"Service %s contributing %.1f%% of load (%s/%s requests)",
260-
service_id,
261-
contribution_pct,
262-
current_volume,
263-
total_volume,
264-
)
265-
return True
257+
min_services = current_app.config.get("THROTTLE_CONTRIBUTION_MIN_SERVICES", 5)
258+
min_total_volume = current_app.config.get("THROTTLE_CONTRIBUTION_MIN_VOLUME", 50)
259+
260+
if len(service_volumes) >= min_services and total_volume >= min_total_volume:
261+
if contribution_pct >= throttle_contribution_pct:
262+
current_app.logger.info(
263+
"Service %s contributing %.1f%% of load (%s/%s requests)",
264+
service_id,
265+
contribution_pct,
266+
current_volume,
267+
total_volume,
268+
)
269+
return True
266270

267271
# Check if volume is significantly above median (outlier detection)
268272
volumes = sorted(service_volumes.values())

tests/app/test_load_shedding.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ def test_throttles_service_with_volume_above_median_multiple(
204204

205205
def test_single_service_contributing_100_pct(self, notify_api: "Flask", mocker: MockerFixture) -> None:
206206
mocker.patch("app.load_shedding.is_worker_overloaded", return_value=True)
207+
notify_api.config["THROTTLE_CONTRIBUTION_MIN_SERVICES"] = 1
208+
notify_api.config["THROTTLE_CONTRIBUTION_MIN_VOLUME"] = 1
207209

208210
# Test with single service (contributes 100% of load)
209211
# Should be throttled by contribution % (100% > 20% threshold)
@@ -216,6 +218,38 @@ def test_single_service_contributing_100_pct(self, notify_api: "Flask", mocker:
216218
# Single service contributing 100% should be throttled (exceeds 20% threshold)
217219
assert should_throttle_service("single") is True
218220

221+
def test_does_not_throttle_by_contribution_with_few_services(
222+
self, notify_api: "Flask", mocker: MockerFixture
223+
) -> None:
224+
mocker.patch("app.load_shedding.is_worker_overloaded", return_value=True)
225+
notify_api.config["THROTTLE_CONTRIBUTION_MIN_SERVICES"] = 5
226+
notify_api.config["THROTTLE_CONTRIBUTION_MIN_VOLUME"] = 50
227+
228+
# Only 2 services, high contribution but below min service count
229+
service_volumes = {"big": 9, "small": 1}
230+
231+
mock_tracker = mocker.patch("app.load_shedding._volume_tracker")
232+
mock_tracker.get_volumes.return_value = service_volumes
233+
234+
# Should not throttle by contribution; median multiple also not met
235+
assert should_throttle_service("big") is False
236+
237+
def test_does_not_throttle_by_contribution_with_low_total_volume(
238+
self, notify_api: "Flask", mocker: MockerFixture
239+
) -> None:
240+
mocker.patch("app.load_shedding.is_worker_overloaded", return_value=True)
241+
notify_api.config["THROTTLE_CONTRIBUTION_MIN_SERVICES"] = 3
242+
notify_api.config["THROTTLE_CONTRIBUTION_MIN_VOLUME"] = 50
243+
244+
# 3 services but total volume below min threshold
245+
service_volumes = {"big": 8, "mid": 1, "small": 1}
246+
247+
mock_tracker = mocker.patch("app.load_shedding._volume_tracker")
248+
mock_tracker.get_volumes.return_value = service_volumes
249+
250+
# Should not throttle by contribution; median multiple also not met
251+
assert should_throttle_service("big") is False
252+
219253
def test_returns_false_on_error(self, notify_api: "Flask", mocker: MockerFixture) -> None:
220254
mocker.patch("app.load_shedding.is_worker_overloaded", return_value=True)
221255

0 commit comments

Comments
 (0)