Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 Heartbeat Message Queue Service

A production-ready, QStash-inspired HTTP message queue service built in Go with Redis. Schedule HTTP requests for immediate or delayed execution with automatic retries, exponential backoff, and comprehensive tracking.

Go Redis License

📋 Table of Contents

✨ Features

Core Functionality

  • 📤 HTTP Message Scheduling - Send HTTP requests immediately or schedule for later
  • ⏰ Delayed Execution - Schedule messages to be sent at specific times
  • 🔄 Automatic Retries - Configurable retry logic with exponential backoff
  • 📊 Complete Tracking - Full lifecycle logging for every message
  • 💀 Dead Letter Queue - Failed messages after max retries for manual inspection
  • 🔍 Status API - Query message status and execution history

Performance & Reliability

  • ⚡ Smart Scheduler - Efficient Redis-based task scheduling with minimal sleep
  • 🔀 Parallel Workers - Multiple worker goroutines for concurrent processing
  • 🎯 Wake-up Signals - Immediate processing for urgent tasks
  • 🛡️ Graceful Shutdown - Clean service termination
  • 📈 Health Monitoring - Built-in health checks and statistics

Developer Experience

  • 🧪 Comprehensive Testing - 99+ unit and integration tests
  • 📝 Rich Logging - Detailed execution logs with worker/scheduler prefixes
  • 🌐 REST API - Simple HTTP interface
  • 🔧 Configurable - Defaults that work, customization when needed

🏗️ Architecture

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   HTTP Client   │───▶│   HTTP Server   │───▶│ Message Logger  │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                                │                       │
                                ▼                       │
                       ┌─────────────────┐              │
                       │  Task Storage   │              │
                       │  (Redis Sorted  │              │
                       │      Set)       │              │
                       └─────────────────┘              │
                                │                       │
                                ▼                       │
                       ┌─────────────────┐              │
                       │ Task Scheduler  │              │
                       │  (Heartbeat)    │              │
                       └─────────────────┘              │
                                │                       │
                                ▼                       │
                       ┌─────────────────┐              │
                       │  Task Workers   │              │
                       │   (3 Parallel)  │              │
                       └─────────────────┘              │
                                │                       │
                                ▼                       │
                       ┌─────────────────┐              │
                       │  HTTP Client    │              │
                       │   (Webhook)     │◀─────────────┘
                       └─────────────────┘

Components

  1. HTTP Server - Receives requests and schedules tasks
  2. Task Scheduler - Smart heartbeat that checks for ready tasks
  3. Task Workers - Execute HTTP requests and handle responses
  4. Message Logger - Tracks message lifecycle in Redis
  5. Task Storage - Redis sorted set for time-based scheduling
  6. Retry Manager - Handles backoff logic and retry scheduling

🚀 Quick Start

Prerequisites

  • Go 1.19 or later
  • Redis 6.0 or later

Installation

# Clone the repository
git clone https://github.com/your-username/heartbeat.git
cd heartbeat

# Install dependencies
go mod tidy

# Build the service
go build -o ./bin/heartbeat .

# Start Redis (if not already running)
redis-server

# Run the service
./heartbeat

The service will start on port 8080 by default.

First Message

# Send an immediate message
curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://httpbin.org/post",
    "payload": {"message": "Hello World!"},
    "method": "POST"
  }'

# Response
{
  "messageId": "msg_a1b2c3d4e5f6...",
  "status": "created",
  "executeAt": "2024-01-19T15:30:00Z",
  "attempt": 1,
  "maxRetries": 3
}

📚 API Reference

POST /send

Schedule a new HTTP message for delivery.

Request Body:

{
  "url": "string (required)",
  "payload": "object (optional)",
  "headers": "object (optional)",
  "method": "string (optional, default: POST)",
  "maxRetries": "number (optional, default: 3)",
  "delaySeconds": "number (optional, default: 0)"
}

Response:

{
  "messageId": "string",
  "status": "created",
  "executeAt": "ISO8601 timestamp",
  "attempt": 1,
  "maxRetries": "number"
}

Example:

# Delayed message (5 minutes)
curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/webhook",
    "payload": {"event": "user_signup", "userId": 123},
    "headers": {"Authorization": "Bearer token123"},
    "method": "POST",
    "maxRetries": 5,
    "delaySeconds": 300
  }'

GET /status/{messageId}

Get detailed status and logs for a message.

Response:

{
  "messageId": "string",
  "url": "string",
  "method": "string",
  "status": "CREATED|ACTIVE|DELIVERED|FAILED|RETRY",
  "createdAt": "ISO8601 timestamp",
  "maxRetries": "number",
  "scheduledTasks": "number",
  "nextExecuteAt": "ISO8601 timestamp (if scheduled)",
  "nextAttempt": "number (if scheduled)",
  "logs": [
    {
      "timestamp": "ISO8601 timestamp",
      "status": "string",
      "attempt": "number",
      "details": "string"
    }
  ]
}

GET /health

Health check endpoint.

Response:

{
  "status": "healthy|unhealthy",
  "timestamp": "ISO8601 timestamp",
  "scheduler_running": "boolean",
  "stats": {
    "scheduler_running": "boolean",
    "max_sleep": "string",
    "task_stats": {
      "total_tasks": "number",
      "ready_tasks": "number", 
      "future_tasks": "number",
      "next_execution": "ISO8601 timestamp"
    }
  }
}

GET /stats

Detailed service statistics.

Response:

{
  "timestamp": "ISO8601 timestamp",
  "scheduler_stats": {
    "scheduler_running": "boolean",
    "max_sleep": "string",
    "task_stats": {
      "total_tasks": "number",
      "ready_tasks": "number",
      "future_tasks": "number"
    }
  },
  "dlq_count": "number",
  "server_port": "number"
}

⚙️ Configuration

Environment Variables

Variable Default Description
PORT 8080 HTTP server port
REDIS_URL localhost:6379 Redis connection string
REDIS_DB 0 Redis database number
MAX_WORKERS 3 Number of parallel workers
MAX_RETRIES 3 Default maximum retries
BASE_DELAY 1s Base delay for exponential backoff
MAX_DELAY 5m Maximum delay between retries

Retry Strategy

The service uses exponential backoff with jitter:

  • Base Delay: 1 second
  • Multiplier: 2.0
  • Max Delay: 5 minutes
  • Max Retries: 3 (configurable per message)

Retry Schedule Example:

  • Attempt 1: Immediate
  • Attempt 2: ~1 second delay
  • Attempt 3: ~2 second delay
  • Attempt 4: ~4 second delay
  • Failed: Move to Dead Letter Queue

📖 Examples

Use Cases

1. Webhook Notifications

# Send user signup notification
curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://webhook.site/your-unique-url",
    "payload": {
      "event": "user.signup",
      "user": {"id": 123, "email": "user@example.com"},
      "timestamp": "2024-01-19T15:30:00Z"
    },
    "headers": {"X-Event-Type": "user.signup"}
  }'

2. Delayed Reminders

# Send reminder email in 1 hour
curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.sendgrid.com/v3/mail/send",
    "payload": {
      "to": "user@example.com",
      "subject": "Meeting Reminder",
      "body": "Your meeting starts in 1 hour"
    },
    "headers": {"Authorization": "Bearer sg.token"},
    "delaySeconds": 3600
  }'

3. Critical Notifications with High Retries

# Payment failure notification (max retries)
curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.slack.com/api/chat.postMessage",
    "payload": {
      "channel": "#alerts",
      "text": "Payment processing failed for order #12345"
    },
    "headers": {"Authorization": "Bearer xoxb-token"},
    "maxRetries": 10
  }'

Checking Message Status

# Get message status
MESSAGE_ID="msg_a1b2c3d4e5f6..."
curl http://localhost:8080/status/$MESSAGE_ID | jq

# Monitor until delivered
while true; do
  STATUS=$(curl -s http://localhost:8080/status/$MESSAGE_ID | jq -r '.status')
  echo "Status: $STATUS"
  [[ "$STATUS" == "DELIVERED" ]] && break
  sleep 1
done

🛠️ Development

Using the Makefile

This project includes a Makefile for common development tasks. Use the following commands:

# Build the project binary
make build

# Run all tests
make test

# Run all tests with verbose output
make test-verbose

# Run the application (after build)
make run

# Build and run in one step (dev mode)
make dev

# Lint the code (requires golangci-lint)
make lint

# Clean up build artifacts and kill any process on port 8080
make clean

Project Structure

heartbeat/
├── main.go                    # Application entry point
├── types.go                   # Core data structures
├── message_id.go              # UUID generation
├── message_logger.go          # Redis logging system
├── task_storage.go            # Redis sorted set operations
├── retry_backoff.go           # Retry logic and backoff
├── task_scheduler.go          # Smart heartbeat scheduler
├── task_worker.go             # HTTP request workers
├── http_server.go             # REST API server
├── integration_test.go        # End-to-end tests
├── *_test.go                  # Unit tests
├── go.mod                     # Dependencies
└── README.md                  # This file

Building

# Development build
go build -o heartbeat .

# Production build with optimizations
go build -ldflags="-s -w" -o heartbeat .

# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o heartbeat-linux .

Running in Development

# Run with verbose logging
go run . -v

# Run with custom port
PORT=9090 go run .

# Run with different Redis
REDIS_URL=redis://localhost:6380 go run .

🧪 Testing

The service has comprehensive test coverage with 99+ tests.

Run All Tests

# Run all tests
go test ./...

# Run with verbose output
go test -v ./...

# Run with coverage
go test -cover ./...

# Run integration tests only
go test -v integration_test.go *.go

Test Categories

  • Unit Tests: Test individual components in isolation
  • Integration Tests: Test complete workflows end-to-end
  • Redis Tests: Automatically skip if Redis unavailable

Example Test Output

=== RUN   TestIntegration_EndToEndImmediateTask
[HTTP] Created message msg_abc123, executing at 15:30:00
[SCHEDULER] Found 1 ready tasks
[WORKER-1] Processing task msg_abc123 (attempt 1)
[WORKER-1] Making POST request to https://httpbin.org/post
[WORKER-1] Task msg_abc123 succeeded: HTTP 200 OK
--- PASS: TestIntegration_EndToEndImmediateTask (1.11s)

📊 Monitoring

Health Checks

# Basic health check
curl http://localhost:8080/health

# Detailed statistics
curl http://localhost:8080/stats

Logs

The service provides detailed structured logging:

🚀 Starting Heartbeat Message Queue Service
✅ Connected to Redis
🔄 Starting 3 task workers
⏰ Starting task scheduler
🌐 Starting HTTP server on port 8080

[HTTP] Created message msg_abc123, executing at 15:30:00
[SCHEDULER] Found 1 ready tasks
[WORKER-1] Processing task msg_abc123 (attempt 1)
[WORKER-1] Task msg_abc123 succeeded: HTTP 200 OK

Metrics

Monitor these key metrics:

  • Queue Depth: Number of pending tasks
  • Processing Rate: Tasks processed per minute
  • Error Rate: Failed tasks / total tasks
  • Retry Rate: Tasks requiring retries
  • DLQ Size: Failed messages in dead letter queue

Dead Letter Queue

Failed messages are stored in Redis for manual inspection:

# Check DLQ size
redis-cli LLEN dlq

# View failed messages
redis-cli LRANGE dlq 0 -1

📄 License

MIT License - see LICENSE file for details.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Commit your changes (git commit -m 'Add amazing feature')
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request

📞 Support

  • 📧 Issues: GitHub Issues
  • 📖 Documentation: This README and inline code comments
  • 🧪 Examples: See the examples/ directory

Built with ❤️ in Go | QStash-inspired | Production Ready

About

recreation of upstash qstash

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages