Changeset 0.27.1 (#167)

This commit is contained in:
2026-03-10 17:43:29 +00:00
parent 7e4f1581f2
commit 41be9d6081
16 changed files with 780 additions and 57 deletions

View File

@@ -160,12 +160,15 @@ func main() {
}
// Background workflow staleness sweep (v0.26.2): mark idle instances as stale
// + v0.27.0: retention enforcement — archive/delete completed instances per workflow policy
if cfg.WorkflowStaleHours > 0 {
go func() {
ticker := time.NewTicker(1 * time.Hour)
defer ticker.Stop()
for {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
// Staleness: mark idle active instances as stale
cutoff := time.Now().UTC().Add(-time.Duration(cfg.WorkflowStaleHours) * time.Hour)
res, err := database.DB.ExecContext(ctx, database.Q(`
UPDATE channels
@@ -179,6 +182,33 @@ func main() {
} else if n, _ := res.RowsAffected(); n > 0 {
log.Printf("🧹 workflows: marked %d instances as stale", n)
}
// v0.27.0: Retention enforcement — delete completed workflow channels
// where the parent workflow has retention.mode="delete" and
// retention.delete_after_days has elapsed since completion.
res2, err := database.DB.ExecContext(ctx, database.Q(`
DELETE FROM channels
WHERE type = 'workflow'
AND workflow_status IN ('completed', 'archived')
AND workflow_id IS NOT NULL
AND last_activity_at < $1
AND workflow_id IN (
SELECT id FROM workflows
WHERE retention IS NOT NULL
AND retention->>'mode' = 'delete'
AND (retention->>'delete_after_days')::int > 0
AND channels.last_activity_at < now() - ((retention->>'delete_after_days')::int || ' days')::interval
)
`), cutoff)
if err != nil {
// SQLite doesn't support JSON operators — skip retention on SQLite
if !database.IsSQLite() {
log.Printf("⚠ workflow retention enforcement failed: %v", err)
}
} else if n, _ := res2.RowsAffected(); n > 0 {
log.Printf("🧹 workflows: deleted %d expired instances (retention policy)", n)
}
cancel()
<-ticker.C
}
@@ -554,7 +584,7 @@ func main() {
protected.GET("/workflows/:id/versions/:version", wfH.GetVersion)
// Workflow instances (v0.26.2 — runtime lifecycle)
wfInstH := handlers.NewWorkflowInstanceHandler(stores)
wfInstH := handlers.NewWorkflowInstanceHandler(stores, hub, notifSvc)
protected.POST("/workflows/:id/start", wfInstH.Start)
protected.GET("/channels/:id/workflow/status", wfInstH.GetStatus)
protected.POST("/channels/:id/workflow/advance", wfInstH.Advance)