Giter Club home page Giter Club logo

java-consistent-hashing-algorithms's Introduction

java-consistent-hashing-algorithms

This project collects Java implementations of the most prominent consistent hashing algorithms for non-peer-to-peer contexts.

The implemented algorithms are:

Each implementation is divided into two classes:

  • Each Engine class (e.g., AnchorEngine) contains an accurate implementation of the algorithm as described in the related paper. These classes do not make any consistency check to keep the performance as close as possible to what was claimed in the related papers.
  • Each Hash class (e.g., AnchorHash) is a wrapper of the related Engine class allowing every implementation to match the same interface. These classes also perform all the consistency checks needed to grant a safe execution.

Benchmarks

The project includes a benchmarking tool designed explicitly for consistent hashing algorithms. The tool allows benchmarking the following metrics in a fair and agnostic way:

  • Memory usage: the amount of memory the algorithm uses to store its internal structure.
  • Init time: the time the algorithm requires to initialize its internal structure.
  • Resize time: the time the algorithm requires to reorganize its internal structure after adding or removing nodes.
  • Lookup time: the time the algorithm needs to find the node a given key belongs to.
  • Balance: the ability of the algorithm to spread the keys evenly across the cluster nodes.
  • Resize balance: the ability of the algorithm to keep its balance after adding or removing nodes.
  • Monotonicity: the ability of the algorithm to move the minimum amount of resources when the cluster scales.

You can build the tool using Apache Maven. It will generate a jar file called consistent-hashing-algorithms-1.0.0-jar-with-dependencies.jar. You can then run the jar file providing a configuration file to customize your execution.

The format of the configuration file is described in detail in the src/main/resources/configs/template.yaml file. The tool will use the src/main/resources/configs/default.yaml file that represents the default configuration if no configuration file is provided.

If the config files are not correctly configured, the tool warns the user and tries to continue the execution. It will run only the correctly configured benchmarks. If the proceeding is not possible, the tool will return an error.

Refer to the template.yaml file for a complete explanation of the configurations.

Once the configuration file is ready, you can run the benchmarks with the following command:

$ java -jar consistent-hashing-algorithms-1.0.0-jar-with-dependencies.jar <your-config>.yaml

Add your own consistent hash algorithm

You can add your own consistent hash algorithm by performing a merge request. The class implementing your algorithm should be called YourAlgorithmEngine. All the classes subfixed by Engine implement the consistent hash algorithms as described in the related papers.

You must implement three more classes to compare your algorithm against the available ones using the benchmark tool. Namely:

  • YourAlgorithmHash: this must implement the ConsistentHash interface and possibly perform all the consistency checks (that can be avoided in the YourAlgorithmEngine).
  • YourAlgorithmEnginePilot: this must implement the ConsistentHashEnginePilot interface and performs the operations of adding a node, removing a node, and lookup a key by invoking the related methods of the YourAlgorithmEngine class in the most efficient way.
  • YourAlgorithmFactory: this must implement the ConsistentHashFactory interface and provides a convenient way to instantiate the algorithm and the other utility classes.

java-consistent-hashing-algorithms's People

Contributors

massimo-coluzzi-supsi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

java-consistent-hashing-algorithms's Issues

RequirementFailure after multiple node additions/removals

After reading your very interesting paper, I did some experiments using your source code. I wrote this test which unfortunately fails after some node additions and removals.

@Test
void test() {

    Set<Node> set = new HashSet<>();
    SplittableRandom random = new SplittableRandom(2L);

    int numIterations = 1000000;
    int maxSetSize = 5;
    int nodeRange = 5;

    Node node = SimpleNode.of(Long.toString(random.nextLong(nodeRange)));

    set.add(node);
    System.out.println("Init with " + node + ", active nodes = " + set);
    MementoHash ch = new MementoHash(Collections.singleton(node));

    for(int i = 0; i < numIterations; ++i) {
        if (random.nextBoolean()) {
            // try to add a node
            if (set.size() < maxSetSize) {
                node = SimpleNode.of(Long.toString(random.nextLong(nodeRange)));
                if (set.add(node)) {
                    // only add if node is new
                    System.out.println("Add " + node + ", active nodes = " + set);
                    ch.addNodes(Collections.singleton(node));
                }
            }
        } else {
            // try to remove a node
            if (set.size() >= 2) {
                // only remove if number of active nodes >= 2
                node = SimpleNode.of(Long.toString(random.nextLong(nodeRange)));
                if (set.remove(node)) {
                    // only remove node if added previously
                    System.out.println("Remove " + node + ", active nodes = " + set);
                    ch.removeNodes(Collections.singleton(node));
                }
            }
        }
    }
}

Output:

Init with 0, active nodes = [0]
Add 4, active nodes = [0, 4]
Add 1, active nodes = [0, 4, 1]
Remove 4, active nodes = [0, 1]
Add 4, active nodes = [0, 4, 1]
Add 3, active nodes = [0, 4, 3, 1]
Add 2, active nodes = [0, 4, 3, 2, 1]
Remove 1, active nodes = [0, 4, 3, 2]
Remove 0, active nodes = [4, 3, 2]
Remove 3, active nodes = [4, 2]
Remove 4, active nodes = [2]
Add 0, active nodes = [0, 2]
Remove 0, active nodes = [2]
Add 3, active nodes = [3, 2]
Add 1, active nodes = [3, 2, 1]
Add 0, active nodes = [0, 3, 2, 1]
Remove 2, active nodes = [0, 3, 1]
Remove 1, active nodes = [0, 3]
Add 1, active nodes = [0, 3, 1]

org.nerd4j.utils.lang.RequirementFailure: [Requirement failed] - The bucket must be in the interval [0,2] but was 3

	at org.nerd4j.utils.lang.Require.toHold(Require.java:138)
	at ch.supsi.dti.isin.cluster.Indirection.put(Indirection.java:73)
	at ch.supsi.dti.isin.consistenthash.memento.MementoHash.addNodes(MementoHash.java:107)
	at ch.supsi.dti.isin.ConsistencyTest.test(ConsistencyTest.java:116)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:139)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:81)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.