Skip to content

Repository files navigation

FlashDB

A Redis-compatible in-memory key-value store written in Rust. Speaks the RESP protocol so any Redis client works out of the box. Uses a fully lock-free concurrent hash map with epoch-based reclamation — no mutex, no RwLock on the data path.

Performance

Benchmarked on a 6-core machine (Intel i5-11400H, 12 threads) with 100 clients, 1M ops, pipeline size 100.

Metric FlashDB (6 cores) Redis Cluster (6 nodes) vs Cluster
Sequential SET ~15.4M ops/sec ~3.5M ops/sec 4.4x
Pipelined SET ~15.9M ops/sec ~7.9M ops/sec 2.0x
Pipelined GET ~19.6M ops/sec ~8.3M ops/sec 2.4x
Pub/Sub delivery ~25.66M msg/sec ~7.3M msg/sec 3.5x

A single FlashDB node outperforms a 6-node Redis Cluster. Redis is single-threaded per node; FlashDB scales linearly with cores.

Resource Usage

State RSS Memory CPU Usage
Idle (no keys) ~30 MB 0%
Under load ~260 MB ~42% avg
Peak ~280 MB ~65% peak

Resource Comparison (FlashDB vs Redis Cluster during benchmark)

FlashDB (1 node) Redis Cluster (6 nodes)
Idle RSS ~30 MB ~75 MB (total)
Peak RSS ~280 MB ~320 MB (total)
Avg RSS ~260 MB ~240 MB (total)
Peak CPU ~65% peak ~99%
Avg CPU ~42% avg ~29%

FlashDB uses more memory (pre-allocated lock-free hash table slots) but delivers 2–4x the throughput of a 6-node cluster on less CPU. The memory cost is the trade-off for zero-lock, zero-contention data access.

Internal Store Throughput (no TCP overhead)

Operation Throughput
SET (new key) 24.6M ops/sec
SET (update) 29.8M ops/sec
GET 42.3M ops/sec

Quick Start

cargo build --release
./target/release/flash_db

redis-cli -p 8000
127.0.0.1:8000> SET name rana
OK
127.0.0.1:8000> GET name
"rana"
127.0.0.1:8000> SUBSCRIBE news
127.0.0.1:8000> PUBLISH news "hello"
127.0.0.1:8000> BGSAVE
Background saving started

Docker

docker run -p 8000:8000 rana718/flashdb:latest

Key Design Decisions

  • Lock-free CustomMap — custom concurrent hash map with epoch-based reclamation. No locks on read or write path. Values swapped atomically, old values freed after grace period.
  • Thread-per-core — one epoll loop per CPU core, SO_REUSEPORT for kernel-level connection distribution.
  • Zero-copy GET — reads write directly from stored value to TCP buffer. No String clone.
  • Inline SET/GET fast path — hot commands dispatched from raw RESP bytes without building intermediate arrays.
  • Value pooling — reclaimed allocations recycled in thread-local pool, eliminating malloc on update path.
  • Batched writes — all epoll events processed before flushing responses, reducing syscall count.

Persistence

FlashDB uses RDB snapshots — the same model as Redis.

  • On startup — loads flashdb.rdb from the current directory if it exists
  • Every 5 minutes — background save, zero impact on performance
  • On shutdown — saves automatically on SIGTERM or Ctrl+C
  • ManualBGSAVE command triggers an immediate background save

The RDB file is written atomically (temp file → rename) so a crash mid-save never corrupts the existing snapshot.

Supported Commands

Connection

Command Description
PING [msg] Returns PONG or echoes msg
ECHO msg Returns msg as bulk string
INFO Server stats: version, memory, clients, keys
DBSIZE Number of keys in the store
FLUSH Delete all keys
BGSAVE Trigger a background RDB snapshot
TYPE key Returns string, hash, or none

String

Command Description
SET key value [EX s] [PX ms] [NX] [XX] [GET] Set a key with optional TTL and flags
GET key Get a value
GETDEL key Get and delete atomically
GETSET key value Get old value, set new value
GETEX key [EX s | PX ms | PERSIST] Get value and update TTL
SETNX key value Set only if key does not exist
SETEX key seconds value Set key with TTL in seconds
PSETEX key ms value Set key with TTL in milliseconds
MSET key val [key val ...] Set multiple keys atomically
MSETNX key val [key val ...] Set multiple keys only if none exist
MGET key [key ...] Get multiple keys
INCR key Increment integer value by 1
DECR key Decrement integer value by 1
INCRBY key n Increment integer value by N
DECRBY key n Decrement integer value by N
INCRBYFLOAT key n Increment float value by N
APPEND key value Append to string, returns new length
STRLEN key String length in bytes
GETRANGE key start end Substring (supports negative indices)
SETRANGE key offset value Overwrite bytes at offset

Keys

Command Description
DEL key [key ...] Delete one or more keys, returns count deleted
UNLINK key [key ...] Alias for DEL
EXISTS key [key ...] Returns count of keys that exist
TTL key TTL in seconds (-1 = no expiry, -2 = missing)
PTTL key TTL in milliseconds (-1 = no expiry, -2 = missing)
EXPIRE key seconds Set TTL in seconds
PEXPIRE key ms Set TTL in milliseconds
EXPIREAT key unix Set expiry as Unix timestamp (seconds)
PERSIST key Remove TTL, make key persistent
RENAME old new Rename key
RENAMENX old new Rename only if new key does not exist
COPY src dst [REPLACE] Copy key to new key
RANDOMKEY Return a random existing key
KEYS pattern All keys matching glob pattern (*, ?)
SCAN cursor [MATCH pat] [COUNT n] Cursor-based key iteration

Hash

Command Description
HSET key field value [field value ...] Set one or more fields, returns count added
HSETNX key field value Set field only if it does not exist
HGET key field Get field value
HMGET key field [field ...] Get multiple fields
HMSET key field value [...] Set multiple fields (deprecated alias for HSET)
HGETALL key Get all field/value pairs
HDEL key field [field ...] Delete fields, returns count deleted
HEXISTS key field Check if field exists
HLEN key Number of fields
HKEYS key All field names
HVALS key All field values
HINCRBY key field n Increment integer field by N
HINCRBYFLOAT key field n Increment float field by N

Pub/Sub

Command Description
SUBSCRIBE channel [channel ...] Subscribe to one or more channels
UNSUBSCRIBE [channel ...] Unsubscribe from channels (all if none specified)
PSUBSCRIBE pattern [pattern ...] Subscribe to channels matching a glob pattern
PUNSUBSCRIBE [pattern ...] Unsubscribe from patterns (all if none specified)
PUBLISH channel message Publish a message, returns number of receivers
PUBSUB CHANNELS [pattern] List active channels with at least one subscriber
PUBSUB NUMSUB [channel ...] Subscriber count per channel
PUBSUB NUMPAT Total number of pattern subscriptions

Running Tests

cargo test
cargo test -- --quiet
cargo test rdb             # persistence tests only
cargo test pubsub          # pub/sub tests only

Benchmarking

cd bench && go run .             # Full benchmark (KV + Pub/Sub)
cd bench && go run . -m key      # KV only
cd bench && go run . -m pub      # Pub/Sub only
cd bench && go run . -p 6379     # Against Redis for comparison
Flag Default Description
-p 8000 Server port
-m all Mode: all, key, or pub

Configuration

FlashDB is configured via environment variables. All settings have production-ready defaults.

Variable Default Description
FLASHDB_PORT 8000 TCP listening port
FLASHDB_WORKERS 0 (auto) Worker threads (0 = number of CPU cores)
FLASHDB_SHARDS 0 (auto) Hash map shards (0 = workers × 4, power of 2)
FLASHDB_MAX_KEYS 1000000 Expected max keys (sizes the hash table)
FLASHDB_MAX_CLIENTS 10000 Max concurrent connections (rejects above)
FLASHDB_RDB_PATH flashdb.rdb Snapshot file path
FLASHDB_RDB_INTERVAL 300 Auto-save interval in seconds

Examples

# Default (1M keys, auto workers)
./flash_db

# High-capacity (10M keys, custom port)
FLASHDB_PORT=6379 FLASHDB_MAX_KEYS=10000000 ./flash_db

# Minimal memory (100k keys)
FLASHDB_MAX_KEYS=100000 ./flash_db

# Docker with custom settings
docker run -p 6379:6379 \
  -e FLASHDB_PORT=6379 \
  -e FLASHDB_MAX_KEYS=5000000 \
  -e FLASHDB_RDB_INTERVAL=60 \
  -v ./data:/data \
  rana718/flashdb:latest

Dependencies

Crate Purpose
customhash Lock-free concurrent hash map (workspace)
crossbeam-utils CachePadded for false-sharing prevention
foldhash Fast non-cryptographic hashing
mio Non-blocking I/O, epoll wrapper
socket2 SO_REUSEPORT — per-thread kernel accept
memchr SIMD-accelerated byte search (AVX2)
smallvec Stack-allocated small vectors
mimalloc High-performance memory allocator
num_cpus CPU count for thread sizing
libc signalfd, sigwait for graceful shutdown
crossbeam-queue Lock-free MPMC queue for pub/sub delivery

Architecture

See ARCHITECTURE.md for a deep-dive into the lock-free hash map design, EBR algorithm, request lifecycle, and complexity analysis.

About

FlashDB is an in-memory key-value database written in Rust. It is designed to be faster than Redis for key-value operations. The project is currently under active development.

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages