Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinitto committed Nov 9, 2022
1 parent 0535f9a commit 9df039a
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 13 deletions.
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [0.0.7] - 2022-11-9

### Added

- More thorough documentation for the Store

### Changed

### Fixed

## [0.0.6] - 2022-11-9

### Added

### Changed

- Optimized the Compact operation.
- Removed unnecessary internal type conversions e.g. to `internal.entries.Index`.
- Got rid of message passing in iterating over the index blocks
- Optimized the Compact operation.
- Removed unnecessary internal type conversions e.g. to `internal.entries.Index`.
- Got rid of message passing in iterating over the index blocks

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A very simple and fast key-value pure-go store but persisting data to disk, with

This is the pure-golang version of the original [scdb](https://github.com/sopherapps/scdb)

**scdb may not be production-ready yet. It works, quite well but it requires more vigorous testing.**
**scdb may not be production-ready yet. It works, quite well but it requires more rigorous testing.**

## Purpose

Expand Down
55 changes: 46 additions & 9 deletions scdb/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import (
"time"
)

// DefaultDbFile is the default name of the database file that contains all the key-value pairs
const DefaultDbFile string = "dump.scdb"
// defaultDbFile is the default name of the database file that contains all the key-value pairs
const defaultDbFile string = "dump.scdb"

var ZeroU64 = internal.Uint64ToByteArray(0)
var zeroU64 = internal.Uint64ToByteArray(0)

// Store is the public interface to the key-value store
// that allows us to do operations like Set, Get, Delete, Clear and Compact
// Store is a key-value store that persists key-value pairs to disk
//
// Store behaves like a HashMap that saves keys and value as byte arrays
// on disk. It allows for specifying how long each key-value pair should be
// kept for i.e. the time-to-live in seconds. If None is provided, they last indefinitely.
type Store struct {
bufferPool *buffers.BufferPool
header *entries.DbFileHeader
Expand All @@ -27,14 +30,48 @@ type Store struct {
isClosed bool
}

// New creates a new Store at the given path, with the given `compactionInterval`
// New creates a new Store at the given path
// The Store has a number of configurations that are passed into this New function
//
// - `storePath` - required:
// The path to a directory where scdb should store its data
//
// - `maxKeys` - default: 1 million:
// The maximum number of key-value pairs to store in store
//
// - `redundantBlocks` - default: 1:
// The store has an index to hold all the keys. This index is split
// into a fixed number of blocks basing on the virtual memory page size
// and the total number of keys to be held i.e. `max_keys`.
// Sometimes, there may be hash collision errors as the store's
// current stored keys approach `max_keys`. The closer it gets, the
// more it becomes likely see those errors. Adding redundant blocks
// helps mitigate this. Just be careful to not add too many (i.e. more than 2)
// since the higher the number of these blocks, the slower the store becomes.
//
// - `poolCapacity` - default: 5:
// The number of buffers to hold in memory as cache's for the store. Each buffer
// has the size equal to the virtual memory's page size, usually 4096 bytes.
// Increasing this number will speed this store up but of course, the machine
// has a limited RAM. When this number increases to a value that clogs the RAM, performance
// suddenly degrades, and keeps getting worse from there on.
//
// - `compactionInterval` - default 3600s (1 hour):
// The interval at which the store is compacted to remove dangling
// keys. Dangling keys result from either getting expired or being deleted.
// When a `delete` operation is done, the actual key-value pair
// is just marked as `deleted` but is not removed.
// Something similar happens when a key-value is updated.
// A new key-value pair is created and the old one is left unindexed.
// Compaction is important because it reclaims this space and reduces the size
// of the database file.
func New(path string, maxKeys *uint64, redundantBlocks *uint16, poolCapacity *uint64, compactionInterval *uint32) (*Store, error) {
err := os.MkdirAll(path, 0755)
if err != nil {
return nil, err
}

dbFilePath := filepath.Join(path, DefaultDbFile)
dbFilePath := filepath.Join(path, defaultDbFile)
bufferPool, err := buffers.NewBufferPool(poolCapacity, dbFilePath, maxKeys, redundantBlocks, nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -133,7 +170,7 @@ func (s *Store) Get(k []byte) ([]byte, error) {
return nil, err
}

if bytes.Equal(kvOffsetInBytes, ZeroU64) {
if bytes.Equal(kvOffsetInBytes, zeroU64) {
continue
}

Expand Down Expand Up @@ -173,7 +210,7 @@ func (s *Store) Delete(k []byte) error {
return err
}

if bytes.Equal(kvOffsetInBytes, ZeroU64) {
if bytes.Equal(kvOffsetInBytes, zeroU64) {
continue
}

Expand Down
79 changes: 79 additions & 0 deletions scdb/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scdb
import (
"fmt"
"github.com/stretchr/testify/assert"
"log"
"os"
"path"
"runtime"
Expand Down Expand Up @@ -521,6 +522,84 @@ func BenchmarkStore_SetWithTtl(b *testing.B) {

}

func ExampleNew() {
var maxKeys uint64 = 1_000_000
var redundantBlocks uint16 = 1
var poolCapacity uint64 = 10
var compactionInterval uint32 = 1_800

store, err := New(
"testdb",
&maxKeys,
&redundantBlocks,
&poolCapacity,
&compactionInterval)
if err != nil {
log.Fatalf("error opening store: %s", err)
}
defer func() {
_ = store.Close()
}()
}

func ExampleStore_Set() {
store, err := New("testdb", nil, nil, nil, nil)
if err != nil {
log.Fatalf("error opening store: %s", err)
}
defer func() {
_ = store.Close()
}()

err = store.Set([]byte("foo"), []byte("bar"), nil)
if err != nil {
log.Fatalf("error setting key value without ttl: %s", err)
}

ttl := uint64(3_600)
err = store.Set([]byte("fake"), []byte("bear"), &ttl)
if err != nil {
log.Fatalf("error setting key value with ttl: %s", err)
}
}

func ExampleStore_Get() {
store, err := New("testdb", nil, nil, nil, nil)
if err != nil {
log.Fatalf("error opening store: %s", err)
}
defer func() {
_ = store.Close()
}()

err = store.Set([]byte("foo"), []byte("bar"), nil)
if err != nil {
log.Fatalf("error setting key value: %s", err)
}

value, err := store.Get([]byte("foo"))
if err != nil {
log.Fatalf("error getting key: %s", err)
}

fmt.Printf("%s", value)
// Output: bar
}
func ExampleStore_Delete() {
store, err := New("testdb", nil, nil, nil, nil)
if err != nil {
log.Fatalf("error opening store: %s", err)
}
defer func() {
_ = store.Close()
}()

err = store.Delete([]byte("foo"))
if err != nil {
log.Fatalf("error deleting key: %s", err)
}
}

// removeStore is a utility to remove the old store just before a given test is run
func removeStore(t *testing.T, path string) {
err := os.RemoveAll(path)
Expand Down

0 comments on commit 9df039a

Please sign in to comment.