Skip to content

aryen1101/Distributed_Cache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 

Repository files navigation

High-Performance Distributed Cache System

A robust, multi-node distributed caching system built in Java. This project demonstrates advanced concurrent programming concepts, modular enterprise architecture (SOLID principles), and fault-tolerant system design.


🚀 Core Features

  • Distributed Node Architecture: Keys are deterministically routed to specific cache nodes using a Modulo Routing strategy, ensuring even data distribution.
  • Thundering Herd Prevention (Request Collapsing): If thousands of threads request a missing key simultaneously, the RequestCollapser ensures only one database query is executed, safely sharing the result with all waiting threads.
  • Asynchronous Prefetching: Uses a background Prefetcher thread pool to predict and load future keys (e.g., fetching key N+1 when N is requested) without blocking the main application thread.
  • Pluggable LRU Eviction: Each cache node manages its own memory capacity using a custom Least Recently Used (LRU) algorithm, integrated via an Observer pattern to safely decouple storage from eviction logic.
  • Thread-Safe Dynamic Scaling: Nodes can be safely added or removed at runtime. The system uses concurrent data structures (CopyOnWriteArrayList, ConcurrentHashMap) to allow dynamic scaling and self-healing routing without crashing active read/write requests.

📂 Folder Structure

The codebase strictly follows the Single Responsibility Principle and is divided into logical domains:

src/
├── collapser/
│   └── RequestCollapser.java       # Collapses concurrent DB requests via CompletableFuture
├── data/
│   ├── CacheEntry.java             # Immutable data object holding value and TTL
│   ├── CacheNode.java              # Represents a single server/node in the cluster
│   └── StorageEngine.java          # Thread-safe map manager; reacts to eviction events
├── eviction/
│   ├── IEvictionObserver.java      # Observer interface for eviction callbacks
│   ├── IEvictionStrategy.java      # Base interface for custom eviction algorithms
│   └── LRUStrategy.java            # Custom Doubly-Linked-List + HashMap implementation
├── manager/
│   └── CacheManager.java           # The core orchestrator and context provider
├── prefetch/
│   ├── IPrefetchContext.java       # Interface hiding main cache logic from background threads
│   ├── IPrefetchStrategy.java      # Interface for key prediction logic
│   ├── LinearPrefetchStrategy.java # Predicts the next 'N' sequential keys
│   └── Prefetcher.java             # Runs async background fetches via ExecutorService
└── routing/
    ├── IRouter.java                # Interface for node selection logic
    └── ModuloRouter.java           # Selects nodes using (key % nodeCount)

🧩 Detailed Class Diagram

This diagram showcases the complete system architecture, emphasizing Dependency Inversion. The CacheManager orchestrates the system by depending on abstractions (IRouter, IPrefetchContext) rather than concrete implementations, preventing circular dependencies.

classDiagram
    %% Core Management
    class CacheManager {
        -List~CacheNode~ nodes
        -IRouter router
        -RequestCollapser collapser
        -Prefetcher prefetcher
        -int timeToLive
        +CacheManager(IRouter, RequestCollapser, Prefetcher, int)
        +addNode(CacheNode node) void
        +removeNode(CacheNode node) void
        +get(int key) String
        +put(int key, String value) void
        -prefetchAsync(int key) void
    }

    class IPrefetchContext {
        <<interface>>
        +isPresent(int key) boolean
        +fetch(int key) String
        +putInCache(int key, String data) void
    }

    %% Orchestration Tools
    class RequestCollapser {
        -ConcurrentHashMap~Integer, CompletableFuture~String~~ map
        +RequestCollapser()
        +execute(int key, IPrefetchContext context) String
    }

    class Prefetcher {
        -IPrefetchStrategy strategy
        -ExecutorService executor
        +Prefetcher(IPrefetchStrategy strategy)
        +run(int currentKey, IPrefetchContext context) void
        +shutdown() void
    }

    class IPrefetchStrategy {
        <<interface>>
        +getNextKeys(int currentKey) List~Integer~
    }

    class LinearPrefetchStrategy {
        -int nextKeys
        +LinearPrefetchStrategy(int nextKeys)
        +getNextKeys(int currentKey) List~Integer~
    }

    %% Routing
    class IRouter {
        <<interface>>
        +getTargetNode(int key, List~CacheNode~ nodes) CacheNode
    }

    class ModuloRouter {
        +getTargetNode(int key, List~CacheNode~ nodes) CacheNode
    }

    %% Data Storage Layer
    class CacheNode {
        -String id
        -StorageEngine engine
        +CacheNode(String id, int capacity, IEvictionStrategy strategy)
        +getId() String
        +get(int key) CacheEntry
        +put(int key, String value, int timeToLive) void
        +isPresent(int key) boolean
    }

    class StorageEngine {
        -Map~Integer, CacheEntry~ data
        -int capacity
        -IEvictionStrategy strategy
        +create(int capacity, IEvictionStrategy strategy)$ StorageEngine
        +set(int key, String value, int timeToLive) void
        +get(int key) CacheEntry
        +remove(int key) void
        +isPresent(int key) boolean
        +onEvict(int key) void
    }

    class CacheEntry {
        -String value
        -int expiryTime
        +CacheEntry(String value, int timeToLive)
        +getValue() String
        +isExpired() boolean
    }

    %% Eviction Layer
    class IEvictionStrategy {
        <<interface>>
        +addObserver(IEvictionObserver observer) void
        +record(int key) void
        +removeKey(int key) void
        +evict() void
    }

    class IEvictionObserver {
        <<interface>>
        +onEvict(int key) void
    }

    class LRUStrategy {
        -Node head
        -Node tail
        -Map~Integer, Node~ map
        -List~IEvictionObserver~ observers
        +LRUStrategy()
    }

    %% Relationships
    CacheManager ..|> IPrefetchContext : Implements
    CacheManager --> IRouter : Uses
    CacheManager --> RequestCollapser : Uses
    CacheManager --> Prefetcher : Triggers
    CacheManager o-- CacheNode : Manages Cluster

    ModuloRouter ..|> IRouter : Implements
    LinearPrefetchStrategy ..|> IPrefetchStrategy : Implements

    Prefetcher o-- IPrefetchStrategy : Has-A
    Prefetcher --> IPrefetchContext : Calls back to
    RequestCollapser --> IPrefetchContext : Calls back to

    CacheNode *-- StorageEngine : Owns
    StorageEngine *-- CacheEntry : Stores
    StorageEngine ..|> IEvictionObserver : Implements
    StorageEngine --> IEvictionStrategy : Delegates to

    IEvictionStrategy --> IEvictionObserver : Notifies
    LRUStrategy ..|> IEvictionStrategy : Implements
Loading

🛠️ Getting Started

Prerequisites

  • Java 11 or higher
  • Git

How to Run

1. Clone the repository

git clone https://github.com/aryen1101/Distributed_Cache.git

2. Navigate into the project folder

cd Distributed_Cache

3. Create an output folder and compile

mkdir out
javac -d out -sourcepath src src/Main.java

4. Run the application

java -cp out Main

🏗️ Design Principles

Principle Application
Single Responsibility Each class owns exactly one concern (routing, eviction, collapsing, etc.)
Dependency Inversion CacheManager depends on IRouter and IPrefetchContext, not concrete classes
Open/Closed New eviction strategies or routing algorithms can be plugged in without modifying existing code
Observer Pattern StorageEngine reacts to eviction events via IEvictionObserver without tight coupling to LRUStrategy

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages