fix(api): store retry attempt timestamps as UTC in the persistence layer (#45461)

Signed-off-by: Eapp1e <163811520+Eapp1e@users.noreply.github.com>
This commit is contained in:
Eapp1e authored and GitHub committed 2026-11-04 04:24:10 +00:00
1 parent 0063bb639c
commit 031a3ee8a0
2 files changed
+54 -3

No files matched your search

+3 -3
View File
@@ -47,7 +47,7 @@ from graphon.enums import (
WorkflowType,
)
from graphon.node_events import NodeRunResult
from libs.datetime_utils import naive_utc_now
from libs.datetime_utils import naive_utc_now, to_utc_timestamp
from repositories.entities.workflow_execution import WorkflowExecution
from services.workflow.inspector_events import (
publish_node_changed as _inspector_publish_node_changed,
@@ -422,8 +422,8 @@ class WorkflowPersistenceLayer(Layer):
error=event.node_run_result.error or event.error,
elapsed_time=max((finished_at - event.start_at).total_seconds(), 0.0),
execution_metadata={key.value: value for key, value in event.node_run_result.metadata.items()},
created_at=int(event.start_at.timestamp()),
finished_at=int(finished_at.timestamp()),
created_at=to_utc_timestamp(event.start_at),
finished_at=to_utc_timestamp(finished_at),
)
history.append(attempt.model_dump(mode="json"))
process_data[RETRY_HISTORY_PROCESS_DATA_KEY] = history
@@ -1,5 +1,6 @@
from __future__ import annotations
import time
from dataclasses import replace
from datetime import UTC, datetime, timedelta
from types import SimpleNamespace
@@ -1257,6 +1258,56 @@ class TestPersistenceLayerCases:
assert retry_history[0]["error"] == "attempt 1 failed"
assert retry_history[0]["execution_metadata"] == {"iteration_id": "iteration-1"}
def test_retry_history_records_unix_timestamps(self, monkeypatch: pytest.MonkeyPatch):
"""Retry attempts store epoch seconds, so the host timezone must not shift them."""
if not hasattr(time, "tzset"):
pytest.skip("tzset is only available on POSIX platforms")
recorded_at = datetime(2024, 5, 1)
expected = int(recorded_at.replace(tzinfo=UTC).timestamp())
monkeypatch.setattr(persistence_mod, "naive_utc_now", lambda: recorded_at)
monkeypatch.setenv("TZ", "Asia/Shanghai")
time.tzset()
try:
layer, _, node_repo, _ = _make_layer(node_run_indices={"exec": 1})
layer.on_event(GraphRunStartedEvent())
layer.on_event(
NodeRunStartedEvent(
id="exec",
node_id="node",
node_type=BuiltinNodeTypes.HTTP_REQUEST,
node_title="HTTP",
start_at=recorded_at,
)
)
layer.on_event(
NodeRunRetryEvent(
id="exec",
node_id="node",
node_type=BuiltinNodeTypes.HTTP_REQUEST,
node_title="HTTP",
start_at=recorded_at,
error="attempt 1 failed",
retry_index=1,
node_run_result=NodeRunResult(inputs={}, process_data={}, outputs={}, metadata={}),
)
)
layer.on_event(
NodeRunSucceededEvent(
id="exec",
node_id="node",
node_type=BuiltinNodeTypes.HTTP_REQUEST,
node_title="HTTP",
start_at=recorded_at,
node_run_result=NodeRunResult(inputs={}, process_data={}, outputs={}, metadata={}),
)
)
attempt = node_repo.saved_exec_data[-1].process_data["__dify_retry_history"][0]
finally:
monkeypatch.undo()
time.tzset()
assert attempt["created_at"] == expected
assert attempt["finished_at"] == expected
@pytest.mark.parametrize(
("event_type", "expected_status"),
[