diff --git a/.github/workflows/java-benchmark.yml b/.github/workflows/java-benchmark.yml new file mode 100644 index 0000000000..88303fd3a4 --- /dev/null +++ b/.github/workflows/java-benchmark.yml @@ -0,0 +1,46 @@ +name: Java client benchmarks + +on: + workflow_dispatch: + inputs: + name: + required: false + type: string + +run-name: ${{ inputs.name == '' && format('{0} @ {1}', github.ref_name, github.sha) || inputs.name }} + +jobs: + java-benchmark: + strategy: + matrix: + java: + - 11 + - 17 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: ${{ matrix.java }} + + - name: Start Redis + run: docker run -p 6379:6379 -p 8001:8001 -d redis/redis-stack + + - name: Run benchmarks + working-directory: java/jabushka + run: ./gradlew :benchmark:run + + - name: Upload test reports + if: always() + continue-on-error: true + uses: actions/upload-artifact@v3 + with: + name: test-reports-${{ matrix.java }} + path: | + java/jabushka/benchmarks/build/reports/** diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml new file mode 100644 index 0000000000..1927cbe58f --- /dev/null +++ b/.github/workflows/java.yml @@ -0,0 +1,63 @@ +name: SQL Java CI + +on: + pull_request: + push: + paths: + - "java/**" + - ".github/workflows/java.yml" + +# Run only most latest job on a branch and cancel previous ones +concurrency: + group: ${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + build-and-test-java-client: + strategy: + # Run all jobs + fail-fast: false + matrix: + java: + - 11 + - 17 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: ${{ matrix.java }} + + - name: Install and run protoc (protobuf) + run: | + sudo apt update + sudo apt install -y protobuf-compiler + mkdir -p java/jabushka/jabushka/src/main/java/org/babushka/jabushka/generated + protoc -Iprotobuf=babushka-core/src/protobuf/ --java_out=java/jabushka/jabushka/src/main/java/org/babushka/jabushka/generated babushka-core/src/protobuf/*.proto + + - name: Build rust part + working-directory: java + run: cargo build + + - name: Start Redis + run: docker run -p 6379:6379 -p 8001:8001 -d redis/redis-stack + + - name: Build java part + working-directory: java/jabushka + run: ./gradlew --continue build + + - name: Upload test reports + if: always() + continue-on-error: true + uses: actions/upload-artifact@v3 + with: + name: test-reports-${{ matrix.java }} + path: | + java/jabushka/jabushka/build/reports/** + java/jabushka/integTest/build/reports/** diff --git a/.gitignore b/.gitignore index 92ac3aaf47..ad4f5014ba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ debug/ target/ +# Git stuff +.worktrees + # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock @@ -17,6 +20,14 @@ dump.rdb .env benchmarks/results +# IDE generaged files +.vs +.vscode +.idea + +# MacOS metadata +.DS_Store + # lock files yarn.lock diff --git a/java/.cargo/config.toml b/java/.cargo/config.toml new file mode 100644 index 0000000000..24a6f21533 --- /dev/null +++ b/java/.cargo/config.toml @@ -0,0 +1,3 @@ +[env] +BABUSHKA_NAME = { value = "BabushkaPy", force = true } +BABUSHKA_VERSION = "0.1.0" diff --git a/java/.gitignore b/java/.gitignore index 1b6985c009..8f60484e5a 100644 --- a/java/.gitignore +++ b/java/.gitignore @@ -3,3 +3,6 @@ # Ignore Gradle build output directory build + +# Ignore generated files (e.g. protobuf) +generated diff --git a/java/Cargo.toml b/java/Cargo.toml new file mode 100644 index 0000000000..ccde88c26d --- /dev/null +++ b/java/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "jabushka" +version = "0.0.0" +edition = "2021" +license = "BSD-3-Clause" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "jabushka" +crate-type = ["cdylib"] + +[dependencies] +redis = { path = "../submodules/redis-rs/redis", features = ["aio", "tokio-comp", "connection-manager", "tls", "tokio-rustls-comp"] } +babushka = { path = "../babushka-core" } +tokio = { version = "^1", features = ["rt", "macros", "rt-multi-thread", "time"] } +logger_core = {path = "../logger_core"} +tracing-subscriber = "0.3.16" + +[profile.release] +lto = true +debug = true diff --git a/java/README.md b/java/README.md index 9f19374828..69b38c9cde 100644 --- a/java/README.md +++ b/java/README.md @@ -1,7 +1,7 @@ # Summary - Java Wrapper -This module contains a Java-client wrapper that connects to the `Babushka`-rust-client. The rust client connects to -redis, while this wrapper provides Java-language binding. The objective of this wrapper is to provide a thin-wrapper +This module contains a Java-client wrapper that connects to the `Babushka`-rust-client. The rust client connects to +redis, while this wrapper provides Java-language binding. The objective of this wrapper is to provide a thin-wrapper language api to enhance performance and limit cpu cycles at scale. ## Organization @@ -16,9 +16,17 @@ The Java client (javabushka) contains the following parts: You can assemble the Java clients benchmarks by compiling using `./gradlew build`. +## Code style + +Code style is enforced by spotless with Google Java Format. The build fails if code formatted incorrectly, but you can auto-format code with `./gradlew spotlessApply`. +Run this command before every commit to keep code in the same style. +These IDE plugins can auto-format code on file save or by single click: +* [For Intellij IDEA](https://plugins.jetbrains.com/plugin/18321-spotless-gradle) +* [For VS Code](https://marketplace.visualstudio.com/items?itemName=richardwillis.vscode-spotless-gradle) + ## Benchmarks -You can run benchmarks using `./gradlew run`. You can set arguments using the args flag like: +You can run benchmarks using `./gradlew run`. You can set arguments using the args flag like: ```shell ./gradlew run --args="--clients lettuce" diff --git a/java/benchmarks/src/main/java/javabushka/client/jedis/JedisClient.java b/java/benchmarks/src/main/java/javabushka/client/jedis/JedisClient.java deleted file mode 100644 index 224817740f..0000000000 --- a/java/benchmarks/src/main/java/javabushka/client/jedis/JedisClient.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka.client.jedis; - -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; - -public class JedisClient { - - public final static String DEFAULT_HOST = "localhost"; - public final static int DEFAULT_PORT = 6379; - - Jedis jedisResource; - public boolean someLibraryMethod() { - return true; - } - - public void connectToRedis(String host, int port) { - JedisPool pool = new JedisPool(host, port); - jedisResource = pool.getResource(); - } - - public void connectToRedis() { - connectToRedis(DEFAULT_HOST, DEFAULT_PORT); - } - - public String info() { - return jedisResource.info(); - } - - public String info(String section) { - return jedisResource.info(section); - } - - public void set(String key, String value) { - jedisResource.set(key, value); - } - - public String get(String key) { - return jedisResource.get(key); - } -} diff --git a/java/benchmarks/src/main/java/javabushka/client/lettuce/LettuceAsyncClient.java b/java/benchmarks/src/main/java/javabushka/client/lettuce/LettuceAsyncClient.java deleted file mode 100644 index 74cb2871d7..0000000000 --- a/java/benchmarks/src/main/java/javabushka/client/lettuce/LettuceAsyncClient.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka.client.lettuce; - -import io.lettuce.core.RedisClient; -import io.lettuce.core.RedisFuture; -import io.lettuce.core.api.StatefulRedisConnection; -import io.lettuce.core.api.async.RedisAsyncCommands; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class LettuceAsyncClient { - - RedisClient client; - RedisAsyncCommands lettuceSync; - StatefulRedisConnection connection; - - public final long MAX_TIMEOUT_MS = 1000; - - public void connectToRedis() { - client = RedisClient.create("redis://localhost:6379"); - connection = client.connect(); - lettuceSync = connection.async(); - } - - public RedisFuture set(String key, String value) { - RedisFuture future = lettuceSync.set(key, value); - return future; - } - - public RedisFuture get(String key) { - RedisFuture future = lettuceSync.get(key); - return future; - } - - public Object waitForResult(RedisFuture future) - throws ExecutionException, InterruptedException, TimeoutException { - return this.waitForResult(future, MAX_TIMEOUT_MS); - } - - public Object waitForResult(RedisFuture future, long timeoutMS) - throws ExecutionException, InterruptedException, TimeoutException { - return future.get(timeoutMS, TimeUnit.MILLISECONDS); - } - - public void closeConnection() { - connection.close(); - client.shutdown(); - } -} diff --git a/java/benchmarks/src/main/java/javabushka/client/lettuce/LettuceClient.java b/java/benchmarks/src/main/java/javabushka/client/lettuce/LettuceClient.java deleted file mode 100644 index ea12367f60..0000000000 --- a/java/benchmarks/src/main/java/javabushka/client/lettuce/LettuceClient.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka.client.lettuce; - -import io.lettuce.core.RedisClient; -import io.lettuce.core.api.StatefulRedisConnection; -import io.lettuce.core.api.sync.RedisStringCommands; - -public class LettuceClient { - - RedisClient client; - RedisStringCommands lettuceSync; - StatefulRedisConnection connection; - - public void connectToRedis() { - client = RedisClient.create("redis://localhost:6379"); - connection = client.connect(); - lettuceSync = connection.sync(); - } - - public void set(String key, String value) { - lettuceSync.set(key, value); - } - - public String get(String key) { - return (String) lettuceSync.get(key); - } - - public void closeConnection() { - connection.close(); - client.shutdown(); - } -} diff --git a/java/benchmarks/src/main/java/javabushka/client/utils/Benchmarking.java b/java/benchmarks/src/main/java/javabushka/client/utils/Benchmarking.java deleted file mode 100644 index de8ee3b7f0..0000000000 --- a/java/benchmarks/src/main/java/javabushka/client/utils/Benchmarking.java +++ /dev/null @@ -1,140 +0,0 @@ -package javabushka.client.utils; - -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.Collections; - -public class Benchmarking { - static final double PROB_GET = 0.8; - static final double PROB_GET_EXISTING_KEY = 0.8; - static final int SIZE_GET_KEYSPACE = 3750000; - static final int SIZE_SET_KEYSPACE = 3000000; - - private static ChosenAction randomAction() { - if (Math.random() > PROB_GET) { - return ChosenAction.SET; - } - if (Math.random() > PROB_GET_EXISTING_KEY) { - return ChosenAction.GET_NON_EXISTING; - } - return ChosenAction.GET_EXISTING; - } - - public static String generateKeyGet() { - int range = SIZE_GET_KEYSPACE - SIZE_SET_KEYSPACE; - return Math.floor(Math.random() * range + SIZE_SET_KEYSPACE + 1) + ""; - } - - public static String generateKeySet() { - return (Math.floor(Math.random() * SIZE_SET_KEYSPACE) + 1) + ""; - } - - public interface Operation { - void go(); - } - - public static Map> getLatencies(int iterations, Map actions) { - Map> latencies = new HashMap>(); - for (ChosenAction action : actions.keySet()) { - latencies.put(action, new ArrayList()); - } - - for (int i = 0; i actionLatencies = latencies.get(action); - addLatency(op, actionLatencies); - } - - return latencies; - } - - private static void addLatency(Operation op, ArrayList latencies) { - long before = System.nanoTime(); - op.go(); - long after = System.nanoTime(); - latencies.add(after - before); - } - - // Assumption: latencies is sorted in ascending order - private static Long percentile(ArrayList latencies, int percentile) { - return latencies.get((int) Math.ceil((percentile / 100.0) * latencies.size())); - } - - private static double stdDeviation(ArrayList latencies, Double avgLatency) { - double stdDeviation = latencies.stream() - .mapToDouble(Long::doubleValue) - .reduce(0.0, (stdDev, latency) -> stdDev + Math.pow(latency - avgLatency, 2)); - return Math.sqrt(stdDeviation / latencies.size()); - } - - // This has the side-effect of sorting each latencies ArrayList - public static Map calculateResults(Map> actionLatencies) { - Map results = new HashMap(); - - for (Map.Entry> entry : actionLatencies.entrySet()) { - ChosenAction action = entry.getKey(); - ArrayList latencies = entry.getValue(); - - Double avgLatency = latencies - .stream() - .collect(Collectors.summingLong(Long::longValue)) / Double.valueOf(latencies.size()); - - Collections.sort(latencies); - results.put(action, new LatencyResults( - avgLatency, - percentile(latencies, 50), - percentile(latencies, 90), - percentile(latencies, 99), - stdDeviation(latencies, avgLatency) - )); - } - - return results; - } - - public static void printResults(Map calculatedResults, Optional resultsFile) throws IOException { - if (resultsFile.isPresent()) { - printResults(calculatedResults, resultsFile.get()); - } else { - printResults(calculatedResults); - } - } - - public static void printResults(Map resultsMap, FileWriter resultsFile) throws IOException { - for (Map.Entry entry : resultsMap.entrySet()) { - ChosenAction action = entry.getKey(); - LatencyResults results = entry.getValue(); - - resultsFile.write( - "Avg. time in ms per " + action + ": " + results.avgLatency / 1000000.0); - resultsFile.write( - action + " p50 latency in ms: " + results.p50Latency / 1000000.0); - resultsFile.write( - action + " p90 latency in ms: " + results.p90Latency / 1000000.0); - resultsFile.write( - action + " p99 latency in ms: " + results.p99Latency / 1000000.0); - resultsFile.write(action + " std dev in ms: " + results.stdDeviation / 1000000.0); - } - } - - public static void printResults(Map resultsMap) { - for (Map.Entry entry : resultsMap.entrySet()) { - ChosenAction action = entry.getKey(); - LatencyResults results = entry.getValue(); - - System.out.println( - "Avg. time in ms per " + action + ": " + results.avgLatency / 1000000.0 - ); - System.out.println(action + " p50 latency in ms: " + results.p50Latency / 1000000.0); - System.out.println(action + " p90 latency in ms: " + results.p90Latency / 1000000.0); - System.out.println(action + " p99 latency in ms: " + results.p99Latency / 1000000.0); - System.out.println(action + " std dev in ms: " + results.stdDeviation / 1000000.0); - } - } -} diff --git a/java/benchmarks/src/main/java/javabushka/client/utils/LatencyResults.java b/java/benchmarks/src/main/java/javabushka/client/utils/LatencyResults.java deleted file mode 100644 index 2cb4593746..0000000000 --- a/java/benchmarks/src/main/java/javabushka/client/utils/LatencyResults.java +++ /dev/null @@ -1,24 +0,0 @@ -package javabushka.client.utils; - -// Raw timing results in nanoseconds -public class LatencyResults { - public final double avgLatency; - public final long p50Latency; - public final long p90Latency; - public final long p99Latency; - public final double stdDeviation; - - public LatencyResults( - double avgLatency, - long p50Latency, - long p90Latency, - long p99Latency, - double stdDeviation - ) { - this.avgLatency = avgLatency; - this.p50Latency = p50Latency; - this.p90Latency = p90Latency; - this.p99Latency = p99Latency; - this.stdDeviation = stdDeviation; - } -} diff --git a/java/benchmarks/src/test/java/javabushka/client/jedis/JedisClientIT.java b/java/benchmarks/src/test/java/javabushka/client/jedis/JedisClientIT.java deleted file mode 100644 index 3ec7c59e79..0000000000 --- a/java/benchmarks/src/test/java/javabushka/client/jedis/JedisClientIT.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka.client.jedis; - -import static org.junit.Assert.assertTrue; - -import javabushka.client.utils.ChosenAction; -import javabushka.client.utils.Benchmarking; - -import org.junit.Before; -import org.junit.Test; -import java.util.HashMap; -import java.util.Map; - -public class JedisClientIT { - - JedisClient jedisClient; - - @Before - public void initializeJedisClient() { - jedisClient = new JedisClient(); - jedisClient.connectToRedis(); - } - - @Test public void someLibraryMethodReturnsTrue() { - JedisClient classUnderTest = new JedisClient(); - assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod()); - } - - @Test public void testResourceInfo() { - String result = jedisClient.info(); - - assertTrue(result.length() > 0); - } - - @Test public void testResourceInfoBySection() { - String section = "Server"; - String result = jedisClient.info(section); - - assertTrue(result.length() > 0); - assertTrue(result.startsWith("# " + section)); - } - - @Test public void testResourceSetGet() { - int iterations = 100000; - String value = "my-value"; - - Map actions = new HashMap<>(); - actions.put(ChosenAction.GET_EXISTING, () -> jedisClient.get(Benchmarking.generateKeySet())); - actions.put(ChosenAction.GET_NON_EXISTING, () -> jedisClient.get(Benchmarking.generateKeyGet())); - actions.put(ChosenAction.SET, () -> jedisClient.set(Benchmarking.generateKeySet(), value)); - - Benchmarking.printResults( - Benchmarking.calculateResults( - Benchmarking.getLatencies(iterations, actions) - ) - ); - } -} - diff --git a/java/benchmarks/src/test/java/javabushka/client/lettuce/LettuceAsyncClientIT.java b/java/benchmarks/src/test/java/javabushka/client/lettuce/LettuceAsyncClientIT.java deleted file mode 100644 index c5b2277733..0000000000 --- a/java/benchmarks/src/test/java/javabushka/client/lettuce/LettuceAsyncClientIT.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka.client.lettuce; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import io.lettuce.core.RedisFuture; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class LettuceAsyncClientIT { - - LettuceAsyncClient lettuceClient; - - LettuceAsyncClient otherLettuceClient; - - @Before - public void initializeJedisClient() { - lettuceClient = new LettuceAsyncClient(); - lettuceClient.connectToRedis(); - - otherLettuceClient = new LettuceAsyncClient(); - otherLettuceClient.connectToRedis(); - } - - @After - public void closeConnection() { - lettuceClient.closeConnection(); - otherLettuceClient.closeConnection(); - } - - @Test public void testResourceSetGet() { - String key = "key1"; - String value = "my-value-1"; - - String otherKey = "key2"; - String otherValue = "my-value-2"; - - RedisFuture setResult = lettuceClient.set(key, value); - RedisFuture otherSetResult = otherLettuceClient.set(otherKey, otherValue); - - // and wait for both clients - try { - lettuceClient.waitForResult(setResult); - } catch (Exception e) { - assertTrue("Can SET redis result without Exception", false); - } - try { - otherLettuceClient.waitForResult(otherSetResult); - } catch (Exception e) { - assertTrue("Can SET other redis result without Exception", false); - } - - RedisFuture getResult = lettuceClient.get(key); - RedisFuture otherGetResult = otherLettuceClient.get(otherKey); - String result = "invalid"; - String otherResult = "invalid"; - try { - result = (String) lettuceClient.waitForResult(getResult); - } catch (Exception e) { - assertTrue("Can GET redis result without Exception", false); - } - - try { - otherResult = (String) otherLettuceClient.waitForResult(otherGetResult); - } catch (Exception e) { - assertTrue("Can GET other redis result without Exception", false); - } - - assertEquals(value, result); - assertEquals(otherValue, otherResult); - } -} diff --git a/java/benchmarks/src/test/java/javabushka/client/lettuce/LettuceClientIT.java b/java/benchmarks/src/test/java/javabushka/client/lettuce/LettuceClientIT.java deleted file mode 100644 index 8f72fa9744..0000000000 --- a/java/benchmarks/src/test/java/javabushka/client/lettuce/LettuceClientIT.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka.client.lettuce; - -import javabushka.client.utils.ChosenAction; -import javabushka.client.utils.Benchmarking; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import java.util.HashMap; - -public class LettuceClientIT { - - LettuceClient lettuceClient; - - @Before - public void initializeJedisClient() { - lettuceClient = new LettuceClient(); - lettuceClient.connectToRedis(); - } - - @After - public void closeConnection() { - lettuceClient.closeConnection(); - } - - @Test public void testResourceSetGet() { - int iterations = 100000; - String value = "my-value"; - - HashMap actions = new HashMap<>(); - actions.put(ChosenAction.GET_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeySet())); - actions.put(ChosenAction.GET_NON_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeyGet())); - actions.put(ChosenAction.SET, () -> lettuceClient.set(Benchmarking.generateKeySet(), value)); - } -} diff --git a/java/benchmarks/build.gradle b/java/jabushka/benchmarks/build.gradle similarity index 94% rename from java/benchmarks/build.gradle rename to java/jabushka/benchmarks/build.gradle index 3b57a0575f..5e21fe3ea6 100644 --- a/java/benchmarks/build.gradle +++ b/java/jabushka/benchmarks/build.gradle @@ -10,7 +10,7 @@ repositories { dependencies { // Use JUnit test framework. - testImplementation 'junit:junit:4.13.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation 'com.google.guava:guava:32.1.1-jre' diff --git a/java/benchmarks/src/main/java/javabushka/client/BenchmarkingApp.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/BenchmarkingApp.java similarity index 92% rename from java/benchmarks/src/main/java/javabushka/client/BenchmarkingApp.java rename to java/jabushka/benchmarks/src/main/java/javabushka/client/BenchmarkingApp.java index e57e3e5bc2..c0341c8da3 100644 --- a/java/benchmarks/src/main/java/javabushka/client/BenchmarkingApp.java +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/BenchmarkingApp.java @@ -18,9 +18,7 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -/** - * Benchmarking app for reporting performance of various redis-rs Java-clients - */ +/** Benchmarking app for reporting performance of various redis-rs Java-clients */ public class BenchmarkingApp { // main application entrypoint @@ -34,8 +32,7 @@ public static void main(String[] args) { // parse the command line arguments CommandLine line = parser.parse(options, args); runConfiguration = verifyOptions(line); - } - catch (ParseException exp) { + } catch (ParseException exp) { // oops, something went wrong System.err.println("Parsing failed. Reason: " + exp.getMessage()); } @@ -69,7 +66,6 @@ public static void main(String[] args) { System.out.println("Error closing results file"); } } - } private static Options getOptions() { @@ -101,7 +97,8 @@ private static RunConfiguration verifyOptions(CommandLine line) throws ParseExce if (line.hasOption("resultsFile")) { try { - runConfiguration.resultsFile = Optional.of(new FileWriter(line.getOptionValue("resultsFile"))); + runConfiguration.resultsFile = + Optional.of(new FileWriter(line.getOptionValue("resultsFile"))); } catch (IOException e) { throw new ParseException("Unable to write to resultsFile."); } @@ -155,7 +152,8 @@ private static RunConfiguration verifyOptions(CommandLine line) throws ParseExce return runConfiguration; } - private static void testJedisClientResourceSetGet(RunConfiguration runConfiguration) throws IOException { + private static void testJedisClientResourceSetGet(RunConfiguration runConfiguration) + throws IOException { JedisClient jedisClient = new JedisClient(); jedisClient.connectToRedis(runConfiguration.host, runConfiguration.port); @@ -170,13 +168,13 @@ private static void testJedisClientResourceSetGet(RunConfiguration runConfigurat Map actions = new HashMap<>(); actions.put(ChosenAction.GET_EXISTING, () -> jedisClient.get(Benchmarking.generateKeySet())); - actions.put(ChosenAction.GET_NON_EXISTING, () -> jedisClient.get(Benchmarking.generateKeyGet())); + actions.put( + ChosenAction.GET_NON_EXISTING, () -> jedisClient.get(Benchmarking.generateKeyGet())); actions.put(ChosenAction.SET, () -> jedisClient.set(Benchmarking.generateKeySet(), value)); Benchmarking.printResults( Benchmarking.calculateResults(Benchmarking.getLatencies(iterations, actions)), - runConfiguration.resultsFile - ); + runConfiguration.resultsFile); } private static LettuceAsyncClient initializeLettuceClient() { @@ -185,7 +183,8 @@ private static LettuceAsyncClient initializeLettuceClient() { return lettuceClient; } - private static void testLettuceClientResourceSetGet(RunConfiguration runConfiguration) throws IOException { + private static void testLettuceClientResourceSetGet(RunConfiguration runConfiguration) + throws IOException { LettuceAsyncClient lettuceClient = initializeLettuceClient(); int iterations = 100000; @@ -199,13 +198,13 @@ private static void testLettuceClientResourceSetGet(RunConfiguration runConfigur HashMap actions = new HashMap<>(); actions.put(ChosenAction.GET_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeySet())); - actions.put(ChosenAction.GET_NON_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeyGet())); + actions.put( + ChosenAction.GET_NON_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeyGet())); actions.put(ChosenAction.SET, () -> lettuceClient.set(Benchmarking.generateKeySet(), value)); Benchmarking.printResults( Benchmarking.calculateResults(Benchmarking.getLatencies(iterations, actions)), - runConfiguration.resultsFile - ); + runConfiguration.resultsFile); } public enum ClientName { @@ -215,12 +214,15 @@ public enum ClientName { ALL("All"); private String name; + private ClientName(String name) { this.name = name; } @Override - public String toString() { return this.name; } + public String toString() { + return this.name; + } public boolean isEqual(String other) { return this.toString().equalsIgnoreCase(other); diff --git a/java/jabushka/benchmarks/src/main/java/javabushka/client/jedis/JedisClient.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/jedis/JedisClient.java new file mode 100644 index 0000000000..b3f9e3ee69 --- /dev/null +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/jedis/JedisClient.java @@ -0,0 +1,44 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package javabushka.client.jedis; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +public class JedisClient { + + public static final String DEFAULT_HOST = "localhost"; + public static final int DEFAULT_PORT = 6379; + + Jedis jedisResource; + + public boolean someLibraryMethod() { + return true; + } + + public void connectToRedis(String host, int port) { + JedisPool pool = new JedisPool(host, port); + jedisResource = pool.getResource(); + } + + public void connectToRedis() { + connectToRedis(DEFAULT_HOST, DEFAULT_PORT); + } + + public String info() { + return jedisResource.info(); + } + + public String info(String section) { + return jedisResource.info(section); + } + + public void set(String key, String value) { + jedisResource.set(key, value); + } + + public String get(String key) { + return jedisResource.get(key); + } +} diff --git a/java/jabushka/benchmarks/src/main/java/javabushka/client/lettuce/LettuceAsyncClient.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/lettuce/LettuceAsyncClient.java new file mode 100644 index 0000000000..6c0c8f5083 --- /dev/null +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/lettuce/LettuceAsyncClient.java @@ -0,0 +1,52 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package javabushka.client.lettuce; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class LettuceAsyncClient { + + RedisClient client; + RedisAsyncCommands lettuceSync; + StatefulRedisConnection connection; + + public final long MAX_TIMEOUT_MS = 1000; + + public void connectToRedis() { + client = RedisClient.create("redis://localhost:6379"); + connection = client.connect(); + lettuceSync = connection.async(); + } + + public RedisFuture set(String key, String value) { + RedisFuture future = lettuceSync.set(key, value); + return future; + } + + public RedisFuture get(String key) { + RedisFuture future = lettuceSync.get(key); + return future; + } + + public Object waitForResult(RedisFuture future) + throws ExecutionException, InterruptedException, TimeoutException { + return this.waitForResult(future, MAX_TIMEOUT_MS); + } + + public Object waitForResult(RedisFuture future, long timeoutMS) + throws ExecutionException, InterruptedException, TimeoutException { + return future.get(timeoutMS, TimeUnit.MILLISECONDS); + } + + public void closeConnection() { + connection.close(); + client.shutdown(); + } +} diff --git a/java/jabushka/benchmarks/src/main/java/javabushka/client/lettuce/LettuceClient.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/lettuce/LettuceClient.java new file mode 100644 index 0000000000..c0dc6a38f0 --- /dev/null +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/lettuce/LettuceClient.java @@ -0,0 +1,34 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package javabushka.client.lettuce; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.sync.RedisStringCommands; + +public class LettuceClient { + + RedisClient client; + RedisStringCommands lettuceSync; + StatefulRedisConnection connection; + + public void connectToRedis() { + client = RedisClient.create("redis://localhost:6379"); + connection = client.connect(); + lettuceSync = connection.sync(); + } + + public void set(String key, String value) { + lettuceSync.set(key, value); + } + + public String get(String key) { + return (String) lettuceSync.get(key); + } + + public void closeConnection() { + connection.close(); + client.shutdown(); + } +} diff --git a/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/Benchmarking.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/Benchmarking.java new file mode 100644 index 0000000000..182b062c99 --- /dev/null +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/Benchmarking.java @@ -0,0 +1,141 @@ +package javabushka.client.utils; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public class Benchmarking { + static final double PROB_GET = 0.8; + static final double PROB_GET_EXISTING_KEY = 0.8; + static final int SIZE_GET_KEYSPACE = 3750000; + static final int SIZE_SET_KEYSPACE = 3000000; + + private static ChosenAction randomAction() { + if (Math.random() > PROB_GET) { + return ChosenAction.SET; + } + if (Math.random() > PROB_GET_EXISTING_KEY) { + return ChosenAction.GET_NON_EXISTING; + } + return ChosenAction.GET_EXISTING; + } + + public static String generateKeyGet() { + int range = SIZE_GET_KEYSPACE - SIZE_SET_KEYSPACE; + return Math.floor(Math.random() * range + SIZE_SET_KEYSPACE + 1) + ""; + } + + public static String generateKeySet() { + return (Math.floor(Math.random() * SIZE_SET_KEYSPACE) + 1) + ""; + } + + public interface Operation { + void go(); + } + + public static Map> getLatencies( + int iterations, Map actions) { + Map> latencies = new HashMap>(); + for (ChosenAction action : actions.keySet()) { + latencies.put(action, new ArrayList()); + } + + for (int i = 0; i < iterations; i++) { + ChosenAction action = randomAction(); + Operation op = actions.get(action); + ArrayList actionLatencies = latencies.get(action); + addLatency(op, actionLatencies); + } + + return latencies; + } + + private static void addLatency(Operation op, ArrayList latencies) { + long before = System.nanoTime(); + op.go(); + long after = System.nanoTime(); + latencies.add(after - before); + } + + // Assumption: latencies is sorted in ascending order + private static Long percentile(ArrayList latencies, int percentile) { + return latencies.get((int) Math.ceil((percentile / 100.0) * latencies.size())); + } + + private static double stdDeviation(ArrayList latencies, Double avgLatency) { + double stdDeviation = + latencies.stream() + .mapToDouble(Long::doubleValue) + .reduce(0.0, (stdDev, latency) -> stdDev + Math.pow(latency - avgLatency, 2)); + return Math.sqrt(stdDeviation / latencies.size()); + } + + // This has the side-effect of sorting each latencies ArrayList + public static Map calculateResults( + Map> actionLatencies) { + Map results = new HashMap(); + + for (Map.Entry> entry : actionLatencies.entrySet()) { + ChosenAction action = entry.getKey(); + ArrayList latencies = entry.getValue(); + + Double avgLatency = + latencies.stream().collect(Collectors.summingLong(Long::longValue)) + / Double.valueOf(latencies.size()); + + Collections.sort(latencies); + results.put( + action, + new LatencyResults( + avgLatency, + percentile(latencies, 50), + percentile(latencies, 90), + percentile(latencies, 99), + stdDeviation(latencies, avgLatency))); + } + + return results; + } + + public static void printResults( + Map calculatedResults, Optional resultsFile) + throws IOException { + if (resultsFile.isPresent()) { + printResults(calculatedResults, resultsFile.get()); + } else { + printResults(calculatedResults); + } + } + + public static void printResults( + Map resultsMap, FileWriter resultsFile) throws IOException { + for (Map.Entry entry : resultsMap.entrySet()) { + ChosenAction action = entry.getKey(); + LatencyResults results = entry.getValue(); + + resultsFile.write("Avg. time in ms per " + action + ": " + results.avgLatency / 1000000.0); + resultsFile.write(action + " p50 latency in ms: " + results.p50Latency / 1000000.0); + resultsFile.write(action + " p90 latency in ms: " + results.p90Latency / 1000000.0); + resultsFile.write(action + " p99 latency in ms: " + results.p99Latency / 1000000.0); + resultsFile.write(action + " std dev in ms: " + results.stdDeviation / 1000000.0); + } + } + + public static void printResults(Map resultsMap) { + for (Map.Entry entry : resultsMap.entrySet()) { + ChosenAction action = entry.getKey(); + LatencyResults results = entry.getValue(); + + System.out.println("Avg. time in ms per " + action + ": " + results.avgLatency / 1000000.0); + System.out.println(action + " p50 latency in ms: " + results.p50Latency / 1000000.0); + System.out.println(action + " p90 latency in ms: " + results.p90Latency / 1000000.0); + System.out.println(action + " p99 latency in ms: " + results.p99Latency / 1000000.0); + System.out.println(action + " std dev in ms: " + results.stdDeviation / 1000000.0); + } + } +} diff --git a/java/benchmarks/src/main/java/javabushka/client/utils/ChosenAction.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/ChosenAction.java similarity index 56% rename from java/benchmarks/src/main/java/javabushka/client/utils/ChosenAction.java rename to java/jabushka/benchmarks/src/main/java/javabushka/client/utils/ChosenAction.java index 48f4367308..0bded99ddc 100644 --- a/java/benchmarks/src/main/java/javabushka/client/utils/ChosenAction.java +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/ChosenAction.java @@ -1,7 +1,7 @@ package javabushka.client.utils; public enum ChosenAction { - GET_NON_EXISTING, - GET_EXISTING, - SET + GET_NON_EXISTING, + GET_EXISTING, + SET } diff --git a/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/LatencyResults.java b/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/LatencyResults.java new file mode 100644 index 0000000000..d81deaa7fe --- /dev/null +++ b/java/jabushka/benchmarks/src/main/java/javabushka/client/utils/LatencyResults.java @@ -0,0 +1,19 @@ +package javabushka.client.utils; + +// Raw timing results in nanoseconds +public class LatencyResults { + public final double avgLatency; + public final long p50Latency; + public final long p90Latency; + public final long p99Latency; + public final double stdDeviation; + + public LatencyResults( + double avgLatency, long p50Latency, long p90Latency, long p99Latency, double stdDeviation) { + this.avgLatency = avgLatency; + this.p50Latency = p50Latency; + this.p90Latency = p90Latency; + this.p99Latency = p99Latency; + this.stdDeviation = stdDeviation; + } +} diff --git a/java/jabushka/benchmarks/src/test/java/javabushka/client/jedis/JedisClientIT.java b/java/jabushka/benchmarks/src/test/java/javabushka/client/jedis/JedisClientIT.java new file mode 100644 index 0000000000..09a0e7ef35 --- /dev/null +++ b/java/jabushka/benchmarks/src/test/java/javabushka/client/jedis/JedisClientIT.java @@ -0,0 +1,61 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package javabushka.client.jedis; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import javabushka.client.utils.Benchmarking; +import javabushka.client.utils.ChosenAction; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class JedisClientIT { + + private static JedisClient jedisClient; + + @BeforeAll + static void initializeJedisClient() { + jedisClient = new JedisClient(); + jedisClient.connectToRedis(); + } + + @Test + public void someLibraryMethodReturnsTrue() { + JedisClient classUnderTest = new JedisClient(); + assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'"); + } + + @Test + public void testResourceInfo() { + String result = jedisClient.info(); + + assertTrue(result.length() > 0); + } + + @Test + public void testResourceInfoBySection() { + String section = "Server"; + String result = jedisClient.info(section); + + assertTrue(result.length() > 0); + assertTrue(result.startsWith("# " + section)); + } + + @Test + public void testResourceSetGet() { + int iterations = 100000; + String value = "my-value"; + + Map actions = new HashMap<>(); + actions.put(ChosenAction.GET_EXISTING, () -> jedisClient.get(Benchmarking.generateKeySet())); + actions.put( + ChosenAction.GET_NON_EXISTING, () -> jedisClient.get(Benchmarking.generateKeyGet())); + actions.put(ChosenAction.SET, () -> jedisClient.set(Benchmarking.generateKeySet(), value)); + + Benchmarking.printResults( + Benchmarking.calculateResults(Benchmarking.getLatencies(iterations, actions))); + } +} diff --git a/java/jabushka/benchmarks/src/test/java/javabushka/client/lettuce/LettuceAsyncClientIT.java b/java/jabushka/benchmarks/src/test/java/javabushka/client/lettuce/LettuceAsyncClientIT.java new file mode 100644 index 0000000000..ddc03c77a2 --- /dev/null +++ b/java/jabushka/benchmarks/src/test/java/javabushka/client/lettuce/LettuceAsyncClientIT.java @@ -0,0 +1,77 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package javabushka.client.lettuce; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import io.lettuce.core.RedisFuture; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class LettuceAsyncClientIT { + + private static LettuceAsyncClient lettuceClient; + + private static LettuceAsyncClient otherLettuceClient; + + @BeforeAll + static void initializeJedisClient() { + lettuceClient = new LettuceAsyncClient(); + lettuceClient.connectToRedis(); + + otherLettuceClient = new LettuceAsyncClient(); + otherLettuceClient.connectToRedis(); + } + + @AfterAll + static void closeConnection() { + lettuceClient.closeConnection(); + otherLettuceClient.closeConnection(); + } + + @Test + public void testResourceSetGet() { + String key = "key1"; + String value = "my-value-1"; + + String otherKey = "key2"; + String otherValue = "my-value-2"; + + RedisFuture setResult = lettuceClient.set(key, value); + RedisFuture otherSetResult = otherLettuceClient.set(otherKey, otherValue); + + // and wait for both clients + try { + lettuceClient.waitForResult(setResult); + } catch (Exception e) { + fail("Can SET redis result without Exception"); + } + try { + otherLettuceClient.waitForResult(otherSetResult); + } catch (Exception e) { + fail("Can SET other redis result without Exception"); + } + + RedisFuture getResult = lettuceClient.get(key); + RedisFuture otherGetResult = otherLettuceClient.get(otherKey); + String result = "invalid"; + String otherResult = "invalid"; + try { + result = (String) lettuceClient.waitForResult(getResult); + } catch (Exception e) { + fail("Can GET redis result without Exception"); + } + + try { + otherResult = (String) otherLettuceClient.waitForResult(otherGetResult); + } catch (Exception e) { + fail("Can GET other redis result without Exception"); + } + + assertEquals(value, result); + assertEquals(otherValue, otherResult); + } +} diff --git a/java/jabushka/benchmarks/src/test/java/javabushka/client/lettuce/LettuceClientIT.java b/java/jabushka/benchmarks/src/test/java/javabushka/client/lettuce/LettuceClientIT.java new file mode 100644 index 0000000000..5180489876 --- /dev/null +++ b/java/jabushka/benchmarks/src/test/java/javabushka/client/lettuce/LettuceClientIT.java @@ -0,0 +1,39 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package javabushka.client.lettuce; + +import java.util.HashMap; +import javabushka.client.utils.Benchmarking; +import javabushka.client.utils.ChosenAction; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class LettuceClientIT { + + private static LettuceClient lettuceClient; + + @BeforeAll + static void initializeJedisClient() { + lettuceClient = new LettuceClient(); + lettuceClient.connectToRedis(); + } + + @AfterAll + static void closeConnection() { + lettuceClient.closeConnection(); + } + + @Test + public void testResourceSetGet() { + int iterations = 100000; + String value = "my-value"; + + HashMap actions = new HashMap<>(); + actions.put(ChosenAction.GET_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeySet())); + actions.put( + ChosenAction.GET_NON_EXISTING, () -> lettuceClient.get(Benchmarking.generateKeyGet())); + actions.put(ChosenAction.SET, () -> lettuceClient.set(Benchmarking.generateKeySet(), value)); + } +} diff --git a/java/jabushka/build.gradle b/java/jabushka/build.gradle new file mode 100644 index 0000000000..340f2c98cb --- /dev/null +++ b/java/jabushka/build.gradle @@ -0,0 +1,100 @@ +plugins { + id 'java' + id 'java-library' + id 'io.freefair.lombok' version '6.4.0' + id 'jacoco' + id 'com.diffplug.spotless' version '6.19.0' +} + +repositories { + mavenCentral() +} + +subprojects { + repositories { + mavenCentral() + } + // minimal java compatibility level + plugins.withId('java') { + sourceCompatibility = targetCompatibility = "11" + } + tasks.withType(Test) { + useJUnitPlatform() + + testLogging { + exceptionFormat "full" + events "started", "skipped", "passed", "failed" + showStandardStreams true + } + // TODO: add jacoco with code coverage + // finalizedBy jacocoTestReport, jacocoTestCoverageVerification + } +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.9.2' +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +// JaCoCo section (code coverage by unit tests) +jacoco { + toolVersion = "0.8.9" +} +jacocoTestReport { + reports { + xml.configure { enabled false } + csv.configure { enabled false } + } + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it) + })) + } +} +jacocoTestCoverageVerification { + violationRules { + rule { + element = 'CLASS' + excludes = [ + ] + limit { + counter = 'LINE' + minimum = 1.0 + } + limit { + counter = 'BRANCH' + minimum = 1.0 + } + } + } + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it) + })) + } +} +// TODO: add jacoco with code coverage +// check.dependsOn jacocoTestCoverageVerification +// End of JaCoCo section + +// Spotless section (code style) +spotless { + java { + target fileTree('.') { + include '**/*.java' + exclude '**/build/**', '**/build-*/**', '**/generated/**' + } + importOrder() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + googleJavaFormat('1.17.0').reflowLongStrings().groupArtifact('com.google.googlejavaformat:google-java-format') + } +} +// End of Spotless section diff --git a/java/jabushka/gradle.properties b/java/jabushka/gradle.properties new file mode 100644 index 0000000000..3013c32a31 --- /dev/null +++ b/java/jabushka/gradle.properties @@ -0,0 +1,2 @@ +version=1.13.0 +org.gradle.jvmargs=-Duser.language=en -Duser.country=US diff --git a/java/gradle/wrapper/gradle-wrapper.jar b/java/jabushka/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from java/gradle/wrapper/gradle-wrapper.jar rename to java/jabushka/gradle/wrapper/gradle-wrapper.jar diff --git a/java/gradle/wrapper/gradle-wrapper.properties b/java/jabushka/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from java/gradle/wrapper/gradle-wrapper.properties rename to java/jabushka/gradle/wrapper/gradle-wrapper.properties diff --git a/java/gradlew b/java/jabushka/gradlew similarity index 100% rename from java/gradlew rename to java/jabushka/gradlew diff --git a/java/gradlew.bat b/java/jabushka/gradlew.bat old mode 100644 new mode 100755 similarity index 96% rename from java/gradlew.bat rename to java/jabushka/gradlew.bat index 6689b85bee..93e3f59f13 --- a/java/gradlew.bat +++ b/java/jabushka/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java/jabushka/integTest/build.gradle b/java/jabushka/integTest/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/java/jabushka/jabushka/build.gradle b/java/jabushka/jabushka/build.gradle new file mode 100644 index 0000000000..b60dc6adfc --- /dev/null +++ b/java/jabushka/jabushka/build.gradle @@ -0,0 +1,46 @@ +import java.nio.file.Paths + +plugins { + id 'java-library' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.24.3' +} + +tasks.register('protobuf', Exec) { + doFirst { + project.mkdir(Paths.get(project.projectDir.path, 'src/main/java/org/babushka/jabushka/generated').toString()) + } + commandLine 'protoc', + '-Iprotobuf=babushka-core/src/protobuf/', + '--java_out=java/jabushka/jabushka/src/main/java/org/babushka/jabushka/generated', + 'babushka-core/src/protobuf/connection_request.proto', + 'babushka-core/src/protobuf/redis_request.proto', + 'babushka-core/src/protobuf/response.proto' + workingDir Paths.get(project.rootDir.path, '../..').toFile() +} + +tasks.register('buildRust', Exec) { + commandLine 'cargo', 'build' + workingDir project.rootDir +} + +tasks.register('buildWithRust') { + dependsOn 'buildRust' + finalizedBy 'build' +} + +tasks.register('buildWithProto') { + dependsOn 'protobuf' + finalizedBy 'build' +} + +tasks.register('buildAll') { + dependsOn 'protobuf', 'buildRust' + finalizedBy 'build' +} diff --git a/java/jabushka/settings.gradle b/java/jabushka/settings.gradle new file mode 100644 index 0000000000..f549b8010a --- /dev/null +++ b/java/jabushka/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'babushka' + +include 'jabushka' +include 'integTest' +include 'benchmarks' diff --git a/java/lib/build.gradle b/java/lib/build.gradle deleted file mode 100644 index cc06cd1213..0000000000 --- a/java/lib/build.gradle +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Java library project to get you started. - * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.3/userguide/building_java_projects.html in the Gradle documentation. - */ - -plugins { - // Apply the java-library plugin for API and implementation separation. - id 'java-library' -} - -repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() -} - -dependencies { - // Use JUnit test framework. - testImplementation 'junit:junit:4.13.2' - - // This dependency is exported to consumers, that is to say found on their compile classpath. - api 'org.apache.commons:commons-math3:3.6.1' - - // This dependency is used internally, and not exposed to consumers on their own compile classpath. - implementation 'com.google.guava:guava:32.1.1-jre' -} - -// Apply a specific Java toolchain to ease working on different environments. -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} diff --git a/java/lib/src/main/java/javabushka/Library.java b/java/lib/src/main/java/javabushka/Library.java deleted file mode 100644 index ff87ce99ce..0000000000 --- a/java/lib/src/main/java/javabushka/Library.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka; - -public class Library { - public boolean someLibraryMethod() { - return true; - } -} diff --git a/java/lib/src/test/java/javabushka/LibraryTest.java b/java/lib/src/test/java/javabushka/LibraryTest.java deleted file mode 100644 index 0676a72ab8..0000000000 --- a/java/lib/src/test/java/javabushka/LibraryTest.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package javabushka; - -import org.junit.Test; -import static org.junit.Assert.*; - -public class LibraryTest { - @Test public void someLibraryMethodReturnsTrue() { - Library classUnderTest = new Library(); - assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod()); - } -} diff --git a/java/settings.gradle b/java/settings.gradle deleted file mode 100644 index f4ab308fd0..0000000000 --- a/java/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.3/userguide/building_swift_projects.html in the Gradle documentation. - */ - -plugins { - // Apply the foojay-resolver plugin to allow automatic download of JDKs - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' -} - -rootProject.name = 'javabushka' -include('lib') -include('benchmarks') diff --git a/java/src/lib.rs b/java/src/lib.rs new file mode 100644 index 0000000000..e69de29bb2