A persistent, high-performance Key-Value store based on the standard Bitcask design. It uses an append-only log format and an in-memory hash map for O(1) lookups.
- Writes: Every
putis a direct append to an active.datafile. The in-memory Keydir is then updated with the pointer (file ID, offset, size). - Reads: The engine looks up the key in the Keydir to get the record pointer and performs exactly one disk seek to fetch the value.
- Deletes: Appends a tombstone record to the log. The key is removed from the Keydir.
- Compaction: A background thread periodically merges old data files, discarding obsolete versions of keys and removing tombstones to reclaim space.
- Recovery: On startup, the engine builds the Keydir by scanning
.hintfiles (which are faster than scanning full.datafiles).
- Memory Efficiency: Instead of using
java.util.HashMap(which has high object overhead), I implemented a custom hash map using primitivelong[]andint[]arrays. This keeps the heap clean and reduces GC pressure. - Generic API: Fully generic
KandR extends Record<K>. You can define your own serialization logic by implementing theCodecinterface. - Concurrency: Uses a
ReentrantReadWriteLockto allow concurrent readers while ensuring write/compaction integrity. - CRC Validation: Every record is checksummed on write and validated on read to detect disk corruption.
Implement a Codec for your data type (see RecordCodec.java for a JSON implementation using Jackson):
Codec<String, MyRecord> codec = new MyRecordCodec();
Bootstrap bootstrap = new Bootstrap("/tmp/bitcask-data");
StorageEngine<String, MyRecord> engine = new StorageEngine<>(codec, bootstrap);
engine.init();
// Storage
engine.put(new MyRecord("user_123", "data"));
// Retrieval
MyRecord r = engine.get("user_123");- FileManager: Handles low-level
FileChannelI/O and synchronization. - RecordManager: Orchestrates CRC calculation, slicing byte buffers, and record layout.
- CompactionManager: Manages the lifecycle of background merges and
.hintfile generation. - Keydir: The primary index. If the engine crashes, this is rebuilt from the hint files.
- Put: O(1) append.
- Get: O(1) disk seek.
- Space Efficiency: Controlled by
COMPACTION_THRESHOLDandMAX_FILE_SIZE.
- Compaction is currently triggered by a fixed schedule/threshold; could be more adaptive.
- File descriptor management needs a reference counter to prevent race conditions during heavy compaction.