Skip to content

Latest commit

 

History

History
219 lines (162 loc) · 5.61 KB

File metadata and controls

219 lines (162 loc) · 5.61 KB

Countdown — Resonate example

Example: Countdown with Durable Sleep (Python)

A countdown timer that demonstrates durable sleep - long pauses without consuming resources, surviving crashes and resuming exactly where it left off.

What This Demonstrates

Problem: Long-Running Timers

Traditional countdown timers keep a process running continuously, wasting resources during sleep periods. If the process crashes, you lose the countdown state.

Solution: Durable Sleep

Resonate's durable sleep allows workflows to:

  • Pause for extended periods (hours, days, weeks)
  • Consume zero resources while sleeping
  • Survive crashes and resume at the right time
  • Send periodic notifications reliably

Use Cases

This pattern applies to:

  • Scheduled reminders - Meeting notifications, task deadlines
  • SLA monitors - Alert if response not received in time
  • Rate limiting - Enforce delays between operations
  • Periodic reports - Daily/weekly/monthly automation
  • Countdown timers - Track time until events

How It Works

def countdown(ctx: Context, count: int, interval, url: str):
    for i in range(count, 0, -1):
        # Send notification (durable)
        yield ctx.run(notify, message=f"Countdown: {i}", url=url)

        # Sleep for interval (process can exit here)
        yield ctx.sleep(interval)

    # Final notification
    yield ctx.run(notify, message="Countdown complete", url=url)

What Happens on Each Iteration

  1. Send notification - ctx.run() executes and checkpoints the result
  2. Sleep - ctx.sleep() suspends the workflow, process can exit
  3. Resume - Resonate wakes the workflow after the interval
  4. Repeat - Continue from checkpoint without re-sending notifications

Crash Recovery

If the process crashes during sleep:

  • Workflow state is preserved in Resonate
  • Resonate automatically resumes at the scheduled wake time
  • No notifications are duplicated
  • Countdown continues as if nothing happened

Running the Example

Prerequisites

  • Python 3.13+
  • uv (Python package manager)
  • ntfy.sh account or webhook URL

Installation

# Install dependencies
uv sync

Usage

# Start the countdown worker
uv run python countdown.py

Then trigger a countdown via the Resonate API or another process:

# Start 10-minute countdown with 1-minute intervals
curl -X POST http://localhost:8001/promises \
  -H "Content-Type: application/json" \
  -d '{
    "id": "countdown/demo",
    "timeout": 36000000,
    "data": {
      "func": "countdown",
      "args": [10, 60000, "https://ntfy.sh/your-topic"]
    }
  }'

Parameters:

  • count: Number of countdown steps (10 = 10 notifications)
  • interval: Milliseconds between steps (60000 = 1 minute)
  • url: Webhook URL for notifications (ntfy.sh, Slack, etc.)

Notification Format

Each notification sends JSON:

{
  "message": "Countdown: 7"
}

Configuration

Environment Variables

Create a .env file:

RESONATE_URL=http://localhost:8001
RESONATE_TOKEN=your-token-here
NTFY_URL=https://ntfy.sh/your-topic

Notification Backends

The example uses ntfy.sh by default, but works with any webhook:

Slack:

url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

Discord:

url = "https://discord.com/api/webhooks/YOUR/WEBHOOK"

Custom API:

url = "https://api.example.com/notifications"

Testing Failure Scenarios

Test 1: Crash During Sleep

  1. Start countdown with long interval:
# 5 steps, 30 seconds each
resonate.run("test/crash", countdown, 5, 30000, url)
  1. Wait for first notification

  2. Kill the process (Ctrl+C)

  3. Restart the process:

uv run python countdown.py
  1. Observe: Countdown resumes at the correct step, no duplicates

Test 2: Long-Running Countdown

# 24-hour countdown with hourly notifications
resonate.run("test/longrun", countdown, 24, 3600000, url)

Process can stop/start freely. Notifications arrive on schedule.

Code Structure

countdown.py    # Countdown workflow definition
pyproject.toml  # Dependencies

Key Concepts

Durable Sleep

  • ctx.sleep(milliseconds) suspends workflow
  • Process can exit, workflow state persists
  • Resonate resumes at the right time
  • No polling, no cron jobs needed

Idempotency

  • Each notification uses a deterministic ID
  • Replays don't duplicate notifications
  • ctx.run() checkpoints prevent re-execution

Resource Efficiency

  • Zero CPU/memory during sleep
  • Scale to zero between notifications
  • Pay only for active execution time

Production Considerations

  1. Monitoring: Track notification delivery success
  2. Error handling: Add retry logic for webhook failures
  3. Timeouts: Set workflow timeout longer than total countdown
  4. Backpressure: Limit concurrent countdown workflows
  5. Observability: Log each notification for audit trail

Learn More

Related Examples