Skip to content

Commit 901d878

Browse files
author
Ryan Hamel
committed
Only remove ‘au’ nodes after boot_timeout
1 parent 0ea3738 commit 901d878

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

‎gridengine/src/gridengine/autoscaler.py‎

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import os
22
import sys
3+
import time
34
import typing
4-
from typing import Any, Dict, List, Optional, Set
5+
from typing import Any, Callable, Dict, List, Optional, Set
56

67
from hpc.autoscale import hpclogging as logging
78
from hpc.autoscale.job import demandcalculator as dcalclib
89
from hpc.autoscale.job import demandprinter
910
from hpc.autoscale.job.demand import DemandResult
1011
from hpc.autoscale.job.demandcalculator import DemandCalculator
12+
from hpc.autoscale.node.node import Node
1113
from hpc.autoscale.node.nodehistory import NodeHistory, SQLiteNodeHistory
1214
from hpc.autoscale.node.nodemanager import new_node_manager
1315
from hpc.autoscale.results import DefaultContextHandler
@@ -30,6 +32,7 @@ def autoscale_grid_engine(
3032
ctx_handler: Optional[DefaultContextHandler] = None,
3133
node_history: Optional[NodeHistory] = None,
3234
dry_run: bool = False,
35+
clock: Callable[[], float] = time.time,
3336
) -> DemandResult:
3437
global _exit_code
3538

@@ -75,8 +78,11 @@ def autoscale_grid_engine(
7578
if not ccnodeid or ccnodeid == cc_node.delayed_node_id.node_id:
7679
if cc_node.state in ["Preparing", "Acquiring"]:
7780
continue
81+
82+
# Sometimes a node will be fully converged but will still be shown as 'au' = alarm, unresponsive
83+
# We will ignore this unless the node has been given its full boot_timeout.
7884
if "a" in state and "u" in state:
79-
invalid_nodes.append(node)
85+
_handle_alarm_node(config, clock, node, invalid_nodes)
8086

8187
# nodes in error state must also be deleted
8288
nodes_to_delete = ge_driver.clean_hosts(invalid_nodes)
@@ -168,6 +174,25 @@ def autoscale_grid_engine(
168174
return demand_result
169175

170176

177+
def _handle_alarm_node(
178+
config: Dict,
179+
clock: Callable[[], float],
180+
node: "Node",
181+
invalid_nodes_out: List["Node"],
182+
) -> None:
183+
now = clock()
184+
omega = config["boot_timeout"] + node.create_time_unix
185+
if now > omega:
186+
invalid_nodes_out.append(node)
187+
else:
188+
logging.warning(
189+
f"Node {node.name} is in an alarm state ('au') but it has not been up longer than its boot_timeout."
190+
)
191+
logging.warning(
192+
f"If {node.name} is still in an alarm state in {omega - now} seconds, it will be removed and shutdown."
193+
)
194+
195+
171196
def new_demand_calculator(
172197
config: Dict,
173198
ge_env: Optional[GridEngineEnvironment] = None,

‎gridengine/test/gridengine_test/autoscaler_test.py‎

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from typing import Dict, List, Optional
2+
from typing import Callable, Dict, List, Optional
33

44
from hpc.autoscale import hpclogging
55
from hpc.autoscale.ccbindings.mock import MockClusterBinding
@@ -314,6 +314,31 @@ def test_fill_up_and_round_robin() -> None:
314314
)
315315

316316

317+
def test_handle_alarm_node() -> None:
318+
class MockNode:
319+
def __init__(self, name: str, create_time_unix: float) -> None:
320+
self.name = name
321+
self.create_time_unix = create_time_unix
322+
323+
def __str__(self) -> str:
324+
return f"Node({self.name})"
325+
326+
def make_clock(t: float) -> Callable[[], float]:
327+
return lambda: t
328+
329+
node = MockNode("htc-1", 100)
330+
config = {"boot_timeout": 1800}
331+
332+
def run_test(now: float, invalid: bool) -> None:
333+
invalid_nodes_out = []
334+
autoscaler._handle_alarm_node(config, make_clock(now), node, invalid_nodes_out)
335+
assert invalid == bool(invalid_nodes_out)
336+
337+
run_test(100, False)
338+
run_test(1899, False)
339+
run_test(1901, True)
340+
341+
317342
def _job(qsub_cmd: str, job_id: int) -> Job:
318343
ge_env = common_ge_env()
319344
qsub = mock_driver.MockQsub(ge_env)
@@ -360,7 +385,7 @@ def common_cluster_test(
360385
qsub_commands: List[str],
361386
pg_counts: Optional[Dict[str, int]] = None,
362387
previous_dcalc: Optional[DemandCalculator] = None,
363-
**array_counts: int
388+
**array_counts: int,
364389
) -> DemandCalculator:
365390
pg_counts = pg_counts or {}
366391
dcalc = common_cluster(qsub_commands, previous_dcalc)

0 commit comments

Comments
 (0)