Vapora/migrations/010_scheduled_workflows.surql
Jesús Pérez bb55c80d2b
feat(workflow-engine): autonomous scheduling with timezone and distributed lock
Add cron-based autonomous workflow firing with two hardening layers:

  - Timezone-aware scheduling via chrono-tz: ScheduledWorkflow.timezone
    (IANA identifier), compute_next_fire_at/after_tz, validate_timezone;
    DST-safe, UTC fallback when absent; validated at config load and REST API

  - Distributed fire-lock via SurrealDB conditional UPDATE (locked_by/locked_at
    fields, 120 s TTL); WorkflowScheduler gains instance_id (UUID) as lock owner;
    prevents double-fires across multi-instance deployments without extra infra

  - ScheduleStore: try_acquire_fire_lock, release_fire_lock (own-instance guard),
    full CRUD (load_one/all, full_upsert, patch, delete, load_runs)

  - REST: 7 endpoints (GET/PUT/PATCH/DELETE schedules, runs history, manual fire)
    with timezone field in all request/response types

  - Migrations 010 (schedule tables) + 011 (timezone + lock columns)
  - Tests: 48 passing (was 26); ADR-0034; changelog; feature docs updated
2026-02-26 11:34:44 +00:00

45 lines
2.5 KiB
Plaintext

-- Migration 010: Scheduled Workflow Definitions and Run History
-- Enables autonomous cron-based workflow firing without REST triggers.
-- Two tables: schedule definitions (managed by TOML + DB) and an append-only run log.
DEFINE TABLE scheduled_workflows SCHEMAFULL;
DEFINE FIELD id ON TABLE scheduled_workflows TYPE record<scheduled_workflows>;
DEFINE FIELD template_name ON TABLE scheduled_workflows TYPE string ASSERT $value != NONE;
DEFINE FIELD cron_expression ON TABLE scheduled_workflows TYPE string ASSERT $value != NONE;
DEFINE FIELD initial_context ON TABLE scheduled_workflows FLEXIBLE TYPE object DEFAULT {};
DEFINE FIELD enabled ON TABLE scheduled_workflows TYPE bool DEFAULT true;
DEFINE FIELD allow_concurrent ON TABLE scheduled_workflows TYPE bool DEFAULT false;
DEFINE FIELD catch_up ON TABLE scheduled_workflows TYPE bool DEFAULT false;
DEFINE FIELD last_fired_at ON TABLE scheduled_workflows TYPE option<datetime> DEFAULT NONE;
DEFINE FIELD next_fire_at ON TABLE scheduled_workflows TYPE option<datetime> DEFAULT NONE;
DEFINE FIELD runs_count ON TABLE scheduled_workflows TYPE int DEFAULT 0;
DEFINE FIELD created_at ON TABLE scheduled_workflows TYPE datetime DEFAULT time::now();
DEFINE FIELD updated_at ON TABLE scheduled_workflows TYPE datetime DEFAULT time::now() VALUE time::now();
DEFINE INDEX idx_scheduled_workflows_template
ON TABLE scheduled_workflows COLUMNS template_name;
DEFINE INDEX idx_scheduled_workflows_enabled
ON TABLE scheduled_workflows COLUMNS enabled;
-- Append-only execution history for audit and debugging.
DEFINE TABLE schedule_runs SCHEMAFULL;
DEFINE FIELD id ON TABLE schedule_runs TYPE record<schedule_runs>;
DEFINE FIELD schedule_id ON TABLE schedule_runs TYPE string ASSERT $value != NONE;
DEFINE FIELD workflow_instance_id ON TABLE schedule_runs TYPE option<string> DEFAULT NONE;
DEFINE FIELD fired_at ON TABLE schedule_runs TYPE datetime ASSERT $value != NONE;
DEFINE FIELD status ON TABLE schedule_runs TYPE string
ASSERT $value INSIDE ['Fired', 'Skipped', 'Failed'];
DEFINE FIELD notes ON TABLE schedule_runs TYPE option<string> DEFAULT NONE;
DEFINE INDEX idx_schedule_runs_schedule_id
ON TABLE schedule_runs COLUMNS schedule_id;
DEFINE INDEX idx_schedule_runs_fired_at
ON TABLE schedule_runs COLUMNS fired_at;
DEFINE INDEX idx_schedule_runs_schedule_fired
ON TABLE schedule_runs COLUMNS schedule_id, fired_at;