Summary
Add the ability to manually release (skip) a timer in a workflow for operational purposes, plus a query method to list pending timers.
Motivation
In production scenarios, waiting for a timer to naturally expire is not always desirable:
- Incident response: A retry delay timer is pending, but an operator has manually fixed the issue
- Testing/debugging: Skip timers to speed up workflow execution
- Business decisions: Proceed early based on external factors
- Stuck workflow recovery: A timer was set based on stale data
Proposed API
Query Pending Timers
trait WorkflowInstance[F[_], State] {
// ... existing methods ...
def getPendingTimers: F[List[PendingTimer]]
}
case class PendingTimer(
index: Int, // ephemeral index for identification
name: Option[String],
resumeAt: Instant,
)
Release Timer
trait WorkflowInstance[F[_], State] {
def releaseTimer(target: TimerTarget): F[Either[NoMatchingTimer, Unit]]
}
sealed trait TimerTarget
object TimerTarget {
case class ByName(name: String) extends TimerTarget
case class ByIndex(index: Int) extends TimerTarget // index from getPendingTimers
case class AsWokenUpAt(at: Instant) extends TimerTarget // release nearest timer as if woken up at `at`
// Note: "release first/nearest" is equivalent to AsWokenUpAt(Instant.MAX)
}
Open Questions
- What should happen if
releaseTimer(ByName("x")) matches multiple timers (e.g., in parallel)? Release first match or error?
Acceptance Criteria
Summary
Add the ability to manually release (skip) a timer in a workflow for operational purposes, plus a query method to list pending timers.
Motivation
In production scenarios, waiting for a timer to naturally expire is not always desirable:
Proposed API
Query Pending Timers
Release Timer
Open Questions
releaseTimer(ByName("x"))matches multiple timers (e.g., in parallel)? Release first match or error?Acceptance Criteria
getPendingTimersreturns list of awaiting timers with name/index/resumeAtreleaseTimerworks withByName,ByIndex, andAsWokenUpAttargets