-
Notifications
You must be signed in to change notification settings - Fork 0
Getting Started
This guide will walk you through installing Senna and running your first background job.
- Go 1.21 or later
- Redis 6.2+ or Valkey 7.2+
go get github.com/mgomes/sennaSenna requires Redis 6.0+ or Valkey 7.2+ as its backend. Make sure you have one running:
# Using Docker
docker run -d --name redis -p 6379:6379 redis:7
# Or using Valkey
docker run -d --name valkey -p 6379:6379 valkey/valkey:7.2Important
Configure Redis with maxmemory-policy noeviction to prevent data loss. If Redis evicts keys due to memory pressure, jobs and batch state can be lost.
The client is used to enqueue jobs from your application.
package main
import (
"context"
"log"
"github.com/mgomes/senna"
"github.com/mgomes/senna/client"
)
func main() {
c, err := client.New(&client.Config{
Redis: senna.RedisConfig{
Addr: "localhost:6379",
},
Namespace: "myapp",
})
if err != nil {
log.Fatal(err)
}
defer c.Close()
ctx := context.Background()
// Enqueue a job
job, err := c.Enqueue(ctx, "send_email", map[string]any{
"to": "user@example.com",
"subject": "Welcome!",
"body": "Thanks for signing up.",
})
if err != nil {
log.Fatal(err)
}
log.Printf("Enqueued job: %s", job.ID)
}The worker processes jobs from the queue using registered handlers.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/mgomes/senna"
"github.com/mgomes/senna/worker"
)
func main() {
w, err := worker.New(&worker.Config{
Redis: senna.RedisConfig{
Addr: "localhost:6379",
},
Namespace: "myapp",
Settings: senna.WorkerSettings{
Concurrency: 10,
Queues: []senna.QueueConfig{
{Name: "default", Priority: 5},
},
ShutdownTimeout: 30 * time.Second,
},
})
if err != nil {
log.Fatal(err)
}
// Register a handler for "send_email" jobs
w.Register("send_email", func(ctx context.Context, job *senna.Job) error {
to := job.Args["to"].(string)
subject := job.Args["subject"].(string)
body := job.Args["body"].(string)
fmt.Printf("Sending email to %s: %s\n", to, subject)
fmt.Printf("Body: %s\n", body)
// Your email sending logic here...
return nil
})
// Run the worker (blocks until shutdown signal)
ctx := context.Background()
if err := w.Run(ctx); err != nil {
log.Fatal(err)
}
}Here's a complete example with both client and worker in a single file for testing:
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/mgomes/senna"
"github.com/mgomes/senna/client"
"github.com/mgomes/senna/worker"
)
func main() {
ctx := context.Background()
redisConfig := senna.RedisConfig{Addr: "localhost:6379"}
namespace := "example"
// Create and start the worker in a goroutine
w, err := worker.New(&worker.Config{
Redis: redisConfig,
Namespace: namespace,
Settings: senna.WorkerSettings{
Concurrency: 5,
Queues: []senna.QueueConfig{
{Name: "default", Priority: 5},
},
},
})
if err != nil {
log.Fatal(err)
}
// Register job handler
w.Register("greet", func(ctx context.Context, job *senna.Job) error {
name := job.Args["name"].(string)
fmt.Printf("Hello, %s!\n", name)
return nil
})
// Start worker in background
go func() {
if err := w.Run(ctx); err != nil {
log.Printf("Worker error: %v", err)
}
}()
// Give worker time to start
time.Sleep(100 * time.Millisecond)
// Create client and enqueue jobs
c, err := client.New(&client.Config{
Redis: redisConfig,
Namespace: namespace,
})
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Enqueue some jobs
for _, name := range []string{"Alice", "Bob", "Charlie"} {
job, err := c.Enqueue(ctx, "greet", map[string]any{"name": name})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Enqueued job %s for %s\n", job.ID, name)
}
// Wait for interrupt signal
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
fmt.Println("\nShutting down...")
w.Stop()
}Run this example:
go run main.goOutput:
Enqueued job abc123 for Alice
Enqueued job def456 for Bob
Enqueued job ghi789 for Charlie
Hello, Alice!
Hello, Bob!
Hello, Charlie!
The Namespace configuration prefixes all Redis keys, allowing multiple applications to share the same Redis instance:
// Application A
client.New(&client.Config{
Redis: redisConfig,
Namespace: "app_a",
})
// Application B
client.New(&client.Config{
Redis: redisConfig,
Namespace: "app_b",
})Each application's jobs and queues are isolated from each other.
Tip
Use descriptive namespace names that identify your application. This makes it easier to debug and monitor in production.
- Learn about Enqueuing Jobs and all available options
- Configure Workers for production
- Set up Priority Queues
- Add Middleware for logging and metrics