A minimalistic in-memory key value store.

Overview

Kiwi Logo

A minimalistic in-memory key value store.

Go CI Docs CI Docs CD PkgGoDev

Overview

You can think of Kiwi as thread safe global variables. This kind of library comes in helpful when you need to manage state accross your application which can be mutated with multiple threads. Kiwi protects your keys with mutex locks so you don't have to.

Head over to kiwi.sdslabs.co for more details and documentation.

Installation

Kiwi requires Go >= 1.14

Kiwi can be integrated with your application just like any other go library.

go get -u github.com/sdslabs/kiwi

Now you can import kiwi any where in your code.

import "github.com/sdslabs/kiwi"

Basic usage

Create a store, add key and play with it. It's that easy!

store := stdkiwi.NewStore()

if err := store.AddKey("my_string", "str"); err != nil {
  // handle error
}

myString := store.Str("my_string")

if err := myString.Update("Hello, World!"); err != nil {
  // handle error
}

str, err := myString.Get()
if err != nil {
  // handle error
}

fmt.Println(str) // Hello, World!

Check out the tutorial to learn how to use Kiwi.

Benchmarks

Following are the benchmarks comparing Kiwi with BuntDB on a MacBook Pro (8th gen Intel i5 2.4GHz processor, 8GB RAM).

❯ go test -bench=. -test.benchmem ./benchmark
goos: darwin
goarch: amd64
pkg: github.com/sdslabs/kiwi/benchmark
BenchmarkBuntDB_Update-8        11777931                96.6 ns/op            48 B/op          1 allocs/op
BenchmarkBuntDB_View-8          23310963                47.1 ns/op            48 B/op          1 allocs/op
BenchmarkKiwi_Update-8          10356004               115 ns/op              48 B/op          3 allocs/op
BenchmarkKiwi_Get-8             21910110                53.2 ns/op             0 B/op          0 allocs/op
PASS
ok      github.com/sdslabs/kiwi/benchmark       6.216s

Following are the key differences due to which Kiwi is a little slow:

  1. BuntDB supports transactions, i.e., it locks the database once to apply all the operations (and this is what is tested).
  2. Kiwi supports dynamic data-types, which means, allocation on heap at runtime (interface{}) whereas BuntDB is statically typed.

The above two differences are what makes Kiwi unique and suitable to use on many occasions. Due to the aforementioned reasons, Kiwi can support typed values and not everything is just another "string".

There are places where we could improve more. Some performance issues also lie in the implementation of values. For example, when updating a string, not returning the updated string avoids an extra allocation.

Contributing

We are always open for contributions. If you find any feature missing, or just want to report a bug, feel free to open an issue and/or submit a pull request regarding the same.

For more information on contribution, check out our docs.

Contact

If you have a query regarding the product or just want to say hello then feel free to visit chat.sdslabs.co or drop a mail at [email protected]


Made by SDSLabs

Comments
  • Add zhash value type

    Add zhash value type

    It would be great to have a "zhash" in standard set of values. This would be similar to zset (or scored set) with the difference that a zhash will have a value along with the score.

    A zset can be thought of as:

    | Key | Score | |-----------|-------| | key_one | 10 | | key_two | 20 | | key_three | 30 |

    whereas a zhash will be:

    | Key | Score | Value | |-----------|-------|-----------| | key_one | 10 | val_one | | key_two | 20 | val_two | | key_three | 30 | val_three |

    For documentation on how to add a new value type, see: https://kiwi.sdslabs.co/docs/add-new-value-type.html

    enhancement 
    opened by vrongmeal 6
  • Wrappers for other languages

    Wrappers for other languages

    Is your feature request related to a problem? Please describe. This doesn't have support for other languages.

    Describe the solution you'd like wrappers to languages like C++, python.

    opened by VedantParanjape 4
  • Add benchmarking with other kv stores.

    Add benchmarking with other kv stores.

    Compare performance and add benchmarking stats with other similar KV stores like BuntDB.

    This would help in better analyzing on how to improve performance when Doing actions.

    performance 
    opened by vrongmeal 1
  • Improve performance of the store.

    Improve performance of the store.

    Summary of progress (commits accordingly)

    Cleanup locking-unlocking of mutex (4319279)

    Most of the functions, including Do, locked mutex twice for the store instead of just once. Changes have been benchmarked and here are results on my (author of this commit) computer:

    Previous benchmarks:

    BenchmarkUpdate-8        3082083          372 ns/op
    BenchmarkGet-8           3884070          307 ns/op
    

    New benchmarks

    BenchmarkUpdate-8        3547335          335 ns/op
    BenchmarkGet-8           4487338          264 ns/op
    

    To run benchmarks, use go test -bench=. ./stdkiwi. Benchmarking is done for "str" type of value.

    Remove defer from all the methods (6a4e790)

    Defer is useful yet very expensive when it comes to performance. On looking at benchmarks, it's quite logical to remove defer statements and writing a little "dirty" code since the difference is significant.

    Old benchmarks:

    BenchmarkUpdate-8        3547335          335 ns/op
    BenchmarkGet-8           4487338          264 ns/op
    

    New benchmarks:

    BenchmarkUpdate-8        3897735          307 ns/op
    BenchmarkGet-8           4881136          242 ns/op
    

    Cache do-map to avoid unnecessary allocations (22954f6)

    Each time an action is executed, space is allocated for the do map. When a key is added or updated, the map is cached for the value so while executing the action no heap allocation takes place.

    Old Benchmarks

    BenchmarkUpdate-8        3897735          307 ns/op         336 B/op        7 allocs/op
    BenchmarkGet-8           4881136          242 ns/op         288 B/op        4 allocs/op
    

    New Benchmarks

    BenchmarkUpdate-8       10086518          115 ns/op          48 B/op        3 allocs/op
    BenchmarkGet-8          21886411          53.4 ns/op          0 B/op        0 allocs/op
    

    Final comparison with BuntDB

    ❯ go test -bench=. -test.benchmem ./benchmark
    goos: darwin
    goarch: amd64
    pkg: github.com/sdslabs/kiwi/benchmark
    BenchmarkBuntDB_Update-8        11777931                96.6 ns/op            48 B/op          1 allocs/op
    BenchmarkBuntDB_View-8          23310963                47.1 ns/op            48 B/op          1 allocs/op
    BenchmarkKiwi_Update-8          10356004               115 ns/op              48 B/op          3 allocs/op
    BenchmarkKiwi_Get-8             21910110                53.2 ns/op             0 B/op          0 allocs/op
    PASS
    ok      github.com/sdslabs/kiwi/benchmark       6.216s
    

    Resolves #14

    opened by vrongmeal 0
  • JSON conversion

    JSON conversion

    ToJSON() converts the value into a raw byte array and returns the rawmessage. FromJSON() takes the rawmessage as an argument and returns the unmarshalled value.

    opened by cypherean 0
  • export fatal err: Unlock of unlocked RWMutex

    export fatal err: Unlock of unlocked RWMutex

    This function fails on store.Export()

    func MakeStorage(){
    	store := stdkiwi.NewStore()
    	for i := 0; i < 10; i++ {
    		res, err := http.Get("http://whatthecommit.com/index.txt")
    		if err != nil {
    			log.Fatal()
    			return
    		}
    		if err := store.AddKey(strconv.Itoa(i), "str"); err != nil {
    			panic(err)
    		}
    		comm := store.Str(strconv.Itoa(i))
    		msg, err := ioutil.ReadAll(res.Body)
    		if err != nil {
    			log.Fatal(err)
    			return
    		}
    		err = comm.Update(string(msg))
    		if err != nil {
    			log.Fatal(err)
    			return
    		}
    		err = res.Body.Close()
    		if err != nil {
    			log.Fatal(err)
    			return
    		}
    		log.Println("written", i, "to file")
    	}
    	js, err :=store.Export()
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    	err = ioutil.WriteFile("commits.json", js, os.ModePerm)
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    }
    
    fatal error: sync: Unlock of unlocked RWMutex
    
    goroutine 1 [running]:
    runtime.throw(0x3e9b00, 0x20)
    	C:/Go/src/runtime/panic.go:1116 +0x79 fp=0xc0000c9ca8 sp=0xc0000c9c78 pc=0x138a19
    sync.throw(0x3e9b00, 0x20)
    	C:/Go/src/runtime/panic.go:1102 +0x3c fp=0xc0000c9cc8 sp=0xc0000c9ca8 pc=0x16521c
    sync.(*RWMutex).Unlock(0xc00020c080)
    	C:/Go/src/sync/rwmutex.go:129 +0xc6 fp=0xc0000c9d00 sp=0xc0000c9cc8 pc=0x184006
    github.com/sdslabs/kiwi.(*Store).Export(0xc0000985e0, 0x3, 0x3, 0x3dffd9, 0x6, 0xc000089280)
    	C:/Users/poly/go/pkg/mod/github.com/sdslabs/[email protected]/store.go:279 +0x23b fp=0xc0000c9e70 sp=0xc0000c9d00 pc=0x1fe8db
    github.com/po1yb1ank/gocommit/commitstorage.MakeStorage()
    	C:/Users/poly/wsp/testt/gocommit/commitstorage/storage.go:41 +0x56a fp=0xc0000c9f78 sp=0xc0000c9e70 pc=0x3666aa
    main.main()
    	C:/Users/poly/wsp/testt/gocommit/commit.go:27 +0x27 fp=0xc0000c9f88 sp=0xc0000c9f78 pc=0x366847
    runtime.main()
    	C:/Go/src/runtime/proc.go:204 +0x209 fp=0xc0000c9fe0 sp=0xc0000c9f88 pc=0x13b1c9
    runtime.goexit()
    	C:/Go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc0000c9fe8 sp=0xc0000c9fe0 pc=0x16a3a1
    
    goroutine 5 [IO wait]:
    internal/poll.runtime_pollWait(0x20ce113a708, 0x72, 0x439260)
    	C:/Go/src/runtime/netpoll.go:222 +0x65
    internal/poll.(*pollDesc).wait(0xc0000cf0b8, 0x72, 0x573300, 0x0, 0x0)
    	C:/Go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
    internal/poll.execIO(0xc0000cef18, 0x3f82a0, 0xc00003ba01, 0x1be18d, 0x602fa544)
    	C:/Go/src/internal/poll/fd_windows.go:175 +0x105
    internal/poll.(*FD).Read(0xc0000cef00, 0xc000044000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
    	C:/Go/src/internal/poll/fd_windows.go:441 +0x2ff
    net.(*netFD).Read(0xc0000cef00, 0xc000044000, 0x1000, 0x1000, 0x13b665, 0xc00003bb58, 0x1627e0)
    	C:/Go/src/net/fd_posix.go:55 +0x56
    net.(*conn).Read(0xc000006010, 0xc000044000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
    	C:/Go/src/net/net.go:182 +0x95
    net/http.(*persistConn).Read(0xc0000de6c0, 0xc000044000, 0x1000, 0x1000, 0xc000180000, 0xc00003bc58, 0x105ab5)
    	C:/Go/src/net/http/transport.go:1887 +0x7c
    bufio.(*Reader).fill(0xc00003e060)
    	C:/Go/src/bufio/bufio.go:101 +0x10a
    bufio.(*Reader).Peek(0xc00003e060, 0x1, 0x0, 0x0, 0x1, 0x0, 0xc000123920)
    	C:/Go/src/bufio/bufio.go:139 +0x56
    net/http.(*persistConn).readLoop(0xc0000de6c0)
    	C:/Go/src/net/http/transport.go:2040 +0x1ba
    created by net/http.(*Transport).dialConn
    	C:/Go/src/net/http/transport.go:1708 +0xcd7
    
    goroutine 6 [select]:
    net/http.(*persistConn).writeLoop(0xc0000de6c0)
    	C:/Go/src/net/http/transport.go:2340 +0x134
    created by net/http.(*Transport).dialConn
    	C:/Go/src/net/http/transport.go:1709 +0xcfc
    

    • go 1.15
    • github.com/sdslabs/kiwi v1.1.0
    opened by po1yb1ank 1
  • Add Time to Live for values

    Add Time to Live for values

    This requires a little thought on how it should be implemented but the basic idea is after d duration a key should have empty value.

    This might require the following method in the Value interface:

      // IsEmpty tells if the value is empty or not.
      IsEmpty() error
    

    The store can then implement a method, called, IsClean:

    // IsEmpty tells if the value associated with the key is empty or not.
    func (s *Store) IsEmpty(key string) (bool, error)
    
    // SetTTL sets the time to live for value.
    //
    // Calls `UpdateKey` after "d" duration, which by default sets the empty value.
    func (s *Store) SetTTL(key string, d time.Duration) error
    

    A new SchemaTTL can be added to define schema with TTL. Changes are made this way such that this version remains compatible with the previous version as well.

    type SchemaTTL map[string]ValueTTL
    
    type ValueTTL struct {
      TTL  time.Duration
      Type ValueType
    }
    
    // Correspondingly a new method called `NewStoreWithSchemaTTL`
    func NewStoreWithSchemaTTL(schema SchemaTTL) (*Store, error)
    
    enhancement discussion 
    opened by vrongmeal 0
Releases(v1.1.0)
Owner
SDSLabs
Think. Build. Ship
SDSLabs
Membin is an in-memory database that can be stored on disk. Data model smiliar to key-value but values store as JSON byte array.

Membin Docs | Contributing | License What is Membin? The Membin database system is in-memory database smiliar to key-value databases, target to effici

Membin 3 Jun 3, 2021
Distributed cache and in-memory key/value data store.

Distributed cache and in-memory key/value data store. It can be used both as an embedded Go library and as a language-independent service.

Burak Sezer 2.3k Sep 21, 2022
A rest-api that works with golang as an in-memory key value store

Rest API Service in GOLANG A rest-api that works with golang as an in-memory key value store Usage Run command below in terminal in project directory.

sercan aydın 0 Dec 6, 2021
Nipo is a powerful, fast, multi-thread, clustered and in-memory key-value database, with ability to configure token and acl on commands and key-regexes written by GO

Welcome to NIPO Nipo is a powerful, fast, multi-thread, clustered and in-memory key-value database, with ability to configure token and acl on command

Morteza Bashsiz 16 Jun 13, 2022
BuntDB is an embeddable, in-memory key/value database for Go with custom indexing and geospatial support

BuntDB is a low-level, in-memory, key/value store in pure Go. It persists to disk, is ACID compliant, and uses locking for multiple readers and a sing

Josh Baker 3.9k Sep 21, 2022
🔑A high performance Key/Value store written in Go with a predictable read/write performance and high throughput. Uses a Bitcask on-disk layout (LSM+WAL) similar to Riak.

bitcask A high performance Key/Value store written in Go with a predictable read/write performance and high throughput. Uses a Bitcask on-disk layout

James Mills 9 Aug 24, 2022
A disk-backed key-value store.

What is diskv? Diskv (disk-vee) is a simple, persistent key-value store written in the Go language. It starts with an incredibly simple API for storin

Peter Bourgon 1.2k Sep 23, 2022
A simple, fast, embeddable, persistent key/value store written in pure Go. It supports fully serializable transactions and many data structures such as list, set, sorted set.

NutsDB English | 简体中文 NutsDB is a simple, fast, embeddable and persistent key/value store written in pure Go. It supports fully serializable transacti

徐佳军 2.5k Sep 21, 2022
Embedded key-value store for read-heavy workloads written in Go

Pogreb Pogreb is an embedded key-value store for read-heavy workloads written in Go. Key characteristics 100% Go. Optimized for fast random lookups an

Artem Krylysov 936 Sep 18, 2022
Fast and simple key/value store written using Go's standard library

Table of Contents Description Usage Cookbook Disadvantages Motivation Benchmarks Test 1 Test 4 Description Package pudge is a fast and simple key/valu

Vadim Kulibaba 330 Sep 22, 2022
Low-level key/value store in pure Go.

Description Package slowpoke is a simple key/value store written using Go's standard library only. Keys are stored in memory (with persistence), value

Vadim Kulibaba 99 Mar 13, 2022
Key-value store for temporary items :memo:

Tempdb TempDB is Redis-backed temporary key-value store for Go. Useful for storing temporary data such as login codes, authentication tokens, and temp

Rafael Jesus 16 Jan 23, 2022
A distributed key-value store. On Disk. Able to grow or shrink without service interruption.

Vasto A distributed high-performance key-value store. On Disk. Eventual consistent. HA. Able to grow or shrink without service interruption. Vasto sca

Chris Lu 239 Aug 31, 2022
Distributed reliable key-value store for the most critical data of a distributed system

etcd Note: The master branch may be in an unstable or even broken state during development. Please use releases instead of the master branch in order

etcd-io 41.3k Sep 22, 2022
a key-value store with multiple backends including leveldb, badgerdb, postgresql

Overview goukv is an abstraction layer for golang based key-value stores, it is easy to add any backend provider. Available Providers badgerdb: Badger

Mohammed Al Ashaal 52 Jun 7, 2022
A simple Git Notes Key Value store

Gino Keva - Git Notes Key Values Gino Keva works as a simple Key Value store built on top of Git Notes, using an event sourcing architecture. Events a

Philips Software 24 Aug 14, 2022
A distributed key value store in under 1000 lines. Used in production at comma.ai

minikeyvalue Fed up with the complexity of distributed filesystems? minikeyvalue is a ~1000 line distributed key value store, with support for replica

George Hotz 2.4k Sep 25, 2022
Simple key value database that use json files to store the database

KValDB Simple key value database that use json files to store the database, the key and the respective value. This simple database have two gRPC metho

Francisco Santos 0 Nov 13, 2021
Eagle - Eagle is a fast and strongly encrypted key-value store written in pure Golang.

EagleDB EagleDB is a fast and simple key-value store written in Golang. It has been designed for handling an exaggerated read/write workload, which su

null 7 Aug 20, 2022