feat(trigger): reinforcement schedule trigger debugging with cron calculation

- Implemented a caching mechanism for schedule trigger debug events using Redis to optimize performance.
- Added methods to create and manage schedule debug runtime configurations, including cron expression handling.
- Updated the ScheduleTriggerDebugEventPoller to utilize the new caching and event creation logic.
- Removed the deprecated build_schedule_pool_key function from event handling.
This commit is contained in:
Harry
2025-10-28 23:34:03 +08:00
parent 720480d05e
commit b41538d8c7
6 changed files with 207 additions and 57 deletions

View File

@ -1,8 +1,11 @@
"""Trigger debug service supporting plugin and webhook debugging in draft workflows."""
import hashlib
import logging
import time
from abc import ABC, abstractmethod
from collections.abc import Mapping
from datetime import datetime
from typing import Any
from pydantic import BaseModel
@ -14,11 +17,14 @@ from core.trigger.debug.events import (
ScheduleDebugEvent,
WebhookDebugEvent,
build_plugin_pool_key,
build_schedule_pool_key,
build_webhook_pool_key,
)
from core.workflow.enums import NodeType
from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData
from core.workflow.nodes.trigger_schedule.entities import ScheduleConfig
from extensions.ext_redis import redis_client
from libs.datetime_utils import ensure_naive_utc, naive_utc_now
from libs.schedule_utils import calculate_next_run_at
from models.model import App
from models.provider_ids import TriggerProviderID
from models.workflow import Workflow
@ -125,18 +131,66 @@ class WebhookTriggerDebugEventPoller(TriggerDebugEventPoller):
class ScheduleTriggerDebugEventPoller(TriggerDebugEventPoller):
def poll(self) -> TriggerDebugEvent | None:
pool_key: str = build_schedule_pool_key(tenant_id=self.tenant_id, app_id=self.app_id, node_id=self.node_id)
schedule_event: ScheduleDebugEvent | None = TriggerDebugEventBus.poll(
event_type=ScheduleDebugEvent,
pool_key=pool_key,
tenant_id=self.tenant_id,
user_id=self.user_id,
app_id=self.app_id,
"""
Poller for schedule trigger debug events.
This poller will simulate the schedule trigger event by creating a schedule debug runtime cache
and calculating the next run at.
"""
RUNTIME_CACHE_TTL = 60 * 5
class ScheduleDebugRuntime(BaseModel):
cache_key: str
timezone: str
cron_expression: str
next_run_at: datetime
def schedule_debug_runtime_key(self, cron_hash: str) -> str:
return f"schedule_debug_runtime:{self.tenant_id}:{self.user_id}:{self.app_id}:{self.node_id}:{cron_hash}"
def get_or_create_schedule_debug_runtime(self):
from services.trigger.schedule_service import ScheduleService
schedule_config: ScheduleConfig = ScheduleService.to_schedule_config(self.node_config)
cron_hash = hashlib.sha256(schedule_config.cron_expression.encode()).hexdigest()
cache_key = self.schedule_debug_runtime_key(cron_hash)
runtime_cache = redis_client.get(cache_key)
if runtime_cache is None:
schedule_debug_runtime = self.ScheduleDebugRuntime(
cron_expression=schedule_config.cron_expression,
timezone=schedule_config.timezone,
cache_key=cache_key,
next_run_at=ensure_naive_utc(
calculate_next_run_at(schedule_config.cron_expression, schedule_config.timezone)
),
)
redis_client.setex(
name=self.schedule_debug_runtime_key(cron_hash),
time=self.RUNTIME_CACHE_TTL,
value=schedule_debug_runtime.model_dump_json(),
)
return schedule_debug_runtime
else:
redis_client.expire(cache_key, self.RUNTIME_CACHE_TTL)
runtime = self.ScheduleDebugRuntime.model_validate_json(runtime_cache)
runtime.next_run_at = ensure_naive_utc(runtime.next_run_at)
return runtime
def create_schedule_event(self, schedule_debug_runtime: ScheduleDebugRuntime) -> ScheduleDebugEvent:
redis_client.delete(schedule_debug_runtime.cache_key)
return ScheduleDebugEvent(
timestamp=int(time.time()),
node_id=self.node_id,
inputs={},
)
if not schedule_event:
def poll(self) -> TriggerDebugEvent | None:
schedule_debug_runtime = self.get_or_create_schedule_debug_runtime()
if schedule_debug_runtime.next_run_at > naive_utc_now():
return None
schedule_event: ScheduleDebugEvent = self.create_schedule_event(schedule_debug_runtime)
workflow_args: Mapping[str, Any] = {
"inputs": schedule_event.inputs or {},
"files": [],

View File

@ -26,16 +26,6 @@ class ScheduleDebugEvent(BaseDebugEvent):
inputs: Mapping[str, Any]
def build_schedule_pool_key(tenant_id: str, app_id: str, node_id: str) -> str:
"""Generate pool key for schedule events.
Args:
tenant_id: Tenant ID
app_id: App ID
node_id: Node ID
"""
return f"{TriggerDebugPoolKey.SCHEDULE}:{tenant_id}:{app_id}:{node_id}"
class WebhookDebugEvent(BaseDebugEvent):
"""Debug event for webhook triggers."""