Providers
This guide covers provider selection, configuration, and provider-specific behaviors.
Provider Overview
Key-Value Stores
| Provider | Package | Best For |
|---|---|---|
| Redis | grub/redis | Distributed cache, sessions, pub/sub |
| BadgerDB | grub/badger | Embedded, high-performance, SSD-optimized |
| BoltDB | grub/bolt | Embedded, simple, single-file |
Blob Storage
| Provider | Package | Best For |
|---|---|---|
| AWS S3 | grub/s3 | Standard object storage, AWS ecosystem |
| MinIO | grub/minio | Self-hosted, S3-compatible, lightweight |
| Google Cloud Storage | grub/gcs | GCP ecosystem, global CDN |
| Azure Blob | grub/azure | Azure ecosystem, tiered storage |
SQL Databases
| Driver | Package | Dialect |
|---|---|---|
| PostgreSQL | grub/postgres | astql/postgres |
| MariaDB | grub/mariadb | astql/mariadb |
| SQLite | grub/sqlite | astql/sqlite |
| SQL Server | grub/mssql | astql/mssql |
Vector Databases
| Provider | Package | Best For |
|---|---|---|
| Qdrant | grub/qdrant | Self-hosted, full-featured, gRPC |
| Pinecone | grub/pinecone | Managed service, simple API |
| Milvus | grub/milvus | Large-scale, distributed |
| Weaviate | grub/weaviate | GraphQL API, semantic search |
Key-Value Provider Setup
Redis
import (
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/redis"
goredis "github.com/redis/go-redis/v9"
)
func main() {
client := goredis.NewClient(&goredis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
defer client.Close()
store := grub.NewStore[Session](redis.New(client))
}
Features:
- Full TTL support (native)
- List uses SCAN (cursor-based, safe for large datasets)
- GetBatch uses MGET
- SetBatch uses pipelined SET
Cluster support:
client := goredis.NewClusterClient(&goredis.ClusterOptions{
Addrs: []string{"node1:6379", "node2:6379", "node3:6379"},
})
store := grub.NewStore[Session](redis.New(client))
BadgerDB
import (
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/badger"
badgerdb "github.com/dgraph-io/badger/v4"
)
func main() {
opts := badgerdb.DefaultOptions("./data")
opts.Logger = nil // Silence logs
db, err := badgerdb.Open(opts)
if err != nil {
log.Fatal(err)
}
defer db.Close()
store := grub.NewStore[Config](badger.New(db))
}
Features:
- Full TTL support (native)
- Embedded (no external service)
- SSD-optimized LSM tree
- Supports encryption at rest
In-memory mode:
opts := badgerdb.DefaultOptions("").WithInMemory(true)
db, _ := badgerdb.Open(opts)
BoltDB
import (
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/bolt"
"go.etcd.io/bbolt"
)
func main() {
db, err := bbolt.Open("my.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Bucket name scopes the data
store := grub.NewStore[Config](bolt.New(db, "configs"))
}
Features:
- Single-file database
- Bucket-scoped (multiple stores per DB)
- ACID transactions
- No TTL support (returns
ErrTTLNotSupported)
Multiple buckets:
configs := grub.NewStore[Config](bolt.New(db, "configs"))
sessions := grub.NewStore[Session](bolt.New(db, "sessions"))
Blob Provider Setup
AWS S3
import (
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/s3"
"github.com/aws/aws-sdk-go-v2/config"
awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
)
func main() {
cfg, err := config.LoadDefaultConfig(context.Background())
if err != nil {
log.Fatal(err)
}
client := awss3.NewFromConfig(cfg)
bucket := grub.NewBucket[Document](s3.New(client, "my-bucket"))
}
Notes:
- Delete checks Exists first (S3 doesn't error on missing keys)
- List uses ListObjectsV2 with continuation tokens
- Metadata preserved in ObjectInfo
Custom endpoint (LocalStack):
client := awss3.NewFromConfig(cfg, func(o *awss3.Options) {
o.BaseEndpoint = aws.String("http://localhost:4566")
o.UsePathStyle = true
})
MinIO
import (
"github.com/zoobz-io/grub"
grubminio "github.com/zoobz-io/grub/minio"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
func main() {
client, err := minio.New("localhost:9000", &minio.Options{
Creds: credentials.NewStaticV4("minioadmin", "minioadmin", ""),
Secure: false,
})
if err != nil {
log.Fatal(err)
}
bucket := grub.NewBucket[Document](grubminio.New(client, "my-bucket"))
}
Notes:
- Delete checks Exists first (MinIO doesn't error on missing keys)
- Uses
minio-go/v7— lighter than the AWS SDK - List uses channel-based iteration with recursive option
- Metadata preserved via UserMetadata
Google Cloud Storage
import (
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/gcs"
"cloud.google.com/go/storage"
)
func main() {
client, err := storage.NewClient(context.Background())
if err != nil {
log.Fatal(err)
}
defer client.Close()
bucket := grub.NewBucket[Document](gcs.New(client, "my-bucket"))
}
Notes:
- ContentType set via writer.ContentType
- Metadata preserved
- Uses storage.Iterator for List
Azure Blob Storage
import (
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/azure"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
)
func main() {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
log.Fatal(err)
}
client, err := azblob.NewClient(
"https://myaccount.blob.core.windows.net/",
cred,
nil,
)
if err != nil {
log.Fatal(err)
}
bucket := grub.NewBucket[Document](azure.New(client, "my-container"))
}
Notes:
- Metadata may require separate GetProperties call
- Uses NewListBlobsFlatPager for List
SQL Database Setup
PostgreSQL
import (
"github.com/zoobz-io/grub"
_ "github.com/zoobz-io/grub/postgres" // Register driver
"github.com/zoobz-io/astql/postgres"
"github.com/jmoiron/sqlx"
)
func main() {
db, err := sqlx.Connect("postgres", "postgres://user:pass@localhost/db?sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
users := grub.NewDatabase[User](db, "users", postgres.New())
}
Vector Search with pgvector: PostgreSQL with the pgvector extension is supported via Database[T]. Use soy's SelectExpr() and OrderByExpr() for distance calculations. See the Vector Search cookbook for complete examples.
MariaDB
import (
_ "github.com/zoobz-io/grub/mariadb"
"github.com/zoobz-io/astql/mariadb"
)
db, _ := sqlx.Connect("mysql", "user:pass@tcp(localhost:3306)/db")
users, _ := grub.NewDatabase[User](db, "users", mariadb.New())
SQLite
import (
_ "github.com/zoobz-io/grub/sqlite"
"github.com/zoobz-io/astql/sqlite"
)
db, _ := sqlx.Connect("sqlite", "./data.db")
users, _ := grub.NewDatabase[User](db, "users", sqlite.New())
SQL Server
import (
_ "github.com/zoobz-io/grub/mssql"
"github.com/zoobz-io/astql/mssql"
)
db, _ := sqlx.Connect("sqlserver", "sqlserver://user:pass@localhost?database=db")
users, _ := grub.NewDatabase[User](db, "users", mssql.New())
Vector Provider Setup
Qdrant
import (
"github.com/qdrant/go-client/qdrant"
"github.com/zoobz-io/grub"
grubqdrant "github.com/zoobz-io/grub/qdrant"
)
func main() {
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
index := grub.NewIndex[Embedding](grubqdrant.New(client, grubqdrant.Config{
Collection: "documents",
}))
}
Features:
- Full filter operator support
- gRPC-based communication
- String IDs (hashed internally to uint64)
Pinecone
import (
"github.com/pinecone-io/go-pinecone/v2/pinecone"
"github.com/zoobz-io/grub"
grubpinecone "github.com/zoobz-io/grub/pinecone"
)
func main() {
client, err := pinecone.NewClient(pinecone.NewClientParams{
ApiKey: os.Getenv("PINECONE_API_KEY"),
})
if err != nil {
log.Fatal(err)
}
indexConn, err := client.Index(pinecone.NewIndexConnParams{
Host: "your-index.svc.environment.pinecone.io",
})
if err != nil {
log.Fatal(err)
}
index := grub.NewIndex[Embedding](grubpinecone.New(indexConn, grubpinecone.Config{
Namespace: "default",
}))
}
Features:
- Managed service (no infrastructure)
- Limited filter operators (Eq, Ne, In, Nin only)
Milvus
import (
"github.com/milvus-io/milvus-sdk-go/v2/client"
"github.com/zoobz-io/grub"
grubmilvus "github.com/zoobz-io/grub/milvus"
)
func main() {
milvusClient, err := client.NewClient(ctx, client.Config{
Address: "localhost:19530",
})
if err != nil {
log.Fatal(err)
}
defer milvusClient.Close()
index := grub.NewIndex[Embedding](grubmilvus.New(milvusClient, grubmilvus.Config{
Collection: "documents",
IDField: "id",
VectorField: "embedding",
MetadataField: "metadata",
}))
}
Features:
- Full filter operator support
- Configurable field names
- Distributed architecture
Weaviate
import (
"github.com/weaviate/weaviate-go-client/v5/weaviate"
"github.com/zoobz-io/grub"
grubweaviate "github.com/zoobz-io/grub/weaviate"
)
func main() {
cfg := weaviate.Config{
Host: "localhost:8080",
Scheme: "http",
}
client, err := weaviate.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
index := grub.NewIndex[Embedding](grubweaviate.New(client, grubweaviate.Config{
Class: "Document",
Properties: []string{"category", "score", "tags"}, // metadata fields to retrieve
}))
}
Features:
- Full filter operator support
- Requires explicit Properties configuration for metadata retrieval
- GraphQL-based queries
Provider Selection Guide
When to Use Each Provider
| Scenario | Recommended Provider |
|---|---|
| Distributed sessions | Redis |
| Local cache | BadgerDB |
| Simple config storage | BoltDB |
| File uploads | S3/MinIO/GCS/Azure |
| Structured queries | PostgreSQL/MariaDB |
| Embedded SQL | SQLite |
| Development/testing | BadgerDB (in-memory) or SQLite |
| Vector search with existing Postgres | DatabaseT with pgvector |
| Self-hosted vector search | Qdrant or Milvus |
| Managed vector service | Pinecone |
| Semantic search with GraphQL | Weaviate |
Feature Comparison (Key-Value)
| Feature | Redis | Badger | Bolt |
|---|---|---|---|
| TTL | ✓ | ✓ | ✗ |
| Distributed | ✓ | ✗ | ✗ |
| Embedded | ✗ | ✓ | ✓ |
| Encryption | ✗ | ✓ | ✗ |
| Transactions | ✓ | ✓ | ✓ |
Feature Comparison (Blob)
| Feature | S3 | MinIO | GCS | Azure |
|---|---|---|---|---|
| Versioning | ✓ | ✓ | ✓ | ✓ |
| Lifecycle | ✓ | ✓ | ✓ | ✓ |
| CDN | CloudFront | ✗ | Cloud CDN | Azure CDN |
| Presigned URLs | ✓ | ✓ | ✓ | SAS tokens |
| Self-hosted | ✗ | ✓ | ✗ | ✗ |
Feature Comparison (Vector)
| Feature | Qdrant | Pinecone | Milvus | Weaviate |
|---|---|---|---|---|
| Self-hosted | ✓ | ✗ | ✓ | ✓ |
| Managed service | ✓ | ✓ | ✓ | ✓ |
| Distance metrics | Multiple | Fixed | Multiple | Multiple |
| Full filter support | ✓ | ✗ | ✓ | ✓ |
Filter Operator Support (Vector)
| Operator | Qdrant | Pinecone | Milvus | Weaviate |
|---|---|---|---|---|
| Eq | ✓ | ✓ | ✓ | ✓ |
| Ne | ✓ | ✓ | ✓ | ✓ |
| Gt/Gte/Lt/Lte | ✓ | ✗ | ✓ | ✓ |
| In | ✓ | ✓ | ✓ | ✓ |
| Nin | ✓ | ✓ | ✓ | ✓ |
| Like | ✓ | ✗ | ✓ | ✓ |
| Contains | ✓ | ✗ | ✓ | ✓ |
| And/Or/Not | ✓ | ✓ | ✓ | ✓ |
Note: Pinecone returns ErrOperatorNotSupported for unsupported operators.
Switching Providers
One of grub's benefits is easy provider switching:
// Development
store := grub.NewStore[Session](badger.New(devDB))
// Production
store := grub.NewStore[Session](redis.New(prodClient))
Business logic remains unchanged. Only the provider initialization differs.
Caveats:
- TTL behavior differs (BoltDB doesn't support it)
- Batch atomicity varies by provider
- Some providers have unique features not exposed through grub