katalis

package module
v0.0.0-...-401eb7d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2026 License: GPL-3.0 Imports: 9 Imported by: 0

README

Katalis

Go Reference Go Report Card codecov

Katalis is a type-safe, generic wrapper around the Pogreb embedded key-value store for Go. It leverages Go generics to provide compile-time type safety while maintaining the performance and simplicity of Pogreb.

Why Katalis?

Traditional key-value stores in Go require manual serialization/deserialization of keys and values to []byte. This approach is:

  • Error-prone: Type mismatches discovered only at runtime
  • Verbose: Boilerplate code for encoding/decoding
  • Unsafe: Easy to store/retrieve wrong types

Katalis solves these issues through:

  • Type Safety: Compile-time guarantees for key-value types
  • Zero Boilerplate: Automatic encoding/decoding via codec system
  • Flexibility: Pluggable codecs for any type
  • Modern Go: Built-in support for Go 1.23+ iterators

Features

  • Type-Safe Operations: Use Go generics to enforce type safety for key-value pairs
  • Flexible Codec System: Predefined codecs for primitives, custom codecs for complex types
  • High Performance: Built on Pogreb's fast, embedded storage engine
  • Iterator Support: Native support for Go 1.23+ range-over-function pattern
  • Error Handling: Multiple iteration strategies for different error handling needs
  • Simple API: Clean, idiomatic Go interface

Installation

go get github.com/NicoNex/katalis@latest

Requires Go 1.23 or later.

Quick Start

package main

import (
	"fmt"
	"github.com/NicoNex/katalis"
)

func main() {
	// Open a type-safe database with string keys and int values
	db, err := katalis.Open("mydb", katalis.StringCodec, katalis.IntCodec)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	// Put and Get operations are type-safe
	db.Put("age", 42)          // Compile-time type checking
	age, _ := db.Get("age")    // Returns int, not interface{}

	fmt.Println(age) // Output: 42
}

Design Philosophy

Codec System

Katalis uses a codec pattern to handle serialization transparently. Each type needs a codec that implements:

type Codec[T any] interface {
    Encode(T) ([]byte, error)
    Decode([]byte) (T, error)
}
Predefined Codecs

Katalis provides codecs for all Go primitive types:

// Integer types
katalis.IntCodec, katalis.Int8Codec, katalis.Int16Codec, katalis.Int32Codec, katalis.Int64Codec
katalis.UintCodec, katalis.Uint8Codec, katalis.Uint16Codec, katalis.Uint32Codec, katalis.Uint64Codec

// Floating point
katalis.Float32Codec, katalis.Float64Codec

// Strings and bytes
katalis.StringCodec, katalis.BytesCodec
Custom Types with Gob

For structs and complex types, use Gob:

type User struct {
	Name string
	Age  int
}

// Option 1: Explicit type parameter
db, _ := katalis.Open("users.db", katalis.StringCodec, katalis.Gob[User]())

// Option 2: Type inference
var user User
db, _ := katalis.Open("users.db", katalis.StringCodec, katalis.Gob(user))
Iteration Methods

Katalis provides three iteration strategies, each with different tradeoffs:

1. Items() - Simple, Error-Skipping Iteration

Use when you want clean, simple iteration that automatically skips corrupted entries:

for key, value := range db.Items() {
	fmt.Printf("%s: %d\n", key, value)
	// Automatically skips entries that fail to decode
}

When to use: Default choice for most cases, when you want simplicity and resilience without explicit error handling.

2. AllItems() - Explicit Error Handling

Use when you need to know about and handle decode errors explicitly:

for entry, err := range db.AllItems() {
	if err != nil {
		log.Printf("Error decoding entry: %v", err)
		continue // or handle differently
	}
	fmt.Printf("%s: %d\n", entry.Key, entry.Value)
}

When to use: Production monitoring, debugging, when you need to log/report errors, or take specific action on failures.

3. Fold() - Callback-Based Iteration

Use when you need custom error handling or accumulation logic:

total := 0
err := db.Fold(func(key string, val int, err error) error {
	if err != nil {
		return err // Stop on error
	}
	total += val
	return nil
})

When to use: Aggregations, reductions, custom control flow.

Usage Examples

Basic CRUD Operations
db, _ := katalis.Open("db", katalis.StringCodec, katalis.IntCodec)
defer db.Close()

// Put
db.Put("score", 100)

// Get
score, err := db.Get("score")
if err != nil {
	// Handle error
}

// Has
exists, _ := db.Has("score")

// Delete
db.Del("score")
Working with Complex Types
type Article struct {
	Title   string
	Author  string
	Content string
	Tags    []string
}

db, _ := katalis.Open(
	"articles.db",
	katalis.StringCodec,      // Keys are strings
	katalis.Gob[Article](),   // Values are Articles
)
defer db.Close()

article := Article{
	Title:   "Understanding Katalis",
	Author:  "Alice",
	Content: "...",
	Tags:    []string{"go", "database"},
}

// Store
db.Put("article-1", article)

// Retrieve with full type safety
retrieved, _ := db.Get("article-1")
fmt.Println(retrieved.Title) // No type assertions needed!
Iteration Examples
// Simple iteration (automatically skips errors)
count := 0
for key, val := range db.Items() {
	count++
}

// Iteration with explicit error handling
for entry, err := range db.AllItems() {
	if err != nil {
		log.Printf("Corrupted entry: %v", err)
		continue
	}
	process(entry.Key, entry.Value)
}

// Early exit
for key, val := range db.Items() {
	if key == "target" {
		break // Stop iteration
	}
}
Error Handling Patterns
// Pattern 1: Strict error handling
for entry, err := range db.AllItems() {
	if err != nil {
		return fmt.Errorf("database corrupted: %w", err)
	}
	// Process entry.Key and entry.Value
}

// Pattern 2: Graceful degradation
var failed []string
for entry, err := range db.AllItems() {
	if err != nil {
		failed = append(failed, "unknown")
		continue
	}
	// Process valid entries
}

// Pattern 3: Fold with accumulation
err := db.Fold(func(key string, val int, err error) error {
	if err != nil {
		return err // Propagate error
	}
	// Process and accumulate
	return nil
})

API Reference

Database Operations
// Open database with codecs
Open[KT, VT any](path string, keyCodec Codec[KT], valCodec Codec[VT]) (DB[KT, VT], error)
OpenOptions[KT, VT any](path string, keyCodec Codec[KT], valCodec Codec[VT], opts *Options) (DB[KT, VT], error)

// CRUD operations
Put(key KT, val VT) error
Get(key KT) (VT, error)
Has(key KT) (bool, error)
Del(key KT) error

// Iteration
Items() iter.Seq2[KT, VT]                            // Simple, skips errors
AllItems() iter.Seq2[Entry[KT, VT], error]           // With error handling
Fold(fn func(key KT, val VT, err error) error) error // Callback-based

// Close
Close() error
Entry Type
type Entry[KT, VT any] struct {
	Key   KT
	Value VT
}

Used by AllItems() to return key-value pairs with error information.

Performance Considerations

  • Codec Choice: Primitive codecs (Int, String) are faster than Gob for simple types
  • Iteration Method: Items() and AllItems() have similar performance; use Items() for simplicity, AllItems() when you need error details
  • Database Options: Tune Pogreb options via OpenOptions for specific workloads

Contributing

Contributions are welcome! Please ensure:

  • Tests pass: go test ./...
  • Code is formatted: go fmt ./...
  • Changes are documented

License

See LICENSE file for details.

Acknowledgments

Built on top of Pogreb by Constantine Peresypkin.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	UintCodec   = uintCodec{}
	Uint16Codec = uint16Codec{}
	Uint32Codec = uint32Codec{}
	Uint64Codec = uint64Codec{}

	IntCodec   = intCodec{}
	Int16Codec = int16Codec{}
	Int32Codec = int32Codec{}
	Int64Codec = int64Codec{}

	Float64Codec = float64Codec{}
	Float32Codec = float32Codec{}

	BytesCodec  = bytesCodec{}
	StringCodec = stringCodec{}
)

Predefined codecs for common Go types. These codecs handle encoding and decoding of primitive types to/from byte slices for database storage.

View Source
var ErrIterationDone = pogreb.ErrIterationDone

Functions

This section is empty.

Types

type Codec

type Codec[T any] interface {
	Encode(T) ([]byte, error)
	Decode([]byte) (T, error)
}

type DB

type DB[KT, VT any] struct {
	*pogreb.DB
	// contains filtered or unexported fields
}

func Open

func Open[KT, VT any](path string, keyCodec Codec[KT], valCodec Codec[VT]) (db DB[KT, VT], err error)

Open opens or creates a new DB. The DB must be closed after use, by calling Close method.

func OpenOptions

func OpenOptions[KT, VT any](path string, keyCodec Codec[KT], valCodec Codec[VT], opts *Options) (db DB[KT, VT], err error)

OpenOptions is like Open but accepts an Options struct.

func (DB[KT, VT]) AllItems

func (db DB[KT, VT]) AllItems() iter.Seq2[Entry[KT, VT], error]

AllItems returns an iterator over all key-value pairs in the database with error reporting. Unlike Items, decode errors are yielded to the caller rather than terminating iteration.

func (DB[KT, VT]) Del

func (db DB[KT, VT]) Del(key KT) error

Del deletes the value for the given key from the DB.

func (DB[KT, VT]) Fold

func (db DB[KT, VT]) Fold(fn func(key KT, val VT, err error) error) (err error)

Fold iterates over all keys in the database calling the function `fn` for each key. If the function returns an error, no further keys are processed and the error returned.

func (DB[KT, VT]) Get

func (db DB[KT, VT]) Get(key KT) (res VT, err error)

Get returns the value for the given key stored in the DB or an empty value if the key doesn't exist.

func (DB[KT, VT]) Has

func (db DB[KT, VT]) Has(key KT) (bool, error)

Has returns true if the DB contains the given key.

func (DB[KT, VT]) Items

func (db DB[KT, VT]) Items() iter.Seq2[KT, VT]

Items returns an iterator over all key-value pairs in the database. Decode errors are silently skipped, allowing iteration to continue. Use AllItems if you need to handle errors explicitly.

func (DB[KT, VT]) Put

func (db DB[KT, VT]) Put(key KT, val VT) error

Put sets the value for the given key. It updates the value for the existing key.

type Entry

type Entry[KT, VT any] struct {
	Key   KT
	Value VT
}

Entry represents a key-value pair from the database. It is used by AllItems to return both the key and value together with potential errors during iteration.

type GobCodec

type GobCodec[T any] struct{}

GobCodec is a generic codec that uses Go's gob encoding to serialize values. It works with any type that can be encoded by the encoding/gob package.

func Gob

func Gob[T any](_ ...T) (g GobCodec[T])

Gob returns a GobCodec for type T. The optional variadic parameter allows type inference from a value.

func (GobCodec[T]) Decode

func (pc GobCodec[T]) Decode(b []byte) (t T, err error)

Decode deserializes the value using gob decoding.

func (GobCodec[T]) Encode

func (pc GobCodec[T]) Encode(a T) ([]byte, error)

Encode serializes the value using gob encoding.

type Options

type Options = pogreb.Options

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL