PostgreSQL Store
Production-ready PostgreSQL store using Grove ORM.
The PostgreSQL store uses Grove ORM with the pgdriver for database access. It is the recommended store for production deployments that need ACID transactions and full SQL query power.
Usage
Standalone
import (
"github.com/xraph/grove"
_ "github.com/xraph/grove/drivers/pgdriver"
pgstore "github.com/xraph/relay/store/postgres"
)
db, err := grove.Open("pg", "postgres://user:pass@localhost:5432/relay?sslmode=disable")
if err != nil {
log.Fatal(err)
}
s := pgstore.New(db)
if err := s.Migrate(ctx); err != nil {
log.Fatal(err)
}
r, err := relay.New(relay.WithStore(s))With Forge extension
When using the Forge extension with Grove, the store is auto-constructed from the DI container:
app.Use(extension.New(
extension.WithGroveDatabase(""), // uses the default grove.DB
))Or via YAML config:
extensions:
relay:
grove_database: ""See the Forge Extension guide for details.
Migrations
Call s.Migrate(ctx) before first use. Migrations are managed by the Grove migrator with Go-defined migration functions. The migration group is named "relay" and creates five tables:
relay_event_types-- event type catalogrelay_endpoints-- webhook endpointsrelay_events-- inbound eventsrelay_deliveries-- delivery queue withFOR UPDATE SKIP LOCKEDdequeuerelay_dlq-- dead letter queue
Internals
| Aspect | Detail |
|---|---|
| Driver | Grove ORM with pgdriver |
| Migrations | Grove migrator with Go-defined migrations |
| Dequeue | FOR UPDATE SKIP LOCKED for concurrent delivery polling |
| Transactions | Database-level ACID |
| JSON fields | JSONB for schema, metadata, headers, payload |
| Timestamps | TIMESTAMPTZ |
| Ping | db.Ping(ctx) |
| Close | Closes the Grove connection |
When to use
- Production deployments requiring durability and ACID guarantees.
- Multi-process environments with concurrent delivery workers.
- When you need
FOR UPDATE SKIP LOCKEDfor efficient dequeue under concurrency.