Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,189 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
POLICY_PATH = ROOT / "ops" / "gitea-deploy-broker" / "runner_capacity_policy.py"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def policy_module():
|
||||
spec = importlib.util.spec_from_file_location("appops_runner_capacity_policy_test", POLICY_PATH)
|
||||
assert spec and spec.loader
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def _state(policy_module, **overrides):
|
||||
values = {
|
||||
"queue_depth": 0,
|
||||
"oldest_queued_seconds": None,
|
||||
"load_ratio": 0.30,
|
||||
"available_memory_bytes": 20 * policy_module.GIB,
|
||||
"burst_running": False,
|
||||
"burst_busy": False,
|
||||
"burst_disabled": True,
|
||||
"seconds_since_demand": 600,
|
||||
"seconds_since_action": 600,
|
||||
"swap_critical": False,
|
||||
"cache_free_bytes": 100 * policy_module.GIB,
|
||||
}
|
||||
values.update(overrides)
|
||||
return policy_module.BurstCapacityInput(**values)
|
||||
|
||||
|
||||
def test_burst_starts_only_for_queue_demand_with_headroom(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(policy_module, queue_depth=2)
|
||||
)
|
||||
|
||||
assert decision.action == "start"
|
||||
assert decision.reason == "queue_pressure_with_host_headroom"
|
||||
assert decision.pressure == "headroom"
|
||||
|
||||
|
||||
def test_old_job_can_trigger_burst_even_with_single_queued_job(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(policy_module, queue_depth=1, oldest_queued_seconds=60)
|
||||
)
|
||||
|
||||
assert decision.action == "start"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("load_ratio", "memory_gib", "swap_critical"),
|
||||
[
|
||||
(0.75, 20, False),
|
||||
(0.30, 10, False),
|
||||
(0.30, 20, True),
|
||||
],
|
||||
)
|
||||
def test_burst_does_not_start_without_headroom(
|
||||
policy_module, load_ratio, memory_gib, swap_critical
|
||||
):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
queue_depth=3,
|
||||
load_ratio=load_ratio,
|
||||
available_memory_bytes=memory_gib * policy_module.GIB,
|
||||
swap_critical=swap_critical,
|
||||
)
|
||||
)
|
||||
|
||||
assert decision.action == "hold"
|
||||
assert decision.reason == "queue_pressure_but_host_headroom_insufficient"
|
||||
|
||||
|
||||
def test_high_pressure_drains_busy_burst_instead_of_stopping_it(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
queue_depth=3,
|
||||
load_ratio=0.95,
|
||||
burst_running=True,
|
||||
burst_busy=True,
|
||||
burst_disabled=False,
|
||||
)
|
||||
)
|
||||
|
||||
assert decision.action == "drain"
|
||||
assert decision.reason == "host_pressure_drain_after_current_job"
|
||||
|
||||
|
||||
def test_high_pressure_stops_only_already_drained_idle_burst(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
load_ratio=0.95,
|
||||
burst_running=True,
|
||||
burst_busy=False,
|
||||
burst_disabled=True,
|
||||
seconds_since_action=600,
|
||||
)
|
||||
)
|
||||
|
||||
assert decision.action == "stop"
|
||||
assert decision.reason == "host_pressure_idle_burst_can_stop"
|
||||
|
||||
|
||||
def test_idle_burst_is_drained_before_stop(policy_module):
|
||||
first = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
burst_running=True,
|
||||
burst_disabled=False,
|
||||
seconds_since_demand=600,
|
||||
seconds_since_action=600,
|
||||
)
|
||||
)
|
||||
second = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
burst_running=True,
|
||||
burst_disabled=True,
|
||||
seconds_since_demand=600,
|
||||
seconds_since_action=600,
|
||||
)
|
||||
)
|
||||
|
||||
assert first.action == "drain"
|
||||
assert second.action == "stop"
|
||||
|
||||
|
||||
def test_disabled_running_burst_reenables_when_demand_returns(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
queue_depth=2,
|
||||
burst_running=True,
|
||||
burst_disabled=True,
|
||||
seconds_since_action=600,
|
||||
)
|
||||
)
|
||||
|
||||
assert decision.action == "enable"
|
||||
|
||||
|
||||
def test_start_cooldown_prevents_flapping(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
queue_depth=2,
|
||||
seconds_since_action=30,
|
||||
)
|
||||
)
|
||||
|
||||
assert decision.action == "hold"
|
||||
|
||||
|
||||
def test_burst_does_not_start_when_cache_headroom_is_low(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
queue_depth=3,
|
||||
cache_free_bytes=20 * policy_module.GIB,
|
||||
)
|
||||
)
|
||||
assert decision.action == "hold"
|
||||
assert decision.reason == "queue_pressure_but_host_headroom_insufficient"
|
||||
|
||||
|
||||
def test_low_cache_headroom_drains_busy_burst(policy_module):
|
||||
decision = policy_module.decide_burst_capacity(
|
||||
_state(
|
||||
policy_module,
|
||||
queue_depth=3,
|
||||
burst_running=True,
|
||||
burst_busy=True,
|
||||
burst_disabled=False,
|
||||
cache_free_bytes=10 * policy_module.GIB,
|
||||
)
|
||||
)
|
||||
assert decision.action == "drain"
|
||||
assert decision.pressure == "critical"
|
||||
Reference in new issue
Block a user