bolted

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2023 License: MIT Imports: 12 Imported by: 23

README

Bolted

Bolted is a lightweight and easy-to-use wrapper around bbolt database, providing additional features and a more concise API. With Bolted, you can work with nested buckets within transactions in a more expressive and readable manner. It also offers an observer pattern for change notifications, supports Prometheus metrics for monitoring, and integrates Open Telemetry spans for tracing read and write operations.

Features

  • Concise Transactions: Express nested bucket operations with fewer lines of code, making your codebase more readable and maintainable.
  • Observer Pattern: Receive notifications when values of interest change within a transaction, allowing you to respond to data updates effectively.
  • Prometheus Metrics: Monitor your database usage and performance with built-in support for publishing Prometheus metrics.
  • Open Telemetry Spans: Gain insights into the performance of read and write operations using Open Telemetry spans.

Why should I use it?

Consider this code that will read a value and unmarshal JSON from two nested buckets:

package example

import (
	"encoding/json"
	"errors"
	"fmt"

	"go.etcd.io/bbolt"
)

func ReadUser(db *bbolt.DB) (*User, error) {
	var v []byte

	err := db.View(func(tx *bbolt.Tx) error {
		b1 := tx.Bucket([]byte("foo"))
		if b1 == nil {
			return errors.New("bucket 'foo' does not exist")
		}
		b2 := b1.Bucket([]byte("bar"))
		if b2 == nil {
			return errors.New("bucket 'foo/bar' does not exist")
		}
		vd := b2.Get([]byte("baz"))
		if vd == nil {
			return errors.New("value 'foo/bar/baz' does not exist")
		}
		v = make([]byte, len(vd))
		copy(v, vd)
		return nil
	})
	if err != nil {
		return nil, fmt.Errorf("read tx failed: %w", err)
	}

	u := &User{}
	err = json.Unmarshal(v, u)
	if err != nil {
		return nil, fmt.Errorf("could not parse the user: %w", err)
	}

	return u, nil

}

and compare it to the identical code using bolted:

package example

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/draganm/bolted/dbpath"
	"github.com/draganm/bolted"
)

func ReadUserBolted(db bolted.Database) (*User, error) {
	var v []byte

	err := db.Read(context.Background(), func(tx bolted.ReadTx) error {
		v = tx.Get(dbpath.ToPath("foo", "bar", "baz"))
		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("read tx failed: %w", err)
	}

	u := &User{}
	err = json.Unmarshal(v, u)
	if err != nil {
		return nil, fmt.Errorf("could not parse the user: %w", err)
	}

	return u, nil

}

you will quickly notice that bolted will let you express the same semantic with 2 lines of code instead of 15.

Contributing

We welcome contributions to Bolted! If you find any issues or have ideas for improvements, feel free to open an issue or submit a pull request on GitHub.

License

Bolted is distributed under the MIT License, making it free and open-source for anyone to use.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrConflict = errors.New("conflict")
View Source
var ErrNotFound = errors.New("not found")

Functions

func IsConflict added in v0.8.0

func IsConflict(err error) bool

func IsNotFound added in v0.7.0

func IsNotFound(err error) bool

Types

type ChangeType added in v0.3.0

type ChangeType int
const (
	ChangeTypeNoChange ChangeType = iota
	ChangeTypeMapCreated
	ChangeTypeValueSet
	ChangeTypeDeleted
)

type Database added in v0.8.0

type Database interface {
	Read(func(tx ReadTx) error) error
	ReadWithContext(context.Context, func(tx ReadTx) error) error

	Write(func(tx WriteTx) error) error
	WriteWithContext(context.Context, func(tx WriteTx) error) error

	Observe(ctx context.Context, path dbpath.Matcher) <-chan ObservedChanges
	Close() error
	Stats() (*bbolt.Stats, error)
}

type Iterator

type Iterator interface {
	GetKey() string
	GetValue() []byte
	GetRawValue() []byte
	IsDone() bool
	Prev()
	Next()
	Seek(key string)
	First()
	Last()
}

type LocalDB added in v0.11.0

type LocalDB struct {
	// contains filtered or unexported fields
}

func Open

func Open(path string, mode os.FileMode, options Options) (*LocalDB, error)

func (*LocalDB) Close added in v0.11.0

func (b *LocalDB) Close() error

func (*LocalDB) Observe added in v0.11.0

func (b *LocalDB) Observe(ctx context.Context, path dbpath.Matcher) <-chan ObservedChanges

func (*LocalDB) Read added in v0.11.0

func (b *LocalDB) Read(fn func(tx ReadTx) error) (err error)

func (*LocalDB) ReadWithContext added in v1.0.1

func (b *LocalDB) ReadWithContext(ctx context.Context, fn func(tx ReadTx) error) (err error)

func (*LocalDB) Stats added in v0.11.0

func (b *LocalDB) Stats() (*bbolt.Stats, error)

func (*LocalDB) Write added in v0.11.0

func (b *LocalDB) Write(fn func(tx WriteTx) error) (err error)

func (*LocalDB) WriteWithContext added in v1.0.1

func (b *LocalDB) WriteWithContext(ctx context.Context, fn func(tx WriteTx) error) (err error)

type ObservedChange added in v0.6.0

type ObservedChange struct {
	Path dbpath.Path
	Type ChangeType
}

type ObservedChanges map[string]ChangeType

type ObservedChanges added in v0.6.0

type ObservedChanges []ObservedChange

func (ObservedChanges) TypeOfChange added in v0.6.1

func (o ObservedChanges) TypeOfChange(path dbpath.Path) ChangeType

func (ObservedChanges) Update added in v0.8.0

type Options added in v0.11.0

type Options struct {
	bbolt.Options
}

type ReadTx

type ReadTx interface {
	Get(path dbpath.Path) []byte
	Iterate(path dbpath.Path) Iterator
	Exists(path dbpath.Path) bool
	IsMap(path dbpath.Path) bool
	GetSizeOf(path dbpath.Path) uint64
	ID() uint64
	DumpDatabase(w io.Writer) (n int64)
	GetDBFileSize() int64
	Context() context.Context
}

type WriteTx

type WriteTx interface {
	CreateMap(path dbpath.Path)
	Delete(path dbpath.Path)
	Put(path dbpath.Path, value []byte)
	SetFillPercent(float64)
	ReadTx
}

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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