-
Notifications
You must be signed in to change notification settings - Fork 42
Adding triggers to graphs #428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
19bc778
Add croniter dependency and implement trigger functionality in graph …
NiveditJain 37b4843
adding triggers
NiveditJain 72ff4b1
Refactor trigger creation logic in verify_graph.py
NiveditJain 54ff19b
Update import paths and add trigger_cron task
NiveditJain 082ff4e
Add DatabaseTriggers model and update imports
NiveditJain 4a8d88d
Update state-manager/app/models/trigger_models.py
NiveditJain 831b8ab
Update state-manager/app/models/trigger_models.py
NiveditJain 582db19
Add namespace field to DatabaseTriggers model and update trigger crea…
NiveditJain c955bc5
Merge branch 'func-trigger' of https://github.com/NiveditJain/exosphe…
NiveditJain eb5acab
Remove unused import from trigger.py
NiveditJain b83fc22
Add APScheduler for cron job management
NiveditJain 7f52fe3
Update test files to reflect model renaming and enhance mock function…
NiveditJain 9fdda88
Implement trigger cron logic for processing due triggers
NiveditJain 45cb95d
Refactor trigger management and settings configuration
NiveditJain 6b2bf85
Refactor verify_graph.py and update test_verify_graph.py
NiveditJain 155f7cc
Add error handling for trigger processing in trigger_cron.py
NiveditJain 5042c7a
Refactor cron trigger handling in verify_graph.py
NiveditJain File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| from pydantic import Field | ||
| from beanie import Document | ||
| from typing import Optional | ||
|
|
||
| from pymongo import IndexModel | ||
| from ..trigger_models import TriggerTypeEnum, TriggerStatusEnum | ||
| from datetime import datetime | ||
|
|
||
| class DatabaseTriggers(Document): | ||
| type: TriggerTypeEnum = Field(..., description="Type of the trigger") | ||
| expression: Optional[str] = Field(default=None, description="Expression of the trigger") | ||
| graph_name: str = Field(..., description="Name of the graph") | ||
| namespace: str = Field(..., description="Namespace of the graph") | ||
| trigger_time: datetime = Field(..., description="Trigger time of the trigger") | ||
| trigger_status: TriggerStatusEnum = Field(..., description="Status of the trigger") | ||
|
|
||
| class Settings: | ||
| indexes = [ | ||
| IndexModel( | ||
| [ | ||
| ("trigger_time", -1), | ||
| ], | ||
| name="idx_trigger_time" | ||
| ), | ||
| IndexModel( | ||
| [ | ||
| ("type", 1), | ||
| ("expression", 1), | ||
| ("graph_name", 1), | ||
| ("namespace", 1), | ||
| ("trigger_time", 1), | ||
| ], | ||
| name="uniq_graph_type_expr_time", | ||
| unique=True | ||
| ) | ||
| ] | ||
NiveditJain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| from pydantic import BaseModel, Field, field_validator, model_validator | ||
| from enum import Enum | ||
| from croniter import croniter | ||
| from typing import Self | ||
|
|
||
| class TriggerTypeEnum(str, Enum): | ||
| CRON = "CRON" | ||
|
|
||
| class TriggerStatusEnum(str, Enum): | ||
| PENDING = "PENDING" | ||
| FAILED = "FAILED" | ||
| CANCELLED = "CANCELLED" | ||
| TRIGGERED = "TRIGGERED" | ||
| TRIGGERING = "TRIGGERING" | ||
|
|
||
| class CronTrigger(BaseModel): | ||
| expression: str = Field(..., description="Cron expression for the trigger") | ||
|
|
||
| @field_validator("expression") | ||
| @classmethod | ||
| def validate_expression(cls, v: str) -> str: | ||
| if not croniter.is_valid(v): | ||
| raise ValueError("Invalid cron expression") | ||
| return v | ||
NiveditJain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| class Trigger(BaseModel): | ||
| type: TriggerTypeEnum = Field(..., description="Type of the trigger") | ||
| value: dict = Field(default_factory=dict, description="Value of the trigger") | ||
|
|
||
| @model_validator(mode="after") | ||
| def validate_trigger(self) -> Self: | ||
| if self.type == TriggerTypeEnum.CRON: | ||
| CronTrigger.model_validate(self.value) | ||
| else: | ||
| raise ValueError(f"Unsupported trigger type: {self.type}") | ||
| return self | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| from datetime import datetime | ||
| from uuid import uuid4 | ||
| from app.models.db.trigger import DatabaseTriggers | ||
| from app.models.trigger_models import TriggerStatusEnum, TriggerTypeEnum | ||
| from app.singletons.logs_manager import LogsManager | ||
| from app.controller.trigger_graph import trigger_graph | ||
| from app.models.trigger_graph_model import TriggerGraphRequestModel | ||
| from pymongo import ReturnDocument | ||
| from app.config.settings import get_settings | ||
| import croniter | ||
| import asyncio | ||
|
|
||
| logger = LogsManager().get_logger() | ||
|
|
||
| async def get_due_triggers(cron_time: datetime) -> DatabaseTriggers | None: | ||
| data = await DatabaseTriggers.get_pymongo_collection().find_one_and_update( | ||
| { | ||
| "trigger_time": {"$lte": cron_time}, | ||
| "trigger_status": TriggerStatusEnum.PENDING | ||
| }, | ||
| { | ||
| "$set": {"trigger_status": TriggerStatusEnum.TRIGGERING} | ||
| }, | ||
| return_document=ReturnDocument.AFTER | ||
| ) | ||
| return DatabaseTriggers(**data) if data else None | ||
|
|
||
| async def call_trigger_graph(trigger: DatabaseTriggers): | ||
| await trigger_graph( | ||
| namespace_name=trigger.namespace, | ||
| graph_name=trigger.graph_name, | ||
| body=TriggerGraphRequestModel(), | ||
| x_exosphere_request_id=str(uuid4()) | ||
| ) | ||
NiveditJain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| async def mark_as_failed(trigger: DatabaseTriggers): | ||
| await DatabaseTriggers.get_pymongo_collection().update_one( | ||
| {"_id": trigger.id}, | ||
| {"$set": {"trigger_status": TriggerStatusEnum.FAILED}} | ||
| ) | ||
|
|
||
| async def create_next_triggers(trigger: DatabaseTriggers, cron_time: datetime): | ||
| assert trigger.expression is not None | ||
| iter = croniter.croniter(trigger.expression, cron_time) | ||
| next_trigger_time = iter.get_next(datetime) | ||
|
|
||
NiveditJain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| await DatabaseTriggers( | ||
| type=TriggerTypeEnum.CRON, | ||
| expression=trigger.expression, | ||
| graph_name=trigger.graph_name, | ||
| namespace=trigger.namespace, | ||
| trigger_time=next_trigger_time, | ||
| trigger_status=TriggerStatusEnum.PENDING | ||
| ).insert() | ||
NiveditJain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| async def mark_as_triggered(trigger: DatabaseTriggers): | ||
| await DatabaseTriggers.get_pymongo_collection().update_one( | ||
| {"_id": trigger.id}, | ||
| {"$set": {"trigger_status": TriggerStatusEnum.TRIGGERED}} | ||
| ) | ||
|
|
||
| async def handle_trigger(cron_time: datetime): | ||
| while(trigger:= await get_due_triggers(cron_time)): | ||
| try: | ||
| await call_trigger_graph(trigger) | ||
| await create_next_triggers(trigger, cron_time) | ||
| await mark_as_triggered(trigger) | ||
| except Exception as e: | ||
| await mark_as_failed(trigger) | ||
| logger.error(f"Error calling trigger graph: {e}") | ||
|
|
||
| async def trigger_cron(): | ||
| cron_time = datetime.now() | ||
| logger.info(f"starting trigger_cron: {cron_time}") | ||
| await asyncio.gather(*[handle_trigger(cron_time) for _ in range(get_settings().trigger_workers)]) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.