litekv

package module
v0.0.4-alpha Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2023 License: MIT Imports: 6 Imported by: 0

README

LiteKV

LiteKV is a simple, lightweight, and efficient in-memory key-value store written in Go. It supports basic operations like reading, writing, deleting, and updating key-value pairs, as well as advanced features like exporting and importing index, rebuilding index, and compacting the store. LiteKV uses an append-only data structure, which provides better write performance and data durability.

Technical Aspects

Append-only Storage

LiteKV stores data in an append-only manner, which means that new key-value pairs are always added to the end of the store, even when updating and deleting existing keys. This approach provides better write performance and ensures that data remains intact even in the case of a crash or failure. Updating works because on

Compaction

As the store grows, it may accumulate duplicate or deleted entries, which can affect performance and memory usage. LiteKV provides a compaction feature that removes duplicates and deleted key-value pairs, making the store more efficient.

To compact the store, simply call the Compact method:

kvs.Compact()
Binary Storage Format

LiteKV uses a custom binary format to store key-value pairs. Each entry consists of a checksum, record type, key length, value length, key, and value. The checksum is calculated using the CRC32 algorithm and helps in detecting corruption or inconsistencies in the data. The record type indicates whether the entry is a normal key-value pair or a deleted one.

LiteKV uses byte slices as the underlying data format for storing key-value pairs. Byte slices offer versatility and flexibility, making it easier to perform various operations such as saving data to disk or using POSIX shared memory.

By using byte slices, LiteKV allows you to seamlessly integrate the stored data with various storage solutions or inter-process communication methods, enhancing the overall usability and adaptability of the library in different use cases.

Getting Started

To use LiteKV, first import the library:

import (
    "github.com/tillknuesting/litekv"
)

Then, create a new instance of KeyValueStore:

kvs := &litekv.KeyValueStore{}

You can now perform basic operations on the store:

kvs.Write([]byte("foo"), []byte("bar"))
value, err := kvs.Read([]byte("foo"))
kvs.Delete([]byte("foo"))

Running Tests

To run the tests for LiteKV, navigate to the project directory and execute:

go test ./...

Running Benchmarks

To run the benchmarks for LiteKV, navigate to the project directory and execute:

go test -bench=. ./...

Fuzz Testing

Fuzz testing is a powerful technique to uncover potential issues in your code by providing a wide range of random inputs.

To run fuzz tests for LiteKV, navigate to the project directory and execute:

go test -fuzz .

Documentation

Index

Constants

View Source
const (
	// ErrorKeyDeleted is returned when trying to read a key that has been deleted.
	ErrorKeyDeleted = Error("key is deleted")

	// ErrorKeyNotFound is returned when trying to read a key that does not exist.
	ErrorKeyNotFound = Error("key not found")

	// ErrorChecksumMismatch is returned when a record's calculated checksum
	// does not match its stored checksum, indicating Data corruption.
	ErrorChecksumMismatch = Error("checksum mismatch")
)

Define constants for common error scenarios in KeyValueStore operations.

Variables

This section is empty.

Functions

This section is empty.

Types

type Error

type Error string

Error is a custom error type that wraps a string. It is used for providing specific error messages related to the KeyValueStore operations.

func (Error) Error

func (e Error) Error() string

Error method implements the error interface for the custom Error type. It returns the string representation of the error message.

type KeyValueStore

type KeyValueStore struct {
	sync.RWMutex                  // Embed the RWMutex to ensure thread safety during concurrent read and write operations.
	Data         []byte           // A byte slice that holds the serialized records.
	Index        map[string]int64 // A map that maps keys (as strings) to their position in the Data byte slice.
}

KeyValueStore is a simple key-value store implementation. It utilizes a byte slice (Data) to store serialized records and a map (Index) to map keys to their position in the Data byte slice. The KeyValueStore struct also embeds the sync.RWMutex to ensure thread safety during concurrent read and write operations.

func (*KeyValueStore) Compact

func (kvs *KeyValueStore) Compact()

Compact iterates through the KeyValueStore's Data byte slice, identifies the latest records for each key, and rebuilds the Data slice and Index, effectively removing any deleted or outdated records. This method is useful for reducing the storage size and improving the performance of the KeyValueStore.

func (*KeyValueStore) Delete

func (kvs *KeyValueStore) Delete(key []byte)

Delete takes a key in the form of a byte slice and marks the associated record as deleted in the KeyValueStore instance. It achieves this by creating a new Record with the RecordType set to RecordTypeDeleted and appending it to the Data byte slice. It also updates the Index map to map the key to the position of the new deleted record.

func (*KeyValueStore) LoadIndex

func (kvs *KeyValueStore) LoadIndex(data []byte) error

LoadIndex deserializes a byte slice containing a serialized Index (a map of keys to their position in the Data byte slice) using the gob package, and restores the deserialized Index to the KeyValueStore. This method can be used to load a previously saved Index from disk or another storage medium.

func (*KeyValueStore) PrintAllKeyValuePairs

func (kvs *KeyValueStore) PrintAllKeyValuePairs()

PrintAllKeyValuePairs iterates through the KeyValueStore's Data byte slice, deserializes each record, and prints the key, value, and record type for each record. This method is useful for debugging and getting an overview of the KeyValueStore's contents.

func (*KeyValueStore) Read

func (kvs *KeyValueStore) Read(key []byte) ([]byte, error)

Read takes a key in the form of a byte slice and retrieves the associated value from the KeyValueStore instance. It returns the value as a byte slice, or an error if the key is not found, deleted, or there's a checksum mismatch.

func (*KeyValueStore) RebuildIndex

func (kvs *KeyValueStore) RebuildIndex()

RebuildIndex iterates through the KeyValueStore's Data byte slice, deserializes each record, and rebuilds the Index by calculating the position of each key in the Data slice. This method is useful when the Index has been lost or corrupted and needs to be reconstructed.

func (*KeyValueStore) SaveIndex

func (kvs *KeyValueStore) SaveIndex() ([]byte, error)

SaveIndex serializes the KeyValueStore's Index (a map of keys to their position in the Data byte slice) using the gob package, and returns the serialized Index as a byte slice. This method can be used to persist the Index to disk or another storage medium, for later restoration.

func (*KeyValueStore) Write

func (kvs *KeyValueStore) Write(key, value []byte)

Write takes a key and a value, both in byte slices, and stores them in the KeyValueStore instance. This method is responsible for creating a new Record with the given key-value pair and appending it to the Data byte slice. It also updates the Index map to map the key to the position of the new record.

type Record

type Record struct {
	Crc         uint32     // 4 bytes: The CRC-32 checksum used to ensure the integrity of the stored record.
	Type        RecordType // 1 byte: The record type, which can be either RecordTypeNormal or RecordTypeDeleted.
	Key         []byte     // Variable length: The key of the key-value pair.
	Value       []byte     // Variable length: The value of the key-value pair.
	KeyLength   uint32     // 4 bytes: The length of the key.
	ValueLength uint32     // 4 bytes: The length of the value.
}

Record represents a single key-value pair along with its metadata in the KeyValueStore. It contains fields for the CRC checksum, record type (normal or deleted), key, value, key length, and value length.

type RecordType

type RecordType uint8

RecordType is a custom uint8 type that represents the type of a record. It is used to differentiate between normal and deleted records in the KeyValueStore.

const (
	// RecordTypeNormal represents a normal record, which contains a key-value pair.
	RecordTypeNormal RecordType = iota

	// RecordTypeDeleted represents a deleted record, which is marked as deleted but not removed from the Data slice.
	RecordTypeDeleted
)

Define constants for the different record types.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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