flashdb

package module
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2022 License: MIT Imports: 13 Imported by: 2

README

FlashDB

flashdb

FlashDB is a simple, in-memory, key/value store in pure Go. It persists to disk, is ACID compliant, and uses locking for multiple readers and a single writer. It supports redis like operations for data structures like SET, SORTED SET, HASH and STRING.

Features

Architecture

FlashDB is made of composable libraries that can be used independently and are easy to understand. The idea is to bridge the learning for anyone new on how to build a simple ACID database.

Getting Started

Installing

To start using FlashDB, install Go and run go get:

$ go get -u github.com/arriqaaq/flashdb

This will retrieve the library.

Opening a database

The primary object in FlashDB is a DB. To open or create your database, use the flashdb.New() function:

package main

import (
	"log"

	"github.com/arriqaaq/flashdb"
)

func main() {
	config := &flashdb.Config{Path:"/tmp", EvictionInterval: 10}
	db, err := flashdb.New(config)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	...
}

It's also possible to open a database that does not persist to disk by keeping the path in the config empty.

config := &flashdb.Config{Path:"", EvictionInterval: 10}
flashdb.New(config)

Transactions

All reads and writes must be performed from inside a transaction. FlashDB can have one write transaction opened at a time, but can have many concurrent read transactions. Each transaction maintains a stable view of the database. In other words, once a transaction has begun, the data for that transaction cannot be changed by other transactions.

When a transaction fails, it will roll back, and revert all changes that occurred to the database during that transaction. When a read/write transaction succeeds all changes are persisted to disk.

Read-only Transactions

A read-only transaction should be used when you don't need to make changes to the data. The advantage of a read-only transaction is that there can be many running concurrently.

err := db.View(func(tx *flashdb.Tx) error {
	...
	return nil
})
Read/write Transactions

A read/write transaction is used when you need to make changes to your data. There can only be one read/write transaction running at a time. So make sure you close it as soon as you are done with it.

err := db.Update(func(tx *flashdb.Tx) error {
	...
	return nil
})
Setting and getting key/values

To set a value you must open a read/write transaction:

err := db.Update(func(tx *flashdb.Tx) error {
	_, _, err := tx.Set("mykey", "myvalue")
	return err
})

To get the value:

err := db.View(func(tx *flashdb.Tx) error {
	val, err := tx.Get("mykey")
	if err != nil{
		return err
	}
	fmt.Printf("value is %s\n", val)
	return nil
})

Commands

String Hash Set ZSet
SET HSET SADD ZADD
GET HGET SISMEMBER ZSCORE
DELETE HGETALL SRANDMEMBER ZCARD
EXPIRE HDEL SREM ZRANK
TTL HEXISTS SMOVE ZREVRANK
HLEN SCARD ZRANGE
HKEYS SMEMBERS ZREVRANGE
HVALS SUNION ZREM
HCLEAR SDIFF ZGETBYRANK
SCLEAR ZREVGETBYRANK
ZSCORERANGE
ZREVSCORERANGE
ZCLEAR

Setup

FlashDB

Run the server

go build -o bin/flashdb-server cmd/server/main.go

Run the client

go build -o bin/flashdb-cli cmd/cli/main.go

Benchmarks

  • Go Version : go1.11.4 darwin/amd64
  • OS: Mac OS X 10.13.6
  • Architecture: x86_64
  • 16 GB 2133 MHz LPDDR3
  • CPU: 3.1 GHz Intel Core i7
badger 2022/03/09 14:04:44 INFO: All 0 tables opened in 0s
goos: darwin
goarch: amd64
pkg: github.com/arriqaaq/flashbench
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz

BenchmarkBadgerDBPutValue64B-16     	    9940	    141844 ns/op	    2208 B/op	      68 allocs/op
BenchmarkBadgerDBPutValue128B-16    	    7701	    192942 ns/op	    2337 B/op	      68 allocs/op
BenchmarkBadgerDBPutValue256B-16    	    7368	    142600 ns/op	    2637 B/op	      69 allocs/op
BenchmarkBadgerDBPutValue512B-16    	    6980	    148056 ns/op	    3149 B/op	      69 allocs/op
BenchmarkBadgerDBGet-16             	 1000000	      1388 ns/op	     408 B/op	       9 allocs/op

BenchmarkFlashDBPutValue64B-16      	  159992	      7208 ns/op	    1461 B/op	      19 allocs/op
BenchmarkFlashDBPutValue128B-16     	  175634	      9499 ns/op	    2003 B/op	      19 allocs/op
BenchmarkFlashDBPutValue256B-16     	  148362	      9278 ns/op	    3322 B/op	      19 allocs/op
BenchmarkFlashDBPutValue512B-16     	  120865	     16542 ns/op	    6037 B/op	      19 allocs/op
BenchmarkFlashDBGet-16              	 1881042	       643.9 ns/op	      32 B/op	       2 allocs/op

PASS
ok  	github.com/arriqaaq/flashbench	28.947s
With fsync enabled for every update transaction
badger 2022/03/09 14:04:44 INFO: All 0 tables opened in 0s
goos: darwin
goarch: amd64
pkg: github.com/arriqaaq/flashbench
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz

BenchmarkNutsDBPutValue64B-16     	      52	  20301019 ns/op	    1315 B/op	      17 allocs/op
BenchmarkNutsDBPutValue128B-16    	      63	  23496536 ns/op	    1059 B/op	      15 allocs/op
BenchmarkNutsDBPutValue256B-16    	      62	  20037952 ns/op	    1343 B/op	      15 allocs/op
BenchmarkNutsDBPutValue512B-16    	      62	  20090731 ns/op	    1754 B/op	      15 allocs/op

BenchmarkFlashDBPutValue64B-16     	     105	  16154174 ns/op	     770 B/op	      18 allocs/op
BenchmarkFlashDBPutValue128B-16    	      62	  21666543 ns/op	    1119 B/op	      18 allocs/op
BenchmarkFlashDBPutValue256B-16    	      61	  18631201 ns/op	    1956 B/op	      18 allocs/op
BenchmarkFlashDBPutValue512B-16    	      62	  20118103 ns/op	    3071 B/op	      18 allocs/op

PASS
ok  	github.com/arriqaaq/flashbench	28.947s

The benchmark code can be found here flashdb-bench.

TODO

FlashDB is a toy db and in early stages of development. A couple of to-do tasks listed:

  • Add transaction to eviction to avoid locks
  • Add more comprehensive unit test cases
  • Add more bench tests
  • Add explicit logging messages

References

FlashDB is inspired by NutsDB and BuntDB.

Contact

Farhan Khan @arriqaaq

License

FlashDB source code is available under the MIT License

Documentation

Index

Constants

View Source
const (
	DefaultAddr         = "127.0.0.1:8000"
	DefaultMaxKeySize   = uint32(1 * 1024)
	DefaultMaxValueSize = uint32(8 * 1024)
)
View Source
const (
	MinimumStartupTime = 500 * time.Millisecond
	MaximumStartupTime = 2 * MinimumStartupTime
)
View Source
const (
	StringRecord uint16 = iota
	HashRecord
	SetRecord
	ZSetRecord
)
View Source
const (
	StringSet uint16 = iota
	StringRem
	StringExpire
)

The operations on Strings.

View Source
const (
	HashHSet uint16 = iota
	HashHDel
	HashHClear
	HashHExpire
)

The operations on Hash.

View Source
const (
	SetSAdd uint16 = iota
	SetSRem
	SetSMove
	SetSClear
	SetSExpire
)

The operations on Set.

View Source
const (
	ZSetZAdd uint16 = iota
	ZSetZRem
	ZSetZClear
	ZSetZExpire
)

The operations on Sorted Set.

Variables

View Source
var (
	ErrInvalidKey     = errors.New("invalid key")
	ErrInvalidTTL     = errors.New("invalid ttl")
	ErrExpiredKey     = errors.New("key has expired")
	ErrTxClosed       = errors.New("tx closed")
	ErrDatabaseClosed = errors.New("database closed")
	ErrTxNotWritable  = errors.New("tx not writable")
)
View Source
var (
	ErrInvalidEntry = errors.New("invalid entry")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	Addr             string `json:"addr" toml:"addr"`
	Path             string `json:"path" toml:"path"`                           // dir path for append-only logs
	EvictionInterval int    `json:"eviction_interval" toml:"eviction_interval"` // in seconds
	// NoSync disables fsync after writes. This is less durable and puts the
	// log at risk of data loss when there's a server crash.
	NoSync bool
}

func DefaultConfig

func DefaultConfig() *Config

type DataType

type DataType = string
const (
	String DataType = "String"
	Hash   DataType = "Hash"
	Set    DataType = "Set"
	ZSet   DataType = "ZSet"
)

type FlashDB

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

func New

func New(config *Config) (*FlashDB, error)

func (*FlashDB) Begin

func (db *FlashDB) Begin(writable bool) (*Tx, error)

Begin opens a new transaction. Multiple read-only transactions can be opened at the same time but there can only be one read/write transaction at a time. Attempting to open a read/write transactions while another one is in progress will result in blocking until the current read/write transaction is completed.

All transactions must be closed by calling Commit() or Rollback() when done.

func (*FlashDB) Close

func (db *FlashDB) Close() error

func (*FlashDB) Update

func (db *FlashDB) Update(fn func(tx *Tx) error) error

Update executes a function within a managed read/write transaction. The transaction has been committed when no error is returned. In the event that an error is returned, the transaction will be rolled back. When a non-nil error is returned from the function, the transaction will be rolled back and the that error will be return to the caller of Update().

func (*FlashDB) View

func (db *FlashDB) View(fn func(tx *Tx) error) error

View executes a function within a managed read-only transaction. When a non-nil error is returned from the function that error will be return to the caller of View().

type Tx

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

Tx represents a transaction on the database. This transaction can either be read-only or read/write. Read-only transactions can be used for retrieving values for keys and iterating through keys and values. Read/write transactions can set and delete keys.

All transactions must be committed or rolled-back when done.

func (*Tx) Commit

func (tx *Tx) Commit() error

Commit writes all changes to disk. An error is returned when a write error occurs, or when a Commit() is called from a read-only transaction.

func (*Tx) Delete

func (tx *Tx) Delete(key string) error

func (*Tx) Exists

func (tx *Tx) Exists(key string) bool

func (*Tx) Expire

func (tx *Tx) Expire(key string, duration int64) (err error)

func (*Tx) Get

func (tx *Tx) Get(key string) (val string, err error)

func (*Tx) HClear

func (tx *Tx) HClear(key string) (err error)

func (*Tx) HDel

func (tx *Tx) HDel(key string, field ...string) (res int, err error)

func (*Tx) HExists

func (tx *Tx) HExists(key, field string) (ok bool)

func (*Tx) HExpire

func (tx *Tx) HExpire(key string, duration int64) (err error)

func (*Tx) HGet

func (tx *Tx) HGet(key string, field string) string

func (*Tx) HGetAll

func (tx *Tx) HGetAll(key string) []string

func (*Tx) HKeyExists

func (tx *Tx) HKeyExists(key string) (ok bool)

func (*Tx) HKeys

func (tx *Tx) HKeys(key string) (val []string)

func (*Tx) HLen

func (tx *Tx) HLen(key string) int

func (*Tx) HSet

func (tx *Tx) HSet(key string, field string, value string) (res int, err error)

func (*Tx) HTTL

func (tx *Tx) HTTL(key string) (ttl int64)

func (*Tx) HVals

func (tx *Tx) HVals(key string) (values []string)

func (*Tx) Rollback

func (tx *Tx) Rollback() error

Rollback closes the transaction and reverts all mutable operations that were performed on the transaction such as Set() and Delete().

Read-only transactions can only be rolled back, not committed.

func (*Tx) SAdd

func (tx *Tx) SAdd(key string, members ...string) (err error)

func (*Tx) SCard

func (tx *Tx) SCard(key string) int

func (*Tx) SClear

func (tx *Tx) SClear(key string) (err error)

SClear clear the specified key in set.

func (*Tx) SDiff

func (tx *Tx) SDiff(keys ...string) (values []string)

func (*Tx) SExpire

func (tx *Tx) SExpire(key string, duration int64) (err error)

SExpire set expired time for the key in set.

func (*Tx) SIsMember

func (tx *Tx) SIsMember(key string, member string) bool

func (*Tx) SKeyExists

func (tx *Tx) SKeyExists(key string) (ok bool)

SKeyExists returns if the key exists.

func (*Tx) SMembers

func (tx *Tx) SMembers(key string) (values []string)

func (*Tx) SMove

func (tx *Tx) SMove(src, dst string, member string) error

func (*Tx) SRandMember

func (tx *Tx) SRandMember(key string, count int) (values []string)

func (*Tx) SRem

func (tx *Tx) SRem(key string, members ...string) (res int, err error)

func (*Tx) STTL

func (tx *Tx) STTL(key string) (ttl int64)

STTL return time to live for the key in set.

func (*Tx) SUnion

func (tx *Tx) SUnion(keys ...string) (values []string)

func (*Tx) Set

func (tx *Tx) Set(key string, value string) error

func (*Tx) SetEx

func (tx *Tx) SetEx(key string, value string, duration int64) (err error)

func (*Tx) TTL

func (tx *Tx) TTL(key string) (ttl int64)

func (*Tx) ZAdd

func (tx *Tx) ZAdd(key string, score float64, member string) error

func (*Tx) ZCard

func (tx *Tx) ZCard(key string) int

func (*Tx) ZClear

func (tx *Tx) ZClear(key string) (err error)

func (*Tx) ZExpire

func (tx *Tx) ZExpire(key string, duration int64) (err error)

func (*Tx) ZGetByRank

func (tx *Tx) ZGetByRank(key string, rank int) []interface{}

func (*Tx) ZKeyExists

func (tx *Tx) ZKeyExists(key string) (ok bool)

func (*Tx) ZRange

func (tx *Tx) ZRange(key string, start, stop int) []interface{}

func (*Tx) ZRangeWithScores

func (tx *Tx) ZRangeWithScores(key string, start, stop int) []interface{}

func (*Tx) ZRank

func (tx *Tx) ZRank(key string, member string) int64

func (*Tx) ZRem

func (tx *Tx) ZRem(key string, member string) (ok bool, err error)

func (*Tx) ZRevGetByRank

func (tx *Tx) ZRevGetByRank(key string, rank int) []interface{}

func (*Tx) ZRevRange

func (tx *Tx) ZRevRange(key string, start, stop int) []interface{}

func (*Tx) ZRevRangeWithScores

func (tx *Tx) ZRevRangeWithScores(key string, start, stop int) []interface{}

func (*Tx) ZRevRank

func (tx *Tx) ZRevRank(key string, member string) int64

func (*Tx) ZRevScoreRange

func (tx *Tx) ZRevScoreRange(key string, max, min float64) []interface{}

func (*Tx) ZScore

func (tx *Tx) ZScore(key string, member string) (ok bool, score float64)

func (*Tx) ZScoreRange

func (tx *Tx) ZScoreRange(key string, min, max float64) []interface{}

func (*Tx) ZTTL

func (tx *Tx) ZTTL(key string) (ttl int64)

Directories

Path Synopsis
cmd
cli

Jump to

Keyboard shortcuts

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