-
Notifications
You must be signed in to change notification settings - Fork 0
Active ttl #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Active ttl #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,58 @@ | ||
| # Custom HashMap | ||
| # Concurrent Key-Value Store (Jmap) | ||
|
|
||
| This project is a custom implementation of a thread-safe HashMap in Java. It is designed to be a learning exercise to understand the internal workings of a HashMap and multithreading in java. | ||
| A custom, high-performance Concurrent HashMap implementation in Java with Time-To-Live (TTL) support, extensive concurrency strategies, and a built-in interactive Command Line Interface. | ||
|
|
||
| ## Features | ||
|
|
||
| - **Thread-Safe**: The `Jmap` class is thread-safe. It uses bucket-level locking to allow multiple threads to access the map concurrently. This is more efficient than locking the entire map for every operation. | ||
| - **Automatic Resizing**: The map automatically resizes itself when the number of elements exceeds the load factor. This is done to maintain a constant time complexity for the basic operations like `put`, `get`, and `remove`. | ||
| - **Key-Value Store**: The `KVStore` class provides a simple key-value store that uses the `Jmap` as its underlying data structure. It provides an interactive command-line interface to perform `put`, `get`, and `delete` operations. | ||
| - **Persistence**: The `KVStore` class also provides a simple persistence mechanism. It logs all the `put` and `delete` operations to a log file. When the application is started, it rebuilds the in-memory key-value store from the log file. | ||
| - **Custom Concurrent Map (`Jmap`)**: Implements a highly concurrent hash map using bucket-level `ReentrantReadWriteLock`s to minimize thread contention down to the specific bucket level. | ||
| - **Optimistic Reads**: Utilizes Java's `StampedLock` for fast optimistic reads, scaling up throughput. When validation fails (like during a concurrent resize), it gracefully falls back to secure read locks. | ||
| - **Dynamic Resizing**: Automatically handles resizing and re-hashing with minimal disruption when the load factor threshold is reached (default 0.75). | ||
| - **Time-To-Live (TTL) Expiration**: Attach an expiration time to any key using the `putexp` command. A background `TTLManager` daemon thread efficiently cleans up expired keys using a `DelayQueue`. | ||
|
|
||
| ## Methods | ||
| ## Project Structure | ||
|
|
||
| - `PUT`: To add a new key-value pair. | ||
| - `GET`: To retrieve the value for a given key. | ||
| - `DELETE`: To remove a key-value pair. | ||
| - `src/main/java/cache/Jmap.java`: The core concurrent hash map implementation, featuring fine-grained locking and optimistic reads. | ||
| - `src/main/java/cache/Jnode.java`: Represents a node/bucket entry in the map. | ||
| - `src/main/java/cache/KVStore.java`: The main application class providing the interactive CLI. | ||
| - `src/main/java/cache/TTLManager.java`: Manages key expiration and cleanup using a background daemon thread. | ||
| - `src/main/java/cache/ExpiryEntry.java`: A supporting class used by `DelayQueue` to track the expiration time of keys. | ||
| - `src/main/java/cache/JmapBenchmark.java`: JMH benchmarking setup to compare throughput running with 1, 8, and 16 concurrent threads. | ||
| - `src/main/java/cache/StressTest.java`: Multi-threaded integration tests running intensive concurrent operations. | ||
| - `src/test/java/cache/TTLTest.java`: Unit tests ensuring correct TTL behavior. | ||
|
|
||
| ## Usage | ||
|
|
||
| mvn exec:java -Dexec.mainClass="KVStore" | ||
| You can run the interactive CLI by executing the `KVStore` main class. | ||
|
|
||
| ### CLI Commands | ||
|
|
||
| - `put <key> <value>`: Insert or update a key with a value. | ||
| - `putexp <key> <value> <ttl_seconds>`: Insert or update a key with a value and a Time-To-Live (TTL) in seconds. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The command docs say 🤖 Prompt for AI Agents |
||
| - `get <key>`: Retrieve the value associated with a key. | ||
| - `del <key>` (or `remove <key>`): Delete a key from the store. | ||
| - `show` (or `display`): Print the internal state and structure of the map. | ||
| - `count` (or `size`): Print the total number of non-expired nodes in the map. | ||
| - `help`: Display the list of available commands. | ||
| - `exit` (or `quit`): Safely shut down the CLI and background threads. | ||
|
|
||
| ## Build and Run | ||
|
|
||
| This project uses Maven for dependency management and builds. Built on Java 21, packing standard tools like JUnit 5 and JMH. | ||
|
|
||
| ```bash | ||
| mvn clean compile | ||
| mvn exec:java -Dexec.mainClass="cache.KVStore" | ||
| ``` | ||
|
|
||
| ### Running Benchmarks | ||
|
|
||
| Because the program implements JMH benchmarks, you can package and run them locally to observe throughput comparison against `java.util.concurrent.ConcurrentHashMap`: | ||
|
|
||
| ```bash | ||
| mvn clean install | ||
| java -jar target/benchmarks.jar | ||
| ``` | ||
| Global ReentrantReadWriteLock became a bottleneck as thread count increased. Although multiple readers can hold the read lock concurrently, acquiring and releasing the read lock is not free. Internally, the lock maintains shared state (such as reader counts) that is updated atomically. This lock state resides in a cache line that may be cached by multiple CPU cores. When a thread acquires or releases the read lock, its core must obtain exclusive ownership of that cache line before modifying it. This causes cache coherence traffic as ownership of the cache line moves between cores. Under high read concurrency, many threads contend on the same lock metadata, causing frequent cache-line bouncing and reducing scalability. As thread count increases, the overhead of maintaining the shared reader count becomes significant even though the protected data itself is only being read. | ||
| Future scope: | ||
|
|
||
| Might move fom one single global lock to sharding the entire map into smaller pieces and having locks for those smaller shards because stampedlock doesn't always try to acquire a lock but it can also still fail when no. of writes are more. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in “Future scope” sentence ( Small docs typo in a user-facing section. 🧰 Tools🪛 LanguageTool[grammar] ~58-~58: Ensure spelling is correct (QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,85 @@ | ||||||||||||||||||||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||||||||||||||||||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||||||||||||||||||||||
| <modelVersion>4.0.0</modelVersion> | ||||||||||||||||||||||
| <groupId>customhashmap</groupId> | ||||||||||||||||||||||
| <artifactId>customhashmap</artifactId> | ||||||||||||||||||||||
| <version>1.0-SNAPSHOT</version> | ||||||||||||||||||||||
| <build> | ||||||||||||||||||||||
| <plugins> | ||||||||||||||||||||||
| <plugin> | ||||||||||||||||||||||
| <artifactId>maven-shade-plugin</artifactId> | ||||||||||||||||||||||
| <version>3.5.0</version> | ||||||||||||||||||||||
| <executions> | ||||||||||||||||||||||
| <execution> | ||||||||||||||||||||||
| <phase>package</phase> | ||||||||||||||||||||||
| <goals> | ||||||||||||||||||||||
| <goal>shade</goal> | ||||||||||||||||||||||
| </goals> | ||||||||||||||||||||||
| </execution> | ||||||||||||||||||||||
| </executions> | ||||||||||||||||||||||
| <configuration> | ||||||||||||||||||||||
| <finalName>benchmarks</finalName> | ||||||||||||||||||||||
| <transformers> | ||||||||||||||||||||||
| <transformer> | ||||||||||||||||||||||
| <mainClass>org.openjdk.jmh.Main</mainClass> | ||||||||||||||||||||||
| </transformer> | ||||||||||||||||||||||
| <transformer /> | ||||||||||||||||||||||
| </transformers> | ||||||||||||||||||||||
|
Comment on lines
+23
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify all shade transformers in dependency-reduced-pom.xml declare implementation classes.
python - <<'PY'
import xml.etree.ElementTree as ET
pom = "dependency-reduced-pom.xml"
ns = {"m": "http://maven.apache.org/POM/4.0.0"}
tree = ET.parse(pom)
root = tree.getroot()
for t in root.findall(".//m:plugin[m:artifactId='maven-shade-plugin']//m:transformer", ns):
print("transformer attributes:", t.attrib, "| has implementation:", "implementation" in t.attrib)
PYRepository: RishabhRawat9/cache Length of output: 173 🌐 Web query:
💡 Result: The ServicesResourceTransformer in the Maven Shade Plugin is designed specifically to relocate class names and merge multiple entries within META-INF/services resources [1][2]. It does not have, nor does it support, a mainClass attribute or configuration element [1]. The mainClass configuration parameter is specific to the ManifestResourceTransformer, which is used to define the entry point of an executable JAR by modifying the MANIFEST.MF file [3][4]. Attempting to add a mainClass or manifestEntries element to a ServicesResourceTransformer configuration will result in a build error (often reported as "Cannot find '...' in class") because the underlying Java class for the ServicesResourceTransformer does not contain these properties [5]. Regarding the dependency-reduced-pom.xml, this is a separate feature of the shade goal that automatically generates a simplified POM file for the shaded artifact [6]. Its creation is controlled by the createDependencyReducedPom configuration parameter (which defaults to true) and is independent of the transformers specified in the plugin configuration [6][7]. The transformers themselves do not interfere with or require specific handling for this file [1][6]. To correctly configure these elements: 1. Use ServicesResourceTransformer with no additional attributes within the element [2][8]. 2. Use ManifestResourceTransformer if you need to specify a mainClass [4]. 3. The dependency-reduced-pom.xml is managed automatically by the plugin based on its own configuration parameters, not by the transformers [6]. Citations:
Fix missing
🔧 Proposed fix- <transformer>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
- <transformer />
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| </configuration> | ||||||||||||||||||||||
| </plugin> | ||||||||||||||||||||||
| <plugin> | ||||||||||||||||||||||
| <artifactId>maven-compiler-plugin</artifactId> | ||||||||||||||||||||||
| <version>3.13.0</version> | ||||||||||||||||||||||
| <configuration> | ||||||||||||||||||||||
| <release>21</release> | ||||||||||||||||||||||
| <annotationProcessorPaths> | ||||||||||||||||||||||
| <path> | ||||||||||||||||||||||
| <groupId>org.openjdk.jmh</groupId> | ||||||||||||||||||||||
| <artifactId>jmh-generator-annprocess</artifactId> | ||||||||||||||||||||||
| <version>1.37</version> | ||||||||||||||||||||||
| </path> | ||||||||||||||||||||||
| </annotationProcessorPaths> | ||||||||||||||||||||||
| </configuration> | ||||||||||||||||||||||
| </plugin> | ||||||||||||||||||||||
| <plugin> | ||||||||||||||||||||||
| <groupId>org.codehaus.mojo</groupId> | ||||||||||||||||||||||
| <artifactId>exec-maven-plugin</artifactId> | ||||||||||||||||||||||
| <version>3.1.0</version> | ||||||||||||||||||||||
| <configuration> | ||||||||||||||||||||||
| <mainClass>cache.StressTest</mainClass> | ||||||||||||||||||||||
| </configuration> | ||||||||||||||||||||||
| </plugin> | ||||||||||||||||||||||
| <plugin> | ||||||||||||||||||||||
| <artifactId>maven-surefire-plugin</artifactId> | ||||||||||||||||||||||
| <version>3.2.5</version> | ||||||||||||||||||||||
| </plugin> | ||||||||||||||||||||||
| </plugins> | ||||||||||||||||||||||
| </build> | ||||||||||||||||||||||
| <dependencies> | ||||||||||||||||||||||
| <dependency> | ||||||||||||||||||||||
| <groupId>org.junit.jupiter</groupId> | ||||||||||||||||||||||
| <artifactId>junit-jupiter</artifactId> | ||||||||||||||||||||||
| <version>5.10.2</version> | ||||||||||||||||||||||
| <scope>test</scope> | ||||||||||||||||||||||
| <exclusions> | ||||||||||||||||||||||
| <exclusion> | ||||||||||||||||||||||
| <artifactId>junit-jupiter-api</artifactId> | ||||||||||||||||||||||
| <groupId>org.junit.jupiter</groupId> | ||||||||||||||||||||||
| </exclusion> | ||||||||||||||||||||||
| <exclusion> | ||||||||||||||||||||||
| <artifactId>junit-jupiter-params</artifactId> | ||||||||||||||||||||||
| <groupId>org.junit.jupiter</groupId> | ||||||||||||||||||||||
| </exclusion> | ||||||||||||||||||||||
| <exclusion> | ||||||||||||||||||||||
| <artifactId>junit-jupiter-engine</artifactId> | ||||||||||||||||||||||
| <groupId>org.junit.jupiter</groupId> | ||||||||||||||||||||||
| </exclusion> | ||||||||||||||||||||||
| </exclusions> | ||||||||||||||||||||||
| </dependency> | ||||||||||||||||||||||
| </dependencies> | ||||||||||||||||||||||
| <properties> | ||||||||||||||||||||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||||||||||||||||||||
| <maven.compiler.target>21</maven.compiler.target> | ||||||||||||||||||||||
| <maven.compiler.source>21</maven.compiler.source> | ||||||||||||||||||||||
| </properties> | ||||||||||||||||||||||
| </project> | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TTL test path in project structure appears incorrect.
README lists
src/test/java/cache/TTLTest.java, but the provided test snippet is fromsrc/test/java/TTLTest.java. Please correct the path so contributors can find tests reliably.🤖 Prompt for AI Agents