A simple, persistent, log-structured key-value store written in Rust.
This project implements a basic key-value database from scratch, inspired by the design of Bitcask.
It features a TCP server that accepts GET, SET, and DELETE commands — similar to Redis.
- Persistent Storage: All data is written to disk in log files (segments).
- Log-Structured Design: All writes are appends to an active log file, ensuring fast write performance. Updates and deletes are handled by appending new entries.
- In-Memory Index: A
HashMapserves as an in-memory "key directory," mapping keys to the exact location of their latest value on disk for fast read access. - Segment Rollover: The active log file is rolled over to a new file once it reaches a configurable maximum size, splitting the data into manageable segments.
- Startup Reloading: On startup, the server rebuilds the in-memory index by scanning the segment files, ensuring data is not lost between restarts.
- TCP Server: A simple multi-threaded TCP server listens on
127.0.0.1:6379to handle client connections.
The store is built around a few core components:
The main struct that orchestrates the Segments and the KeyDirectory.
It handles the public-facing put, get, and delete operations.
An in-memory HashMap that acts as the index.
It stores keys and maps them to an AppendEntryResponse, which contains the file_id and offset where the value can be found.
Manages all the log files.
It holds one active_segment for new writes and a collection of inactive_segments for old data.
It is responsible for segment rollover.
Represents a single data file on disk (e.g., 1678886400_segment.data).
It uses a Store to append and read raw bytes.
A low-level wrapper around a file that provides basic append and read capabilities.
The unit of data that is serialized and written to the log.
It contains the key, value, timestamp, and a tombstone marker to indicate if an entry is deleted.
- A
PUTcommand is received. - The
KVStorepasses the key and value to theSegmentsmanager. Segmentsserializes them into anEntryand appends it to the active segment.- The
Storewrites the bytes to the end of the file. - The file offset and file ID are returned to the
KVStore, which updates theKeyDirectorywith the new location for that key.
- A
GETcommand is received. - The
KVStorelooks up the key in theKeyDirectory. - If the key exists, the directory returns the
file_id,offset, and length of the data. KVStoreinstructsSegmentsto read from the specified file (active or inactive) at that exact offset.- The
Storeseeks to the offset, reads the bytes, and returns them. - The
Entryis decoded, and the value is returned to the client.
You can run the server using Cargo:
cargo run