From fb5406f2d512ae15346b84d9c929d7180c1b5d63 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Sat, 17 Dec 2022 16:34:01 +0530 Subject: [PATCH] chore: cosmetic fixes --- .gitignore | 3 + Makefile | 40 +++++------ README.md | 122 ++++++++++++++++++++++++++++++++-- TODO.md | 1 - _docs/logo.png | Bin 0 -> 28244 bytes cmd/server/config.sample.toml | 7 +- examples/main.go | 68 +++++++++++++------ pkg/barrel/barrel_test.go | 11 +++ pkg/barrel/benchmark_test.go | 92 +++++++++++++++++++++++++ 9 files changed, 292 insertions(+), 52 deletions(-) create mode 100644 _docs/logo.png create mode 100644 pkg/barrel/benchmark_test.go diff --git a/.gitignore b/.gitignore index d4c314e..ea5c017 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ barrel*.lock barrel*.db barrel.hints config.toml +bin/ +data/ +coverage.txt diff --git a/Makefile b/Makefile index b768f51..a5f05ec 100644 --- a/Makefile +++ b/Makefile @@ -1,30 +1,30 @@ +APP-BIN := ./bin/barreldb.bin + LAST_COMMIT := $(shell git rev-parse --short HEAD) LAST_COMMIT_DATE := $(shell git show -s --format=%ci ${LAST_COMMIT}) -VERSION := $(shell git describe --abbrev=1) -BUILDSTR := ${VERSION} (build "\\\#"${LAST_COMMIT} $(shell date '+%Y-%m-%d %H:%M:%S')) - -BIN := ./bin/barreldb.bin +VERSION := $(shell git describe --tags) +BUILDSTR := ${VERSION} (Commit: ${LAST_COMMIT_DATE} (${LAST_COMMIT}), Build: $(shell date +"%Y-%m-%d% %H:%M:%S %z")) .PHONY: build -build: $(BIN) - -$(BIN): $(shell find . -type f -name "*.go") - CGO_ENABLED=0 go build -o ${BIN} -ldflags="-s -w -X 'main.buildString=${BUILDSTR}'" ./cmd/server/*.go +build: ## Build binary. + go build -o ${APP-BIN} -ldflags="-X 'main.buildString=${BUILDSTR}'" ./cmd/server/ .PHONY: run -run: - CGO_ENABLED=0 go run -ldflags="-s -w -X 'main.buildString=${BUILDSTR}'" ./cmd/server --config=cmd/server/config.toml +run: ## Run binary. + ./${APP-BIN} --config=./cmd/server/config.sample.toml + +.PHONY: clean +clean: ## Remove temporary files and the `bin` folder. + rm -rf bin + rm -rf data/barrel_* + +.PHONY: fresh +fresh: build run .PHONY: test test: - go test ./... - -# Use goreleaser to do a dry run producing local builds. -.PHONY: release-dry -release-dry: - goreleaser --parallelism 1 --rm-dist --snapshot --skip-validate --skip-publish + go test -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt ./... -# Use goreleaser to build production releases and publish them. -.PHONY: release -release: - goreleaser --parallelism 1 --rm-dist --skip-validate +.PHONY: bench +bench: + go test -bench=. -benchmem ./pkg/barrel/... diff --git a/README.md b/README.md index 889fe75..7fec97d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,122 @@ +

+logo +

+ # barreldb -A disk based KV store (based on Bitcask implementation) -Bitcask is a log-structured hash table (LSHT) database that is designed for use as a storage engine for key-value data. It uses a log-structured approach to data management, which means that data is written to a log file and is periodically merged with other log files in the database to create new, larger log files. +_A disk based key-value store based on [Bitcask](https://en.wikipedia.org/wiki/Bitcask)_. + +--- + +BarrelDB is a Golang implementation of [Bitcask by Riak](https://riak.com/assets/bitcask-intro.pdf) paper and aims to closely follow the spec. + +Bitcask is based on a log-structured hash table to store key-value data on disk. It opens a "datafile" (term used for a Bitcask DB file) in an _append-only_ mode and all the writes are sequentially written to this file. Additionally, it also updates an in-memory hash table which maps the key with the offset of the record in the file. This clever yet simple design decision makes it possible to retrieve records from the disk using a _single_ disk seek. + +### Benefits of this approach + +- Low Latency: Write queries are handled with a single O(1) disk seek. Keys lookup happen in memory using a hash table lookup. This makes it possible to achieve low latency even with a lot of keys/values in the database. Bitcask also relies on the filesystem read-ahead cache for a faster reads. +- High Throughput: Since the file is opened in "append only" mode, it can handle large volumes of write operations with ease. +- Predictable performance. The DB has a consistent performance even with growing number of records. This can be seen in benchmarks as well. +- Crash friendly. Bitcask commits each record to the disk and also generates a "hints" file which makes it easy to recover in case of a crash. +- Elegant design. Bitcask achieves a lot just by keeping the architecture simple and relying on filesystem primitives for complex scenarios (backup/recovery, cache etc). +- Ability to handle datasets larger than RAM. + +### Limitations + +- The main limitation is that all the keys must fit in RAM since they're held inside as an in-memory hash table. A potential workaround for this could be to shard the keys in multiple buckets. Incoming records can be hashed into different buckets based on the key. A shard based approach allows each bucket to have limited RAM usage. + +## Internals + +You can refer to https://mrkaran.dev/ for a post which goes over the internals of Bitcask and also explains how BarrelDB works. + +## Usage + +### Library + + +```go +import ( + "github.com/mr-karan/barreldb/pkg/barrel" +) + +barrel, _ := barrel.Init(barrel.WithDir("data/")) + +// Set a key. +barrel.Put("hello", []byte("world")) + +// Fetch the key. +v, _ := barrel.Get("hello") + +// Delete a key. +barrel.Delete("hello") + +// Set with expiry. +barrel.PutEx("hello", []byte("world"), time.Second * 3) +``` + +For a complete example, visit [examples](./examples/main.go). + +### Redis Client + +`barreldb` implements the API over a simple Redis-compatible server (`barreldb`): + +``` +127.0.0.1:6379> set hello world +OK +127.0.0.1:6379> get hello +"world" +127.0.0.1:6379> set goodbye world 10s +OK +127.0.0.1:6379> get goodbye +"world" +127.0.0.1:6379> get goodbye +ERR: invalid key: key is already expired +``` + +## Benchmarks + +Using `make bench`: + +``` +go test -bench=. -benchmem ./pkg/barrel/... +HELLO +goos: linux +goarch: amd64 +pkg: github.com/mr-karan/barreldb/pkg/barrel +cpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz +BenchmarkPut/DisableSync-8 385432 3712 ns/op 1103.48 MB/s 88 B/op 4 allocs/op +BenchmarkPut/AlwaysSync-8 222 5510563 ns/op 0.74 MB/s 115 B/op 4 allocs/op +BenchmarkGet-8 840627 1304 ns/op 3142.20 MB/s 4976 B/op 5 allocs/op +PASS +ok github.com/mr-karan/barreldb/pkg/barrel 10.751s +``` + +Using `redis-benchmark`: + +``` +$ redis-benchmark -p 6379 -t set -n 10000 -r 100000000 +Summary: + throughput summary: 140845.06 requests per second + latency summary (msec): + avg min p50 p95 p99 max + 0.196 0.016 0.175 0.255 1.031 2.455 + +$ redis-benchmark -p 6379 -t set -n 200000 -r 100000000 +Summary: + throughput summary: 143678.17 requests per second + latency summary (msec): + avg min p50 p95 p99 max + 0.184 0.016 0.183 0.223 0.455 2.183 + +$ redis-benchmark -p 6379 -t get -n 100000 -r 100000000 +Summary: + throughput summary: 170068.03 requests per second + latency summary (msec): + avg min p50 p95 p99 max + 0.153 0.040 0.143 0.199 0.367 1.447 +``` -The process of merging log files in Bitcask is called compaction. When compaction occurs, the Bitcask database will select two or more log files to merge, and it will create a new log file that contains the combined data from the selected log files. This new log file will be used in place of the old log files, and the old log files will be deleted to free up space. +## References -The advantage of this approach is that it allows Bitcask to store data efficiently and to perform well even when dealing with large amounts of data. By periodically merging log files, Bitcask can avoid the need to split or resize its data files, which can be slow and expensive operations. It also allows Bitcask to perform efficient lookups of data by key, since the keys are stored in a hash table that is stored in memory. \ No newline at end of file +- [Bitcask paper](https://riak.com/assets/bitcask-intro.pdf) +- [Highscalability article on Bitcask](http://highscalability.com/blog/2011/1/10/riaks-bitcask-a-log-structured-hash-table-for-fast-keyvalue.html) diff --git a/TODO.md b/TODO.md index 837a581..d8fe3eb 100644 --- a/TODO.md +++ b/TODO.md @@ -50,4 +50,3 @@ Availability with N+1 node with raft - [ ] Merge - [ ] Hints file - [ ] Rotate size - diff --git a/_docs/logo.png b/_docs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5ec07d63c5684f270743f98c708b0bdbe3e3d38 GIT binary patch literal 28244 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 4kb. + b.SetBytes(int64(4096)) + b.ReportAllocs() + + var ( + key = "hello" + val = []byte(strings.Repeat(" ", 4096)) + ) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + err = brl.Put(key, val) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() + }) + brl.Shutdown() + } +} + +func BenchmarkGet(b *testing.B) { + // Create a temp directory for running tests. + tmpDir, err := os.MkdirTemp("", "barreldb") + if err != nil { + b.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + brl, err := barrel.Init(barrel.WithDir(tmpDir)) + if err != nil { + b.Fatal(err) + } + defer brl.Shutdown() + + // Size of each value -> 4kb. + b.SetBytes(int64(4096)) + b.ReportAllocs() + + var ( + key = "hello" + val = []byte(strings.Repeat(" ", 4096)) + ) + + // Put dummy keys. + for i := 0; i < b.N; i++ { + err = brl.Put(key, val) + if err != nil { + b.Fatal(err) + } + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := brl.Get(key) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() +}