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.

Built on customhash — a sharded, lock-free concurrent hash map with epoch-based reclamation written from scratch. Reads are wait-free, writes are lock-free, and retired values are freed only after all readers have unpinned. Read the full write-up: Custom Concurrent HashMap in Rust

Performance

Peak observed on a 6-core Intel i5-11400H (12 hardware threads), loopback TCP, 100 clients, 1M operations, and a warmed server. Each figure is the best of three complete runs; sustained throughput will vary with CPU scheduling, cache state, key cardinality, and subscriber fan-out.

MetricFlashDB (6 cores)Redis Cluster (6 nodes)vs Cluster
Pipeline-64 SET~14.7M ops/sec~3.5M ops/sec4.2x
Pipeline-100 SET~14.9M ops/sec~7.9M ops/sec1.9x
Pipeline-100 GET~19.3M ops/sec~8.3M ops/sec2.3x
Pub/Sub delivery~25.6M msg/sec~7.3M msg/sec3.5x

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

Resource Usage

MeasurementResult
Idle RSS (no keys)~55 MB
Average RSS under load~215 MB
Peak RSS during a run~235 MB
Average CPU under load~50%
Peak CPU during a run~60%

Resource Comparison (FlashDB vs Redis Cluster during benchmark)

FlashDB (1 node)Redis Cluster (6 nodes)
Idle RSS~55 MB~75 MB (total)
Peak RSS~235 MB~154 MB (total)
Avg RSS~215 MB~126 MB (total)
Peak CPU~60%~96%
Avg CPU~50%~25%

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.

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

CommandDescription
PING [msg]Returns PONG or echoes msg
ECHO msgReturns msg as bulk string
INFOServer stats: version, memory, clients, keys
DBSIZENumber of keys in the store
FLUSHDelete all keys
BGSAVETrigger a background RDB snapshot
TYPE keyReturns string, hash, or none

String

CommandDescription
SET key value [EX s] [PX ms] [NX] [XX] [GET]Set a key with optional TTL and flags
GET keyGet a value
GETDEL keyGet and delete atomically
GETSET key valueGet old value, set new value
GETEX key [EX s | PX ms | PERSIST]Get value and update TTL
SETNX key valueSet only if key does not exist
SETEX key seconds valueSet key with TTL in seconds
PSETEX key ms valueSet 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 keyIncrement integer value by 1
DECR keyDecrement integer value by 1
INCRBY key nIncrement integer value by N
DECRBY key nDecrement integer value by N
INCRBYFLOAT key nIncrement float value by N
APPEND key valueAppend to string, returns new length
STRLEN keyString length in bytes
GETRANGE key start endSubstring (supports negative indices)
SETRANGE key offset valueOverwrite bytes at offset

Keys

CommandDescription
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 keyTTL in seconds (-1 = no expiry, -2 = missing)
PTTL keyTTL in milliseconds (-1 = no expiry, -2 = missing)
EXPIRE key secondsSet TTL in seconds
PEXPIRE key msSet TTL in milliseconds
EXPIREAT key unixSet expiry as Unix timestamp (seconds)
PERSIST keyRemove TTL, make key persistent
RENAME old newRename key
RENAMENX old newRename only if new key does not exist
COPY src dst [REPLACE]Copy key to new key
RANDOMKEYReturn a random existing key
KEYS patternAll keys matching glob pattern (*, ?)
SCAN cursor [MATCH pat] [COUNT n]Cursor-based key iteration

Hash

CommandDescription
HSET key field value [field value ...]Set one or more fields, returns count added
HSETNX key field valueSet field only if it does not exist
HGET key fieldGet field value
HMGET key field [field ...]Get multiple fields
HMSET key field value [...]Set multiple fields (deprecated alias for HSET)
HGETALL keyGet all field/value pairs
HDEL key field [field ...]Delete fields, returns count deleted
HEXISTS key fieldCheck if field exists
HLEN keyNumber of fields
HKEYS keyAll field names
HVALS keyAll field values
HINCRBY key field nIncrement integer field by N
HINCRBYFLOAT key field nIncrement float field by N

Pub/Sub

CommandDescription
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 messagePublish 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 NUMPATTotal 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 onlycd bench && go run . -m pub # Pub/Sub onlycd bench && go run . -p 6379 # Against Redis for comparison
FlagDefaultDescription
-p8000Server port
-mallMode: all, key, or pub

Configuration

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

VariableDefaultDescription
FLASHDB_PORT8000TCP listening port
FLASHDB_WORKERS0 (auto)Worker threads (0 = number of CPU cores)
FLASHDB_SHARDS0 (auto)Hash map shards (0 = workers × 4, power of 2)
FLASHDB_MAX_KEYS1000000Expected max keys (sizes the hash table)
FLASHDB_MAX_CLIENTS10000Max concurrent connections (rejects above)
FLASHDB_RDB_PATHflashdb.rdbSnapshot file path
FLASHDB_RDB_INTERVAL300Auto-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

CratePurpose
customhashLock-free concurrent hash map (workspace)
crossbeam-utilsCachePadded for false-sharing prevention
foldhashFast non-cryptographic hashing
mioNon-blocking I/O, epoll wrapper
socket2SO_REUSEPORT — per-thread kernel accept
memchrSIMD-accelerated byte search (AVX2)
smallvecStack-allocated small vectors
mimallocHigh-performance memory allocator
num_cpusCPU count for thread sizing
libcsignalfd, sigwait for graceful shutdown
crossbeam-queueLock-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.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages