Getting Started
This guide walks through installing Warp and building a realistic application: a User Session Store. We will start simple and progressively enable advanced features like distributed invalidation, leases, and metrics.
Installation
Install the library using go get:
go get github.com/mirkobrombin/go-warp/v1
Scenario: User Session Store
Imagine we are building a backend for a web application. We need to store user sessions and we need it to have:
- Fast access (cache)
- Persistence (database fallback)
- When a user logs out, their session must be invalidated immediately across all nodes
Step 1: Basic Setup (Local Cache + Store)
First, we set up Warp with an in-memory cache and a simulated persistent store.
package main
import (
"context"
"fmt"
"time"
"github.com/mirkobrombin/go-warp/v1/adapter"
"github.com/mirkobrombin/go-warp/v1/cache"
"github.com/mirkobrombin/go-warp/v1/core"
"github.com/mirkobrombin/go-warp/v1/merge"
)
type Session struct {
UserID string
Username string
ExpiresAt time.Time
}
func main() {
ctx := context.Background()
// 1. Storage Adapter: In a real app, this would be the Redis adapter or a custom adapter for your database (e.g. Postgres, MySQL).
// We use an in-memory store for this example.
store := adapter.NewInMemoryStore[Session]()
// 2. Cache: We use an in-memory LRU cache.
c := cache.NewInMemory[merge.Value[Session]]()
// 3. Warp Core: The orchestrator.
w := core.New[Session](c, store, nil, merge.NewEngine[Session]())
// 4. Registration: We MUST register keys before using them.
// ModeStrongLocal: Strong consistency on this node, no distributed events.
w.Register("session:*", core.ModeStrongLocal, 30*time.Minute)
// Usage
sess := Session{UserID: "u123", Username: "alice"}
// Set: Writes to Cache AND Store
if err := w.Set(ctx, "session:u123", sess); err != nil {
panic(err)
}
// Get: Reads from Cache (fast). If missing, fetches from Store and populates Cache.
v, err := w.Get(ctx, "session:u123")
if err != nil {
panic(err)
}
fmt.Printf("Session found: %+v\n", v)
}
Step 2: Distributed Invalidation
Now, imagine we scale our application to multiple nodes. If Alice logs out on Node A, Node B must also invalidate her session. We use ModeEventualDistributed and a SyncBus.
import "github.com/mirkobrombin/go-warp/v1/syncbus"
// ... inside main ...
// 1. Sync Bus: Connects nodes. In production, use NATS or Kafka.
// Here we use InMemoryBus to simulate it locally.
bus := syncbus.NewInMemoryBus()
w := core.New[Session](c, store, bus, merge.NewEngine[Session]())
// 2. Change Mode: EventualDistributed ensures invalidations are propagated.
w.Register("session:*", core.ModeEventualDistributed, 30*time.Minute)
// Node A invalidates
w.Invalidate(ctx, "session:u123")
// Node B (listening on the same bus) will automatically remove "session:u123" from its cache.
[!TIP]
UseModeEventualDistributedfor most cache scenarios where “eventual” consistency is acceptable. The invalidation happens milliseconds after the write.
Step 3: Leases (Group Invalidation)
What if we want to invalidate all sessions for a specific tenant or organization? Or maybe a “kill switch” for a user’s devices? Leases allow grouping keys.
// Create a lease for User u123
leaseID, _ := w.GrantLease(ctx, 24*time.Hour)
// Attach specific sessions to this lease
w.AttachKey(leaseID, "session:u123:mobile")
w.AttachKey(leaseID, "session:u123:desktop")
// Revoke the lease: Invalidates BOTH "session:u123:mobile" and "session:u123:desktop"
w.RevokeLease(ctx, leaseID)
Step 4: Metrics
Observability is critical. Warp exposes Prometheus metrics out of the box.
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/mirkobrombin/go-warp/v1/metrics"
)
// ...
reg := prometheus.NewRegistry()
// Enable metrics on Cache and Core
c := cache.NewInMemory[merge.Value[Session]](cache.WithMetrics[merge.Value[Session]](reg))
w := core.New[Session](c, store, bus, nil, core.WithMetrics[Session](reg))
// Expose endpoint
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
go http.ListenAndServe(":2112", nil)
Next Steps
- Core Concepts: Deep dive into Consistency Modes and Architecture.
- Presets: Production-ready configurations (NewRedisEventual, etc.).
- Sidecar Mode: Using Warp with Python, Node.js, etc.
- Best Practices: Recommended patterns for different use cases.
- Sync Bus: How to configure NATS/Kafka/Redis adapters.