Skip to content

Commit 4be13c9

Browse files
committed
minor fixes
1 parent 4ee52fd commit 4be13c9

3 files changed

Lines changed: 78 additions & 34 deletions

File tree

state-manager/app/controller/enqueue_states.py

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,81 @@
77
from ..models.state_status_enum import StateStatusEnum
88

99
from app.singletons.logs_manager import LogsManager
10+
from app.config.settings import get_settings
1011
from pymongo import ReturnDocument
1112

1213
logger = LogsManager().get_logger()
1314

1415

1516
async def find_state(namespace_name: str, nodes: list[str]) -> State | None:
1617
current_time_ms = int(time.time() * 1000)
18+
settings = get_settings()
19+
20+
# Use pipeline to calculate timeout_at based on state-specific or global timeout
21+
pipeline = [
22+
{
23+
"$match": {
24+
"namespace_name": namespace_name,
25+
"status": StateStatusEnum.CREATED,
26+
"node_name": {"$in": nodes},
27+
"enqueue_after": {"$lte": current_time_ms}
28+
}
29+
},
30+
{
31+
"$addFields": {
32+
"status": StateStatusEnum.QUEUED,
33+
"queued_at": current_time_ms,
34+
"timeout_at": {
35+
"$add": [
36+
current_time_ms,
37+
{
38+
"$multiply": [
39+
{
40+
"$ifNull": [
41+
"$timeout_minutes",
42+
settings.node_timeout_minutes
43+
]
44+
},
45+
60000 # Convert minutes to milliseconds
46+
]
47+
}
48+
]
49+
}
50+
}
51+
}
52+
]
53+
1754
data = await State.get_pymongo_collection().find_one_and_update(
1855
{
1956
"namespace_name": namespace_name,
2057
"status": StateStatusEnum.CREATED,
21-
"node_name": {
22-
"$in": nodes
23-
},
58+
"node_name": {"$in": nodes},
2459
"enqueue_after": {"$lte": current_time_ms}
2560
},
26-
{
27-
"$set": {
28-
"status": StateStatusEnum.QUEUED,
29-
"queued_at": current_time_ms
61+
[
62+
{
63+
"$set": {
64+
"status": StateStatusEnum.QUEUED,
65+
"queued_at": current_time_ms,
66+
"timeout_at": {
67+
"$add": [
68+
current_time_ms,
69+
{
70+
"$multiply": [
71+
{
72+
"$ifNull": [
73+
"$timeout_minutes",
74+
settings.node_timeout_minutes
75+
]
76+
},
77+
60000 # Convert minutes to milliseconds
78+
]
79+
}
80+
]
81+
}
82+
}
3083
}
31-
},
84+
],
3285
return_document=ReturnDocument.AFTER
3386
)
3487
return State(**data) if data else None

state-manager/app/models/db/state.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class State(BaseDatabaseModel):
2929
fanout_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Fanout ID of the state")
3030
manual_retry_fanout_id: str = Field(default="", description="Fanout ID from a manual retry request, ensuring unique retries for unite nodes.")
3131
queued_at: Optional[int] = Field(None, description="Unix time in milliseconds when state was queued")
32+
timeout_at: Optional[int] = Field(None, description="Unix time in milliseconds when state times out")
3233
timeout_minutes: Optional[int] = Field(None, gt=0, description="Timeout in minutes for this specific state, taken from node registration")
3334

3435
@before_event([Insert, Replace, Save])
@@ -108,7 +109,7 @@ class Settings:
108109
IndexModel(
109110
[
110111
("status", 1),
111-
("queued_at", 1),
112+
("timeout_at", 1),
112113
],
113114
name="timeout_query_index"
114115
)
Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,33 @@
11
import time
2-
from beanie.operators import Ne
32
from app.models.db.state import State
43
from app.models.state_status_enum import StateStatusEnum
54
from app.singletons.logs_manager import LogsManager
6-
from app.config.settings import get_settings
75

86
logger = LogsManager().get_logger()
97

108

119
async def check_node_timeout():
1210
try:
13-
settings = get_settings()
1411
current_time_ms = int(time.time() * 1000)
1512

1613
logger.info(f"Checking for timed out nodes at {current_time_ms}")
1714

18-
# Find all QUEUED states with queued_at set
19-
queued_states = await State.find(
20-
State.status == StateStatusEnum.QUEUED,
21-
Ne(State.queued_at, None)
22-
).to_list()
15+
# Use database query to find and update timed out states in one operation
16+
result = await State.get_pymongo_collection().update_many(
17+
{
18+
"status": StateStatusEnum.QUEUED,
19+
"timeout_at": {"$ne": None, "$lte": current_time_ms}
20+
},
21+
{
22+
"$set": {
23+
"status": StateStatusEnum.TIMEDOUT,
24+
"error": "Node execution timed out"
25+
}
26+
}
27+
)
2328

24-
states_to_timeout = []
25-
26-
for state in queued_states:
27-
# Use state-specific timeout if available, otherwise fall back to global
28-
timeout_minutes = state.timeout_minutes if state.timeout_minutes else settings.node_timeout_minutes
29-
timeout_ms = timeout_minutes * 60 * 1000
30-
timeout_threshold = current_time_ms - timeout_ms
31-
32-
if state.queued_at <= timeout_threshold:
33-
state.status = StateStatusEnum.TIMEDOUT
34-
state.error = f"Node execution timed out after {timeout_minutes} minutes"
35-
states_to_timeout.append(state)
36-
37-
if states_to_timeout:
38-
# Update all timed out states in bulk
39-
await State.save_all(states_to_timeout)
40-
logger.info(f"Marked {len(states_to_timeout)} states as TIMEDOUT")
29+
if result.modified_count > 0:
30+
logger.info(f"Marked {result.modified_count} states as TIMEDOUT")
4131

4232
except Exception:
4333
logger.error("Error checking node timeout", exc_info=True)

0 commit comments

Comments
 (0)