zoobzio January 6, 2025 Edit this page

Providers

This guide covers provider selection, configuration, and provider-specific behaviors.

Provider Overview

Key-Value Stores

ProviderPackageBest For
Redisgrub/redisDistributed cache, sessions, pub/sub
BadgerDBgrub/badgerEmbedded, high-performance, SSD-optimized
BoltDBgrub/boltEmbedded, simple, single-file

Blob Storage

ProviderPackageBest For
AWS S3grub/s3Standard object storage, AWS ecosystem
MinIOgrub/minioSelf-hosted, S3-compatible, lightweight
Google Cloud Storagegrub/gcsGCP ecosystem, global CDN
Azure Blobgrub/azureAzure ecosystem, tiered storage

SQL Databases

DriverPackageDialect
PostgreSQLgrub/postgresastql/postgres
MariaDBgrub/mariadbastql/mariadb
SQLitegrub/sqliteastql/sqlite
SQL Servergrub/mssqlastql/mssql

Vector Databases

ProviderPackageBest For
Qdrantgrub/qdrantSelf-hosted, full-featured, gRPC
Pineconegrub/pineconeManaged service, simple API
Milvusgrub/milvusLarge-scale, distributed
Weaviategrub/weaviateGraphQL 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

ScenarioRecommended Provider
Distributed sessionsRedis
Local cacheBadgerDB
Simple config storageBoltDB
File uploadsS3/MinIO/GCS/Azure
Structured queriesPostgreSQL/MariaDB
Embedded SQLSQLite
Development/testingBadgerDB (in-memory) or SQLite
Vector search with existing PostgresDatabaseT with pgvector
Self-hosted vector searchQdrant or Milvus
Managed vector servicePinecone
Semantic search with GraphQLWeaviate

Feature Comparison (Key-Value)

FeatureRedisBadgerBolt
TTL
Distributed
Embedded
Encryption
Transactions

Feature Comparison (Blob)

FeatureS3MinIOGCSAzure
Versioning
Lifecycle
CDNCloudFrontCloud CDNAzure CDN
Presigned URLsSAS tokens
Self-hosted

Feature Comparison (Vector)

FeatureQdrantPineconeMilvusWeaviate
Self-hosted
Managed service
Distance metricsMultipleFixedMultipleMultiple
Full filter support

Filter Operator Support (Vector)

OperatorQdrantPineconeMilvusWeaviate
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