add next_fire_at to automation_triggers and croniter dep

This commit is contained in:
CREDO23 2026-05-27 17:55:58 +02:00
parent 861b91004d
commit f08b316441
4 changed files with 33 additions and 0 deletions

View file

@ -89,6 +89,7 @@ def upgrade() -> None:
params JSONB NOT NULL, params JSONB NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT true, enabled BOOLEAN NOT NULL DEFAULT true,
last_fired_at TIMESTAMP WITH TIME ZONE, last_fired_at TIMESTAMP WITH TIME ZONE,
next_fire_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
); );
""" """
@ -105,6 +106,17 @@ def upgrade() -> None:
op.execute( op.execute(
"CREATE INDEX ix_automation_triggers_created_at ON automation_triggers(created_at);" "CREATE INDEX ix_automation_triggers_created_at ON automation_triggers(created_at);"
) )
# Partial index for the schedule tick: only enabled schedule triggers
# with a scheduled next fire are ever scanned for due rows.
op.execute(
"""
CREATE INDEX ix_automation_triggers_due
ON automation_triggers (next_fire_at)
WHERE enabled = true
AND type = 'schedule'
AND next_fire_at IS NOT NULL;
"""
)
# automation_runs — the immutable per-fire execution record # automation_runs — the immutable per-fire execution record
op.execute( op.execute(
@ -148,6 +160,7 @@ def downgrade() -> None:
op.execute("DROP INDEX IF EXISTS ix_automation_runs_automation_id;") op.execute("DROP INDEX IF EXISTS ix_automation_runs_automation_id;")
op.execute("DROP TABLE IF EXISTS automation_runs;") op.execute("DROP TABLE IF EXISTS automation_runs;")
op.execute("DROP INDEX IF EXISTS ix_automation_triggers_due;")
op.execute("DROP INDEX IF EXISTS ix_automation_triggers_created_at;") op.execute("DROP INDEX IF EXISTS ix_automation_triggers_created_at;")
op.execute("DROP INDEX IF EXISTS ix_automation_triggers_enabled;") op.execute("DROP INDEX IF EXISTS ix_automation_triggers_enabled;")
op.execute("DROP INDEX IF EXISTS ix_automation_triggers_type;") op.execute("DROP INDEX IF EXISTS ix_automation_triggers_type;")

View file

@ -46,6 +46,11 @@ class AutomationTrigger(BaseModel, TimestampMixin):
last_fired_at = Column(TIMESTAMP(timezone=True), nullable=True) last_fired_at = Column(TIMESTAMP(timezone=True), nullable=True)
# Precomputed next fire moment in UTC; advanced after each fire by the
# schedule tick. NULL means the trigger has never been scheduled (the
# tick self-heals on first sight). Manual triggers leave this NULL.
next_fire_at = Column(TIMESTAMP(timezone=True), nullable=True)
automation = relationship("Automation", back_populates="triggers") automation = relationship("Automation", back_populates="triggers")
runs = relationship( runs = relationship(
"AutomationRun", "AutomationRun",

View file

@ -87,6 +87,7 @@ dependencies = [
"opentelemetry-instrumentation-httpx>=0.61b0", "opentelemetry-instrumentation-httpx>=0.61b0",
"opentelemetry-instrumentation-celery>=0.61b0", "opentelemetry-instrumentation-celery>=0.61b0",
"opentelemetry-instrumentation-logging>=0.61b0", "opentelemetry-instrumentation-logging>=0.61b0",
"croniter>=2.0.0",
] ]
[dependency-groups] [dependency-groups]

View file

@ -1265,6 +1265,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8e/ca/6a667ccbe649856dcd3458bab80b016681b274399d6211187c6ab969fc50/courlan-1.3.2-py3-none-any.whl", hash = "sha256:d0dab52cf5b5b1000ee2839fbc2837e93b2514d3cb5bb61ae158a55b7a04c6be", size = 33848, upload-time = "2024-10-29T16:40:18.325Z" }, { url = "https://files.pythonhosted.org/packages/8e/ca/6a667ccbe649856dcd3458bab80b016681b274399d6211187c6ab969fc50/courlan-1.3.2-py3-none-any.whl", hash = "sha256:d0dab52cf5b5b1000ee2839fbc2837e93b2514d3cb5bb61ae158a55b7a04c6be", size = 33848, upload-time = "2024-10-29T16:40:18.325Z" },
] ]
[[package]]
name = "croniter"
version = "6.2.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/de/5832661ed55107b8a09af3f0a2e71e0957226a59eb1dcf0a445cce6daf20/croniter-6.2.2.tar.gz", hash = "sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab", size = 113762, upload-time = "2026-03-15T08:43:48.112Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/39/783980e78cb92c2d7bdb1fc7dbc86e94ccc6d58224d76a7f1f51b6c51e30/croniter-6.2.2-py3-none-any.whl", hash = "sha256:a5d17b1060974d36251ea4faf388233eca8acf0d09cbd92d35f4c4ac8f279960", size = 45422, upload-time = "2026-03-15T08:43:46.626Z" },
]
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "46.0.6" version = "46.0.6"
@ -8132,6 +8144,7 @@ dependencies = [
{ name = "celery", extra = ["redis"] }, { name = "celery", extra = ["redis"] },
{ name = "chonkie", extra = ["all"] }, { name = "chonkie", extra = ["all"] },
{ name = "composio" }, { name = "composio" },
{ name = "croniter" },
{ name = "datasets" }, { name = "datasets" },
{ name = "daytona" }, { name = "daytona" },
{ name = "deepagents" }, { name = "deepagents" },
@ -8228,6 +8241,7 @@ requires-dist = [
{ name = "celery", extras = ["redis"], specifier = ">=5.5.3" }, { name = "celery", extras = ["redis"], specifier = ">=5.5.3" },
{ name = "chonkie", extras = ["all"], specifier = ">=1.5.0" }, { name = "chonkie", extras = ["all"], specifier = ">=1.5.0" },
{ name = "composio", specifier = ">=0.10.9" }, { name = "composio", specifier = ">=0.10.9" },
{ name = "croniter", specifier = ">=2.0.0" },
{ name = "datasets", specifier = ">=2.21.0" }, { name = "datasets", specifier = ">=2.21.0" },
{ name = "daytona", specifier = ">=0.146.0" }, { name = "daytona", specifier = ">=0.146.0" },
{ name = "deepagents", specifier = ">=0.4.12,<0.5" }, { name = "deepagents", specifier = ">=0.4.12,<0.5" },