Documentation
¶
Overview ¶
Package badgerx extends github.com/dgraph-io/badger/v4 with pluggable encoding and compression strategies, removing the need to manually serialize and compress values at every call site.
Design ¶
BadgerXDb wraps a badger.DB and applies an Encoder and a Compressor on every read and write. Both are swappable via functional options, defaulting to GobEncoderDecoder and DefaultNoOpCompressor when not specified.
Quick start ¶
db, _ := badger.Open(badger.DefaultOptions("/tmp/mydb"))
// default: gob encoding, no compression
xdb := badgerx.NewBadgerXDb(db)
defer xdb.Close()
type User struct{ Name string; Age int }
_ = xdb.Update([]byte("user:1"), User{Name: "somak", Age: 30})
var u User
_ = xdb.View([]byte("user:1"), &u)
Encoders ¶
Two encoders are provided out of the box:
- GobEncoderDecoder — default, best for Go-to-Go communication.
- JsonEncoderDecoder — human-readable, good for interoperability.
Implement the Encoder interface to supply your own (e.g. msgpack, protobuf).
Compressors ¶
Three compressors are provided:
- DefaultNoOpCompressor — default, no compression.
- ZstdCompressor — high compression ratio, fast decompression.
- SnappyCompressor — very fast, moderate compression ratio.
Implement the Compressor interface to supply your own.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BadgerXDb ¶
type BadgerXDb struct {
// contains filtered or unexported fields
}
BadgerXDb wraps a badger.DB with pluggable encoding and compression. Values are encoded then compressed on write, and decompressed then decoded on read, transparently at every Update and View call.
Use NewBadgerXDb to create an instance. Always call BadgerXDb.Close when done to release resources held by the compressor and the underlying DB.
func NewBadgerXDb ¶
NewBadgerXDb creates a new BadgerXDb wrapping the given badger.DB. Defaults to GobEncoderDecoder and DefaultNoOpCompressor unless overridden via WithEncoder or WithCompressor.
xdb := badgerx.NewBadgerXDb(db,
badgerx.WithEncoder(&badgerx.JsonEncoderDecoder{}),
badgerx.WithCompressor(zstdC),
)
Example ¶
ExampleNewBadgerXDb demonstrates creating a BadgerXDb with default settings (gob encoding, no compression) and performing a basic store and retrieve.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
db := openDB()
xdb := badgerx.NewBadgerXDb(db)
defer xdb.Close()
_ = xdb.Update([]byte("user:1"), User{Name: "somak", Age: 30})
var u User
_ = xdb.View([]byte("user:1"), &u)
fmt.Println(u.Name, u.Age)
}
Output: somak 30
func (*BadgerXDb) Close ¶
Close releases resources held by the compressor and closes the underlying badger DB. Always defer Close after creating a BadgerXDb:
xdb := badgerx.NewBadgerXDb(db) defer xdb.Close()
If both the compressor and the DB return errors on close, both are returned joined via errors.Join.
func (*BadgerXDb) IterateView ¶ added in v0.1.2
IterateView iterates over all keys sharing the given prefix, calling fn once per matching key. fn receives a DecodeFunc that decodes the current item's value into any pointer the caller provides.
opts controls iteration behaviour — use badger.DefaultIteratorOptions for forward iteration, or customise for reverse iteration, keys-only mode, or prefetch tuning:
// forward iteration (default)
xdb.IterateView([]byte("user:"), badger.DefaultIteratorOptions, fn)
// reverse iteration
opts := badger.DefaultIteratorOptions
opts.Reverse = true
xdb.IterateView([]byte("user:"), opts, fn)
Create a fresh variable inside fn on each call — do not reuse a variable declared outside, as it will be overwritten on every iteration:
var results []User
err := xdb.IterateView([]byte("user:"), badger.DefaultIteratorOptions, func(decode badgerx.DecodeFunc) error {
var u User
if err := decode(&u); err != nil {
return err
}
results = append(results, u)
return nil
})
Returning a non-nil error from fn stops iteration and surfaces that error as the return value of IterateView.
Example ¶
ExampleBadgerXDb_IterateView demonstrates iterating over all keys sharing a common prefix and collecting the decoded values into a slice.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
xdb := badgerx.NewBadgerXDb(openDB())
defer xdb.Close()
_ = xdb.Update([]byte("user:1"), User{Name: "alice", Age: 30})
_ = xdb.Update([]byte("user:2"), User{Name: "bob", Age: 25})
_ = xdb.Update([]byte("user:3"), User{Name: "carol", Age: 35})
var users []User
err := xdb.IterateView([]byte("user:"), badger.DefaultIteratorOptions, func(decode badgerx.DecodeFunc) error {
var u User
if err := decode(&u); err != nil {
return err
}
users = append(users, u)
return nil
})
if err != nil {
log.Fatal(err)
}
for _, u := range users {
fmt.Println(u.Name, u.Age)
}
}
Output: alice 30 bob 25 carol 35
func (*BadgerXDb) Update ¶
Update encodes value using the configured Encoder, optionally compresses it using the configured Compressor, and stores the result under key.
The same encoder and compressor must be active when reading the value back via BadgerXDb.View.
Example ¶
ExampleBadgerXDb_Update demonstrates storing a value under a key.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
xdb := badgerx.NewBadgerXDb(openDB())
defer xdb.Close()
err := xdb.Update([]byte("user:1"), User{Name: "somak", Age: 30})
if err != nil {
log.Fatal(err)
}
fmt.Println("stored")
}
Output: stored
func (*BadgerXDb) View ¶
View retrieves the value stored under key, decompresses it, and decodes it into v. v must be a non-nil pointer to the same type that was passed to BadgerXDb.Update.
Returns badger.ErrKeyNotFound if the key does not exist.
var u User
if err := xdb.View([]byte("user:1"), &u); errors.Is(err, badger.ErrKeyNotFound) {
// key not found
}
Example ¶
ExampleBadgerXDb_View demonstrates retrieving a value by key. Returns badger.ErrKeyNotFound when the key does not exist.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
xdb := badgerx.NewBadgerXDb(openDB())
defer xdb.Close()
_ = xdb.Update([]byte("user:1"), User{Name: "somak", Age: 30})
var u User
err := xdb.View([]byte("user:1"), &u)
if err != nil {
log.Fatal(err)
}
fmt.Println(u.Name, u.Age)
}
Output: somak 30
Example (NotFound) ¶
ExampleBadgerXDb_View_notFound demonstrates handling a missing key.
package main
import (
"errors"
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
xdb := badgerx.NewBadgerXDb(openDB())
defer xdb.Close()
var u User
err := xdb.View([]byte("user:missing"), &u)
if errors.Is(err, badger.ErrKeyNotFound) {
fmt.Println("not found")
}
}
Output: not found
type BdOptions ¶
type BdOptions func(*BadgerXDb)
BdOptions is a functional option for configuring a BadgerXDb. Use WithEncoder and WithCompressor to create options.
func WithCompressor ¶
func WithCompressor(c Compressor) BdOptions
WithCompressor returns a BdOptions that sets the compressor used by BadgerXDb. If not specified, DefaultNoOpCompressor is used by default (no compression).
func WithEncoder ¶
WithEncoder returns a BdOptions that sets the encoder used by BadgerXDb. If not specified, GobEncoderDecoder is used by default.
Example ¶
ExampleWithEncoder demonstrates switching to JSON encoding.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
xdb := badgerx.NewBadgerXDb(openDB(),
badgerx.WithEncoder(&badgerx.JsonEncoderDecoder{}),
)
defer xdb.Close()
_ = xdb.Update([]byte("user:1"), User{Name: "somak", Age: 30})
var u User
_ = xdb.View([]byte("user:1"), &u)
fmt.Println(u.Name, u.Age)
}
Output: somak 30
type Compressor ¶
type Compressor interface {
// Compress compresses data and returns the compressed bytes.
Compress(data []byte) ([]byte, error)
// Decompress decompresses data and returns the original bytes.
Decompress(data []byte) ([]byte, error)
// Close releases any resources held by the compressor.
Close() error
}
Compressor is the strategy interface for compressing and decompressing encoded byte slices before they are written to or after they are read from badger.
Implement this interface to provide a custom compression strategy. Close must release any resources held by the compressor.
type DecodeFunc ¶ added in v0.1.2
DecodeFunc is the function injected into each IterFunc call during BadgerXDb.IterateView. Call it with a non-nil pointer to decode the current item's value into your own variable:
fn := func(decode badgerx.DecodeFunc) error {
var u User
return decode(&u)
}
type DefaultNoOpCompressor ¶
type DefaultNoOpCompressor struct{}
DefaultNoOpCompressor implements Compressor with no compression. It is the default compressor used by BadgerXDb when no compressor is specified. Use WithCompressor to swap in ZstdCompressor or SnappyCompressor when storage efficiency matters.
func (*DefaultNoOpCompressor) Close ¶
func (z *DefaultNoOpCompressor) Close() error
Close is a no-op for DefaultNoOpCompressor.
func (*DefaultNoOpCompressor) Compress ¶
func (z *DefaultNoOpCompressor) Compress(data []byte) ([]byte, error)
Compress returns data unmodified.
func (*DefaultNoOpCompressor) Decompress ¶
func (z *DefaultNoOpCompressor) Decompress(data []byte) ([]byte, error)
Decompress returns data unmodified.
type Encoder ¶
type Encoder interface {
// Encode serializes v into a byte slice.
Encode(v any) ([]byte, error)
// Decode deserializes data into v. v must be a non-nil pointer.
Decode(data []byte, v any) error
}
Encoder is the strategy interface for serializing and deserializing values.
Encode must convert v into a byte slice suitable for storage in badger. Decode must reconstruct the original value from data into v, where v is always a non-nil pointer to the target type (e.g. *MyStruct).
Implement this interface to provide a custom encoding strategy such as msgpack, protobuf, or any other binary format.
type GobEncoderDecoder ¶
type GobEncoderDecoder struct{}
GobEncoderDecoder implements Encoder using the standard encoding/gob package. It is the default encoder used by BadgerXDb when no encoder is specified.
For structs that contain interface{} fields, call GobEncoderDecoder.RegisterType once at startup for each concrete type that may appear in those fields.
func (*GobEncoderDecoder) Decode ¶
func (g *GobEncoderDecoder) Decode(data []byte, v any) error
Decode deserializes gob-encoded data into v. v must be a non-nil pointer.
func (*GobEncoderDecoder) Encode ¶
func (g *GobEncoderDecoder) Encode(v any) ([]byte, error)
Encode serializes v into gob-encoded bytes.
func (*GobEncoderDecoder) RegisterType ¶
func (g *GobEncoderDecoder) RegisterType(v any)
RegisterType registers a concrete type with gob so it can be correctly encoded and decoded when stored inside an interface{} field. This is only required when your structs contain interface{} fields. Call once at application startup — not on every read or write.
enc := &badgerx.GobEncoderDecoder{}
enc.RegisterType(MyPayload{})
enc.RegisterType(AnotherPayload{})
xdb := badgerx.NewBadgerXDb(db, badgerx.WithEncoder(enc))
Example ¶
ExampleGobEncoderDecoder_RegisterType demonstrates registering a concrete type for structs that contain interface{} fields.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
type Payload struct{ Value string }
type Record struct {
Name string
Payload any
}
enc := &badgerx.GobEncoderDecoder{}
enc.RegisterType(Payload{})
xdb := badgerx.NewBadgerXDb(openDB(), badgerx.WithEncoder(enc))
defer xdb.Close()
_ = xdb.Update([]byte("rec:1"), Record{Name: "badgerx", Payload: Payload{Value: "hello"}})
var r Record
_ = xdb.View([]byte("rec:1"), &r)
fmt.Println(r.Name, r.Payload.(Payload).Value)
}
Output: badgerx hello
type IterFunc ¶ added in v0.1.2
type IterFunc func(decode DecodeFunc) error
IterFunc is the callback passed to BadgerXDb.IterateView. It is called once per matching key. The provided DecodeFunc decodes the current item only — create a fresh variable inside IterFunc on each call to avoid overwriting previous results:
var results []User
fn := func(decode badgerx.DecodeFunc) error {
var u User // fresh every iteration
if err := decode(&u); err != nil {
return err
}
results = append(results, u)
return nil
}
type JsonEncoderDecoder ¶
type JsonEncoderDecoder struct{}
JsonEncoderDecoder implements Encoder using the standard encoding/json package. Prefer this over GobEncoderDecoder when human-readable storage or cross-language interoperability is required. Values must be JSON-serializable (exported fields, json struct tags recommended).
type SnappyCompressor ¶
type SnappyCompressor struct{}
SnappyCompressor implements Compressor using the Snappy algorithm. It prioritises speed over compression ratio, making it a good fit for latency-sensitive workloads with large values where some size reduction is still desirable.
func (*SnappyCompressor) Close ¶
func (s *SnappyCompressor) Close() error
Close is a no-op for SnappyCompressor.
func (*SnappyCompressor) Compress ¶
func (s *SnappyCompressor) Compress(data []byte) ([]byte, error)
Compress compresses data using the Snappy algorithm.
func (*SnappyCompressor) Decompress ¶
func (s *SnappyCompressor) Decompress(data []byte) ([]byte, error)
Decompress decompresses snappy-compressed data.
type ZstdCompressor ¶
type ZstdCompressor struct {
// contains filtered or unexported fields
}
ZstdCompressor implements Compressor using the Zstandard algorithm. It offers an excellent compression ratio with fast decompression, making it well-suited for workloads where storage size matters more than write speed.
The encoder and decoder are created once and reused across calls — safe for concurrent use. Use NewZstdCompressor to create an instance.
func NewZstdCompressor ¶
func NewZstdCompressor() (*ZstdCompressor, error)
NewZstdCompressor creates a ZstdCompressor initialising reusable zstd encoder and decoder instances. The returned compressor is safe for concurrent use. Call ZstdCompressor.Close when done to release resources.
Example ¶
ExampleNewZstdCompressor demonstrates using Zstandard compression alongside the default gob encoder.
package main
import (
"fmt"
"log"
badger "github.com/dgraph-io/badger/v4"
badgerx "github.com/somak2kai/badgerx"
)
type User struct {
Name string
Age int
}
func openDB() *badger.DB {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLogger(nil))
if err != nil {
log.Fatal(err)
}
return db
}
func main() {
zstdC, err := badgerx.NewZstdCompressor()
if err != nil {
log.Fatal(err)
}
xdb := badgerx.NewBadgerXDb(openDB(), badgerx.WithCompressor(zstdC))
defer xdb.Close()
_ = xdb.Update([]byte("user:1"), User{Name: "somak", Age: 30})
var u User
_ = xdb.View([]byte("user:1"), &u)
fmt.Println(u.Name, u.Age)
}
Output: somak 30
func (*ZstdCompressor) Close ¶
func (z *ZstdCompressor) Close() error
Close releases resources held by the zstd encoder and decoder.
func (*ZstdCompressor) Compress ¶
func (z *ZstdCompressor) Compress(data []byte) ([]byte, error)
Compress compresses data using the Zstandard algorithm.
func (*ZstdCompressor) Decompress ¶
func (z *ZstdCompressor) Decompress(data []byte) ([]byte, error)
Decompress decompresses zstd-compressed data.