Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

DisDB

A distributed NoSQL database inspired by Apache Cassandra, implementing a Log-Structured Merge-tree (LSM) architecture in Java. DisDB provides high write throughput, horizontal scalability, and fault tolerance for modern distributed applications.

Overview

DisDB is a learning-oriented implementation of a Cassandra-like distributed database system. It demonstrates core concepts of distributed databases including:

  • LSM Tree Storage Engine: Optimized for write-heavy workloads
  • Distributed Architecture: Horizontal scalability across multiple nodes
  • Eventual Consistency: CAP theorem trade-offs favoring availability and partition tolerance
  • Decentralized Design: Peer-to-peer architecture with no single point of failure

Features

Core Database Features

  • LSM Tree Storage: Efficient write operations with background compaction
  • Memtable & SSTable: In-memory buffer with persistent disk storage
  • Write-Ahead Log (WAL): Durability guarantees for write operations
  • Bloom Filters: Fast negative lookups to reduce disk I/O
  • Compaction Strategies: Background merging of SSTables to optimize storage

Distributed Features

  • 🌐 Consistent Hashing: Even data distribution across cluster nodes
  • 🔄 Replication: Configurable replication factor for data redundancy
  • 💾 Partition Key-based Sharding: Automatic data partitioning
  • 🔍 Gossip Protocol: Node discovery and cluster state management
  • Tunable Consistency: Configurable read/write consistency levels

Coming Soon

  • Secondary indexes
  • Range queries
  • Materialized views
  • Multi-datacenter replication
  • Compression support

Architecture

LSM Tree Structure

┌─────────────────────────────────────────────────┐
│                   Write Path                    │
├─────────────────────────────────────────────────┤
│  Client Write → WAL → Memtable                  │
│                        ↓                        │
│              (When threshold reached)           │
│                        ↓                        │
│                  Flush to SSTable               │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│                   Read Path                     │
├─────────────────────────────────────────────────┤
│  Client Read → Memtable → Bloom Filter          │
│                        ↓                        │
│                  SSTable (L0)                   │
│                        ↓                        │
│                  SSTable (L1)                   │
│                        ↓                        │
│                  SSTable (Ln)                   │
└─────────────────────────────────────────────────┘

Distributed Architecture

          ┌──────────────┐
          │   Client     │
          └──────┬───────┘
                 │
         ┌───────┴───────┐
         │  Coordinator  │
         │     Node      │
         └───────┬───────┘
                 │
    ┌────────────┼────────────┐
    │            │            │
┌───▼────┐  ┌───▼────┐  ┌───▼────┐
│ Node 1 │  │ Node 2 │  │ Node 3 │
│ (RF=3) │  │ (RF=3) │  │ (RF=3) │
└────────┘  └────────┘  └────────┘
    │            │            │
    └────────────┴────────────┘
       Gossip Protocol

Getting Started

Prerequisites

  • Java 11 or higher
  • Maven 3.6+
  • At least 2GB RAM (recommended)

Installation

# Clone the repository
git clone https://github.com/suvaidkhan/DisDB.git
cd DisDB

# Build the project
mvn clean package

# Run a single node
java -jar target/disdb-1.0-SNAPSHOT.jar

Running a Cluster

Terminal 1 (Seed Node):

java -jar target/disdb-1.0-SNAPSHOT.jar \
  --node-id node1 \
  --port 7000 \
  --data-dir /tmp/disdb/node1

Terminal 2 (Node 2):

java -jar target/disdb-1.0-SNAPSHOT.jar \
  --node-id node2 \
  --port 7001 \
  --data-dir /tmp/disdb/node2 \
  --seed-nodes localhost:7000

Terminal 3 (Node 3):

java -jar target/disdb-1.0-SNAPSHOT.jar \
  --node-id node3 \
  --port 7002 \
  --data-dir /tmp/disdb/node3 \
  --seed-nodes localhost:7000

Usage

Basic Operations

Connect to DisDB

DisDBClient client = new DisDBClient("localhost:7000");

Write Data

// Simple put operation
client.put("users:1001", "name", "Alice");
client.put("users:1001", "email", "alice@example.com");
client.put("users:1001", "age", "28");

// With consistency level
client.put("users:1002", "name", "Bob", ConsistencyLevel.QUORUM);

Read Data

// Simple get operation
String name = client.get("users:1001", "name");

// With consistency level
String email = client.get("users:1001", "email", ConsistencyLevel.ONE);

Delete Data

client.delete("users:1001", "age");

Command Line Interface

# Connect to node
disdb-cli --host localhost --port 7000

# Execute commands
DisDB> PUT users:1001 name Alice
OK

DisDB> GET users:1001 name
Alice

DisDB> DELETE users:1001 name
OK

Configuration

disdb.yaml

cluster:
  name: "DisDB Cluster"
  replication_factor: 3
  
storage:
  data_directory: /var/lib/disdb/data
  commit_log_directory: /var/lib/disdb/commitlog
  memtable_size_mb: 64
  sstable_compression: none

network:
  listen_address: localhost
  rpc_port: 9042
  storage_port: 7000

compaction:
  strategy: size_tiered
  max_threshold: 32
  min_threshold: 4

consistency:
  default_read: ONE
  default_write: QUORUM

How It Works

Write Path

  1. Write-Ahead Log: Every write is first appended to the commit log for durability
  2. Memtable: Data is written to an in-memory sorted structure (Memtable)
  3. Flush: When Memtable reaches threshold, it's flushed to disk as an SSTable
  4. Compaction: Background process merges SSTables to optimize storage and reads

Read Path

  1. Memtable Check: First checks the in-memory Memtable
  2. Bloom Filter: Uses Bloom filters to quickly eliminate SSTables that don't contain the key
  3. SSTable Search: Searches SSTables from newest to oldest
  4. Merge Results: Combines results using timestamp-based conflict resolution

Consistent Hashing

DisDB uses consistent hashing with virtual nodes to distribute data evenly across the cluster:

  • Each physical node is assigned multiple virtual nodes (tokens)
  • Keys are hashed and assigned to nodes based on the token ring
  • Data is replicated to N successive nodes (where N = replication factor)

Replication

Data is automatically replicated across multiple nodes:

Replication Factor = 3

Key: "users:1001" (Hash: 12345)
↓
Token Ring:
  Node1 (Token: 0-10000)
  Node2 (Token: 10001-20000)  ← Primary
  Node3 (Token: 20001-30000)  ← Replica 1
  Node1 (Token: 30001-40000)  ← Replica 2

Consistency Levels

DisDB supports tunable consistency:

Write Consistency

  • ANY: Write succeeds after being written to at least one node
  • ONE: Write succeeds after being written to commit log and memtable of one replica
  • QUORUM: Write succeeds after being written to (replication_factor / 2 + 1) replicas
  • ALL: Write succeeds after being written to all replicas

Read Consistency

  • ONE: Returns data from the nearest replica
  • QUORUM: Returns data after (replication_factor / 2 + 1) replicas respond
  • ALL: Returns data after all replicas respond

Performance Characteristics

  • Write Latency: O(log n) for Memtable insert
  • Read Latency: O(log n) for Memtable + O(k) for SSTable scans where k = number of SSTables
  • Space Amplification: ~2-3x due to LSM compaction overhead
  • Write Throughput: ~50,000 writes/sec per node (hardware dependent)
  • Read Throughput: ~20,000 reads/sec per node (hardware dependent)

Testing

# Run unit tests
mvn test

# Run integration tests
mvn verify

# Run performance benchmarks
mvn test -Pbenchmark

Project Structure

DisDB/
├── src/main/java/com/disdb/
│   ├── core/
│   │   ├── lsm/
│   │   │   ├── Memtable.java
│   │   │   ├── SSTable.java
│   │   │   ├── WAL.java
│   │   │   └── Compaction.java
│   │   ├── storage/
│   │   │   ├── BloomFilter.java
│   │   │   └── StorageEngine.java
│   │   └── index/
│   │       └── Index.java
│   ├── cluster/
│   │   ├── Node.java
│   │   ├── GossipProtocol.java
│   │   ├── ConsistentHash.java
│   │   └── Replication.java
│   ├── protocol/
│   │   ├── Server.java
│   │   └── Client.java
│   └── util/
│       ├── Hash.java
│       └── Config.java
├── pom.xml
└── README.md

Contributing

Contributions are welcome! This project is designed for learning, so feel free to:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Guidelines

  • Follow Java coding conventions
  • Add unit tests for new features
  • Update documentation for significant changes
  • Keep commits atomic and well-described

Roadmap

Phase 1 (Current)

  • LSM tree implementation
  • Basic read/write operations
  • Write-ahead logging
  • Memtable and SSTable

Phase 2

  • Distributed cluster support
  • Consistent hashing
  • Gossip protocol
  • Replication

Phase 3

  • Compaction strategies (size-tiered, leveled)
  • Bloom filters optimization
  • Query language (CQL-like)
  • Client drivers

Phase 4

  • Advanced features (secondary indexes, materialized views)
  • Multi-datacenter replication
  • Performance optimizations
  • Production hardening

Resources

Learning Materials

Similar Projects

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by Apache Cassandra's architecture and design principles
  • LSM tree concepts from Google's Bigtable and LevelDB
  • Distributed systems patterns from Amazon's Dynamo paper

Contact

Suvaid Khan - GitHub

Project Link: https://github.com/suvaidkhan/DisDB


⚠️ Note: DisDB is an educational project designed for learning distributed database concepts. It is not recommended for production use.

About

NoSql Distributed LSM Database

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages