fudge

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 18, 2025 License: MIT Imports: 13 Imported by: 0

README

Fudge - Compact key/value store

Table of Contents

Description

Package fudge is a fast and simple key/value store written using Go's standard library.

Fork from pudge

It presents the following:

  • Supporting very efficient lookup, insertions and deletions
  • Performance is comparable to hash tables
  • Ability to get the data in sorted order, which enables additional operations like range scan
  • Select with limit/offset/from key, with ordering or by prefix
  • Safe for use in goroutines
  • Space efficient
  • Very short and simple codebase
  • Well tested, used in production

这个包对于我来说最大的好处是单文件存储并且能快速存取少量数据。

Usage

package main

import (
	"log"

	"github.com/gnuos/fudge"
)

func main() {
	// Close all database on exit
	defer fudge.CloseAll()

	// Set (directories will be created)
	fudge.Set("../test/test", "Hello", "World")

	// Get (lazy open db if needed)
	var output string
	fudge.Get("../test/test", "Hello", &output)
	log.Println("Output:", output)

	ExampleSelect()
}

//ExampleSelect
func ExampleSelect() {
	cfg := &fudge.Config{ SyncInterval: 1 } // every second fsync
	db, err := fudge.Open("../test/db", cfg)
	if err != nil {
		log.Panic(err)
	}
	defer db.DeleteFile()
	type Point struct {
		X int
		Y int
	}
	for i := 100; i >= 0; i-- {
		p := &Point{X: i, Y: i}
		db.Set(i, p)
	}
	var point Point
	db.Get(8, &point)
	log.Printf("%v", point)
	// Output: {8 8}
	// Select 2 keys, from 7 in ascending order
	keys, _ := db.Keys(7, 2, 0, true)
	for _, key := range keys {
		var p Point
		db.Get(key, &p)
		log.Println(p)
	}
	// Output: {8 8}
	// Output: {9 9}
}

Cookbook

  • Store data of any type. Fudge uses CBOR encoder/decoder internally. No limits on keys/values size.
fudge.Set("strings", "Hello", "World")
fudge.Set("numbers", 1, 42)

type User struct {
	Id int
	Name string
}
u := &User{Id: 1, Name: "name"}
fudge.Set("users", u.Id, u)

  • Fudge is stateless and safe for use in goroutines. You don't need to create/open files before use. Just write data to fudge, don't worry about state.

  • Fudge is parallel. Readers don't block readers, but a writer - does, but by the stateless nature of fudge it's safe to use multiples files for storages.

  • Default store system: like memcache + file storage. Fudge uses in-memory hashmap for keys, and writes values to files (no value data stored in memory). But you may use inmemory mode for values, with custom config:

cfg = fudge.DefaultConfig()
cfg.StoreMode = 2
db, err := fudge.Open(dbPrefix+"/"+group, cfg)
...
db.Counter(key, val)

In that case, all data is stored in memory and will be stored on disk only on Close.

  • Don't forget to close all opened databases on shutdown/kill.
 	// Wait for interrupt signal to gracefully shutdown the server 
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt, os.Kill)
	<-quit
	log.Println("Shutdown Server ...")
	if err := fudge.CloseAll(); err != nil {
		log.Println("Fudge Shutdown err:", err)
	}
  • example recovery function for gin framework
func globalRecover(c *gin.Context) {
	defer func(c *gin.Context) {

		if err := recover(); err != nil {
			if err := fudge.CloseAll(); err != nil {
				log.Println("Database Shutdown err:", err)
			}
			log.Println("Server recovery with err:", err)
			gin.RecoveryWithWriter(gin.DefaultErrorWriter)
		}
	}(c)
	c.Next()
}
	
  • Fudge has a primitive select/query engine.
// Select 2 keys, from 7 in ascending order
   keys, _ := db.Keys(7, 2, 0, true)
// select keys from db where key>7 order by keys asc limit 2 offset 0
  • Fudge will work well on SSD or spined disks. Fudge doesn't eat memory or storage or your sandwich. No hidden compaction/rebalancing/resizing and so on tasks. No LSM Tree. No MMap. It's a very simple database with less than 500 LOC.

Disadvantages

  • No transaction system. All operations are isolated, but you don't may batching them with automatic rollback.
  • Keys function (select/query engine) may be slow. Speed of query may vary from 10ms to 1sec per million keys. Fudge don't use BTree/Skiplist or Adaptive radix tree for store keys in ordered way on every insert. Ordering operation is "lazy" and run only if needed.
  • No fsync on every insert. Most of database fsync data by the timer too
  • Deleted data don't remove from physically (but upsert will try to reuse space). You may shrink database only with backup right now
fudge.BackupAll("backup")
  • Keys automatically convert to binary and ordered with binary comparator. It's simple for use, but ordering will not work correctly for negative numbers for example
  • Author of project don't work at Google or Facebook and his name not Howard Chu or Brad Fitzpatrick. But I'm open for issue or contributions.

Motivation

Some databases very well for writing. Some of the databases very well for reading. But fudge is well balanced for both types of operations. It has small api, and don't have hidden graveyards. It's just hashmap where values written in files. And you may use one database for in-memory/persistent storage in a stateless stressfree way.

Licence

MIT

Documentation

Overview

Package pudge implements a low-level key/value store in pure Go. Keys stored in memory, Value stored on disk

Usage

package main

import (
	"log"

	"github.com/gnuos/fudge"
)

func main() {
	cfg := fudge.DefaultConfig()
	cfg.SyncInterval = 0 //disable every second fsync
	db, err := fudge.Open("../test/db", cfg)
	if err != nil {
		log.Panic(err)
	}
	defer db.DeleteFile()
	type Point struct {
		X int
		Y int
	}
	for i := 100; i >= 0; i-- {
		p := &Point{X: i, Y: i}
		db.Set(i, p)
	}
	point := new(Point)
	db.Get(8, point)
	log.Println(point)
	// Output: {8 8}
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultConfig = &Config{
	FileMode:     0644,
	DirMode:      0755,
	SyncInterval: 0,
	StoreMode:    0,
}

DefaultConfig is default config

View Source
var (

	// ErrKeyNotFound - key not found
	ErrKeyNotFound = errors.New("error: key not found")
)

Functions

func BackupAll

func BackupAll(dir string) (err error)

BackupAll - backup all opened Db if dir not set it will be backup delete old backup file before run ignore all errors

func Close

func Close(f string) error

Close - sync & close files. Return error if any.

func CloseAll

func CloseAll() (err error)

CloseAll - close all opened Db

func Count

func Count(f string) (int, error)

Count returns the number of items in the Db.

func Delete

func Delete(f string, key any) error

Delete remove key Returns error if key not found

func DeleteFile

func DeleteFile(file string) error

DeleteFile close db and delete file

func Get

func Get(f string, key any, value any) error

Get return value by key with opening if needed Return error if any.

Example
Set("test/test", "Hello", "World")
var output string
Get("test/test", "Hello", &output)
defer CloseAll()
fmt.Printf("%v\n", output)
Output:
World

func Gets

func Gets(file string, keys []any) (result [][]byte)

Gets return key/value pairs in random order result contains key and value Gets not return error if key not found If no keys found return empty result

func Has

func Has(f string, key any) (bool, error)

Has return true if key exists. Return error if any.

func KeyToBinary

func KeyToBinary(v any) ([]byte, error)

KeyToBinary return key in bytes

func Keys

func Keys(f string, from any, limit, offset int, asc bool) ([][]byte, error)

Keys return keys in ascending or descending order (false - descending,true - ascending) if limit == 0 return all keys if offset > 0 - skip offset records If from not nil - return keys after from (from not included)

func Set

func Set(f string, key any, value any) error

Set store any key value to db with opening if needed

Example
Set("test/test", "Hello", "World")
defer CloseAll()

func Sets

func Sets(file string, pairs []any) (err error)

Sets store vals and keys Use it for mass insertion every pair must contain key and value

func ValToBinary

func ValToBinary(v any) ([]byte, error)

ValToBinary return value in bytes

Types

type Cmd

type Cmd struct {
	Seek    uint32
	Size    uint32
	KeySeek uint32
	Val     []byte
}

Cmd represent keys and vals addresses

type Config

type Config struct {
	FileMode     int // 0644
	DirMode      int // 0755
	SyncInterval int // in seconds
	StoreMode    int // 0 - file first, 2 - memory first(with persist on close), 2 - with empty file - memory without persist
}

Config fo db Default FileMode = 0644 Default DirMode = 0755 Default SyncInterval = 0 sec, 0 - disable sync (os will sync, typically 30 sec or so) If StroreMode==2 && file == "" - pure inmemory mode

type DB

type DB struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

DB represent database

func Open

func Open(f string, cfg *Config) (*DB, error)

Open return db object if it opened. Create new db if not exist. Read db to obj if exist. Or error if any. Default Config (if nil): &Config{FileMode: 0644, DirMode: 0755, SyncInterval: 0}

Example
cfg := &Config{
	SyncInterval: 0} //disable every second fsync
db, err := Open("test/db", cfg)
if err != nil {
	log.Panic(err)
}
defer db.DeleteFile()
type Point struct {
	X int
	Y int
}
for i := 100; i >= 0; i-- {
	p := &Point{X: i, Y: i}
	db.Set(i, p)
}
point := new(Point)
db.Get(8, point)
fmt.Printf("%v\n", *point)
Output:
{8 8}

func (*DB) Close

func (db *DB) Close() error

Close - sync & close files. Return error if any.

func (*DB) Count

func (db *DB) Count() (int, error)

Count returns the number of items in the Db.

func (*DB) Delete

func (db *DB) Delete(key any) error

Delete remove key Returns error if key not found

func (*DB) DeleteFile

func (db *DB) DeleteFile() error

DeleteFile close and delete file

func (*DB) FileSize

func (db *DB) FileSize() (int64, error)

FileSize returns the total size of the disk storage used by the DB.

func (*DB) Get

func (db *DB) Get(key any, value any) error

Get return value by key Return error if any.

func (*DB) Has

func (db *DB) Has(key any) (bool, error)

Has return true if key exists. Return error if any.

func (*DB) Keys

func (db *DB) Keys(from any, limit, offset int, asc bool) ([][]byte, error)

Keys return keys in ascending or descending order (false - descending,true - ascending) if limit == 0 return all keys if offset > 0 - skip offset records If from not nil - return keys after from (from not included)

func (*DB) KeysByPrefix

func (db *DB) KeysByPrefix(prefix []byte, limit, offset int, asc bool) ([][]byte, error)

KeysByPrefix return keys with prefix in ascending or descending order (false - descending,true - ascending) if limit == 0 return all keys if offset > 0 - skip offset records If from not nil - return keys after from (from not included)

func (*DB) Set

func (db *DB) Set(key any, value any) error

Set store any key value to db

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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