Skip to content

Sleep

Durable pause that survives crashes and restarts. If the process crashes during the sleep, it resumes with only the remaining time.

What to notice:

  • turbine.Pause records the wake-up time as a step
  • On recovery, if the time has already passed, it returns immediately
  • The 202 response returns right away — the workflow runs in the background
go
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/YakirOren/turbine"
	"github.com/pocketbase/pocketbase"
	"github.com/pocketbase/pocketbase/core"
)

// ReminderWorkflow sends a reminder after a durable delay.
// If the process crashes during the sleep, it resumes with only the remaining time.
func ReminderWorkflow(ctx turbine.Context, userID string) (string, error) {
	// Wait 24 hours — survives crashes and restarts
	if err := turbine.Pause(ctx, 24*time.Hour); err != nil {
		return "", err
	}

	_, err := turbine.Do(ctx, func(ctx context.Context) (bool, error) {
		fmt.Printf("sending reminder to %s\n", userID)
		return true, nil
	}, turbine.WithStepName("send-reminder"))
	if err != nil {
		return "", err
	}

	return "reminder sent to " + userID, nil
}

func main() {
	app := pocketbase.New()

	rt := turbine.Setup(app, turbine.Config{})

	turbine.Register(rt, ReminderWorkflow)

	app.OnServe().BindFunc(func(e *core.ServeEvent) error {
		e.Router.POST("/remind/{userID}", func(re *core.RequestEvent) error {
			userID := re.Request.PathValue("userID")

			handle, err := turbine.Run(rt, ReminderWorkflow, userID)
			if err != nil {
				return re.JSON(500, map[string]string{"error": err.Error()})
			}

			return re.JSON(202, map[string]string{
				"workflow_id": handle.GetWorkflowID(),
				"status":      "sleeping for 24h",
			})
		})
		return e.Next()
	})

	if err := app.Start(); err != nil {
		log.Fatal(err)
	}
}