Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- Add 'once' value to schedule_type enum for one-off delayed jobs
ALTER TYPE trax.schedule_type ADD VALUE IF NOT EXISTS 'once';

-- Add scheduled_at column to manifest (used by Once schedule type)
ALTER TABLE trax.manifest
ADD COLUMN IF NOT EXISTS scheduled_at timestamptz;

-- Add scheduled_at column to work_queue (used by delayed triggers)
ALTER TABLE trax.work_queue
ADD COLUMN IF NOT EXISTS scheduled_at timestamptz;

-- Index for efficiently finding work queue entries with future scheduled_at.
-- Note: no partial index on manifest for schedule_type='once' here because
-- new enum values cannot be referenced in the same transaction they are added.
-- The existing ix_manifest_scheduling index covers enabled manifests.
CREATE INDEX IF NOT EXISTS ix_work_queue_scheduled_at
ON trax.work_queue (scheduled_at)
WHERE status = 'queued' AND scheduled_at IS NOT NULL;
10 changes: 10 additions & 0 deletions src/Trax.Effect/Enums/ScheduleType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,14 @@ public enum ScheduleType
/// which dependents fire and with what input.
/// </remarks>
DormantDependent = 5,

/// <summary>
/// The manifest fires exactly once at a scheduled time, then auto-disables.
/// </summary>
/// <remarks>
/// Use this for delayed one-off jobs (e.g., "send reminder in 30 minutes").
/// The <see cref="Manifest.Manifest.ScheduledAt"/> property must be set.
/// After successful execution, the manifest is automatically disabled by the scheduler.
/// </remarks>
Once = 6,
}
6 changes: 6 additions & 0 deletions src/Trax.Effect/Models/Manifest/DTOs/CreateManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,11 @@ public class CreateManifest
/// </summary>
public int? MisfireThresholdSeconds { get; set; }

/// <summary>
/// The earliest time this manifest should be executed.
/// Used with <see cref="ScheduleType.Once"/> for delayed one-off jobs.
/// </summary>
public DateTime? ScheduledAt { get; set; }

#endregion
}
13 changes: 13 additions & 0 deletions src/Trax.Effect/Models/Manifest/Manifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@ public class Manifest : IModel
[Column("misfire_threshold_seconds")]
public int? MisfireThresholdSeconds { get; set; }

/// <summary>
/// Gets or sets the earliest time this manifest should be executed.
/// </summary>
/// <remarks>
/// Used with <see cref="ScheduleType.Once"/> to define when the one-off job should fire.
/// The ManifestManager will queue a work queue entry when <c>ScheduledAt &lt;= now</c>
/// and <see cref="LastSuccessfulRun"/> is null. After successful execution, the manifest
/// is automatically disabled.
/// </remarks>
[Column("scheduled_at")]
public DateTime? ScheduledAt { get; set; }

#endregion

#endregion
Expand Down Expand Up @@ -235,6 +247,7 @@ public static Manifest Create(CreateManifest manifest)
Priority = manifest.Priority,
MisfirePolicy = manifest.MisfirePolicy,
MisfireThresholdSeconds = manifest.MisfireThresholdSeconds,
ScheduledAt = manifest.ScheduledAt,
};

if (manifest.Properties != null)
Expand Down
6 changes: 6 additions & 0 deletions src/Trax.Effect/Models/WorkQueue/DTOs/CreateWorkQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ public class CreateWorkQueue
/// Dispatch priority. Range: 0-31 (clamped in WorkQueue.Create). Defaults to 0.
/// </summary>
public int Priority { get; set; }

/// <summary>
/// The earliest time this work queue entry should be dispatched.
/// Null means dispatch immediately.
/// </summary>
public DateTime? ScheduledAt { get; set; }
}
12 changes: 12 additions & 0 deletions src/Trax.Effect/Models/WorkQueue/WorkQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@
[Column("dispatched_at")]
public DateTime? DispatchedAt { get; set; }

/// <summary>
/// The earliest time this work queue entry should be dispatched.
/// </summary>
/// <remarks>
/// When set, the JobDispatcher will skip this entry until <c>ScheduledAt &lt;= now</c>.
/// Used by <c>TriggerAsync(externalId, delay)</c> to create delayed triggers without
/// affecting the manifest's normal schedule. Null means dispatch immediately.
/// </remarks>
[Column("scheduled_at")]
public DateTime? ScheduledAt { get; set; }

/// <summary>
/// Dispatch priority for this entry. Higher values (up to 31) are dispatched first.
/// </summary>
Expand Down Expand Up @@ -114,6 +125,7 @@
InputTypeName = dto.InputTypeName,
ManifestId = dto.ManifestId,
Priority = Math.Clamp(dto.Priority, MinPriority, MaxPriority),
ScheduledAt = dto.ScheduledAt,
Status = WorkQueueStatus.Queued,
CreatedAt = DateTime.UtcNow,
};
Expand All @@ -129,5 +141,5 @@
#endregion

[JsonConstructor]
public WorkQueue() { }

Check warning on line 144 in src/Trax.Effect/Models/WorkQueue/WorkQueue.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Non-nullable property 'TrainName' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 144 in src/Trax.Effect/Models/WorkQueue/WorkQueue.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Non-nullable property 'ExternalId' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
}
Loading