From 77767010d7c0b2e813e7975da3aeec851ac1ddd1 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 8 Sep 2024 13:24:03 +0330 Subject: [PATCH 01/28] Add base image for Java benchmarks on OpenWhisk --- config/systems.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/config/systems.json b/config/systems.json index 5a4077a2..9d1baaa7 100644 --- a/config/systems.json +++ b/config/systems.json @@ -316,6 +316,24 @@ "minio": "7.0.16" } } + }, + "java": { + "base_images": { + "8": "openwhisk/actionloop-java-v8", + }, + "images": [ + "function" + ], + "username": "docker_user", + "deployment": { + "files": [ + "index.js", + "storage.js" + ], + "packages": { + "minio": "8.5.9" + } + } } }, "architecture": ["x64"], From 13aa9ec6c4385449d01c22060654fa8313ee1b34 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 8 Sep 2024 17:55:11 +0330 Subject: [PATCH 02/28] Add Dockerfile for running Java benchmarks on OpenWhisk --- dockerfiles/openwhisk/java/Dockerfile.function | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 dockerfiles/openwhisk/java/Dockerfile.function diff --git a/dockerfiles/openwhisk/java/Dockerfile.function b/dockerfiles/openwhisk/java/Dockerfile.function new file mode 100644 index 00000000..d86cd461 --- /dev/null +++ b/dockerfiles/openwhisk/java/Dockerfile.function @@ -0,0 +1,8 @@ +ARG BASE_IMAGE +FROM $BASE_IMAGE +COPY . /function/ + +RUN apt-get update && apt-get install -y maven + +# Check if pom.xml exists before running Maven +RUN if [ -f ./pom.xml ]; then mvn clean install; else echo "pom.xml not found, aborting build." && exit 1; fi From b02657713ecb51c403cb0f6ed2e4dc545100aecc Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 8 Sep 2024 19:30:20 +0330 Subject: [PATCH 03/28] Update base image of java on OpenWhisk --- config/systems.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/systems.json b/config/systems.json index 9d1baaa7..3bbb5acb 100644 --- a/config/systems.json +++ b/config/systems.json @@ -319,7 +319,7 @@ }, "java": { "base_images": { - "8": "openwhisk/actionloop-java-v8", + "8": "openwhisk/java8action" }, "images": [ "function" @@ -327,8 +327,8 @@ "username": "docker_user", "deployment": { "files": [ - "index.js", - "storage.js" + "Main.java", + "Storage.java" ], "packages": { "minio": "8.5.9" From d478dc1d01d249c4c51d9ad8a30277214e0f2d76 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 10 Sep 2024 21:02:36 +0330 Subject: [PATCH 04/28] Add Java-based handler for OpenWhisk --- benchmarks/wrappers/openwhisk/java/Main.java | 55 ++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 benchmarks/wrappers/openwhisk/java/Main.java diff --git a/benchmarks/wrappers/openwhisk/java/Main.java b/benchmarks/wrappers/openwhisk/java/Main.java new file mode 100644 index 00000000..d21960ae --- /dev/null +++ b/benchmarks/wrappers/openwhisk/java/Main.java @@ -0,0 +1,55 @@ +import com.google.gson.JsonObject; +import java.util.faas.Function; +import java.time.Instant; +import java.time.Duration; +import java.io.File; +import java.io.IOException; + + +public class Main { + public static JsonObject main(JsonObject args) { + + // Logger logger = Logger.getLogger(FunctionHandler.class.getName()); + // logger.setLevel(Level.INFO); + + Gson gson = new Gson(); + Function function = new Function(); + + Instant begin = Instant.now(); + JsonObject result = function.handler(args); + Instant end = Instant.now(); + + long computeTime = Duration.between(begin, end).toNanos() / 1000; // Convert nanoseconds to microseconds + + boolean isCold = false; + String fileName = "/cold_run"; + + File file = new File(fileName); + if (!file.exists()) { + isCold = true; + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // Convert to Unix timestamp in seconds.microseconds + String formattedBegin = String.format("%d.%06d", begin.getEpochSecond(), begin.getNano() / 1000); // Convert nanoseconds to microseconds + String formattedEnd = String.format("%d.%06d", end.getEpochSecond(), end.getNano() / 1000); + + String requestId = System.getenv("__OW_ACTIVATION_ID"); + + JsonObject jsonResult = new JsonObject(); + jsonObject.put("begin", formattedBegin); + jsonObject.put("end", formattedEnd); + jsonObject.put("request_id", "requestId"); + jsonObject.put("compute_time", computeTime); + jsonObject.put("is_cold", isCold); + jsonObject.put("result", result); + return jsonResult; + } +} + + + \ No newline at end of file From d36481cadca157d2b9ed2a73087251ac138d0f13 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 19 Sep 2024 03:35:44 +0330 Subject: [PATCH 05/28] Add example config file for running java benchmarks on OpenWhisk --- config/example2.json | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 config/example2.json diff --git a/config/example2.json b/config/example2.json new file mode 100644 index 00000000..1cf21c81 --- /dev/null +++ b/config/example2.json @@ -0,0 +1,69 @@ +{ + "experiments": { + "deployment": "openwhisk", + "update_code": false, + "update_storage": false, + "download_results": false, + "runtime": { + "language": "java", + "version": "8" + }, + "type": "invocation-overhead", + "perf-cost": { + "benchmark": "110.dynamic-html", + "experiments": ["cold", "warm", "burst", "sequential"], + "input-size": "test", + "repetitions": 50, + "concurrent-invocations": 50, + "memory-sizes": [128, 256] + }, + "network-ping-pong": { + "invocations": 50, + "repetitions": 1000, + "threads": 1 + }, + "invocation-overhead": { + "repetitions": 5, + "N": 20, + "type": "payload", + "payload_begin": 1024, + "payload_end": 6251000, + "payload_points": 20, + "code_begin": 1048576, + "code_end": 261619712, + "code_points": 20 + }, + "eviction-model": { + "invocations": 1, + "function_copy_idx": 0, + "repetitions": 5, + "sleep": 1 + } + }, + "deployment": { + "openwhisk": { + "shutdownStorage": false, + "removeCluster": false, + "wskBypassSecurity": "true", + "wskExec": "wsk", + "experimentalManifest": false, + "docker_registry": { + "registry": "", + "username": "", + "password": "" + }, + "storage": { + "address": "", + "mapped_port": 9011, + "access_key": "", + "secret_key": "", + "instance_id": "", + "output_buckets": [], + "input_buckets": [], + "type": "minio" + } + + } + } + } + \ No newline at end of file From a7055e15f6c8a7fbb8f9ce9aff4864716d1b1e45 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 19 Sep 2024 03:41:50 +0330 Subject: [PATCH 06/28] Add JAVA enum to list of languages --- sebs/faas/function.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sebs/faas/function.py b/sebs/faas/function.py index 0fab7bcf..9ddad97f 100644 --- a/sebs/faas/function.py +++ b/sebs/faas/function.py @@ -263,6 +263,7 @@ def deserialize(cached_config: dict) -> "Trigger": class Language(Enum): PYTHON = "python" NODEJS = "nodejs" + JAVA = "java" # FIXME: 3.7+ python with future annotations @staticmethod @@ -299,7 +300,7 @@ def serialize(self) -> dict: @staticmethod def deserialize(config: dict) -> Runtime: - languages = {"python": Language.PYTHON, "nodejs": Language.NODEJS} + languages = {"python": Language.PYTHON, "nodejs": Language.NODEJS, "java": Language.JAVA} return Runtime(language=languages[config["language"]], version=config["version"]) From cdd1763c10fcc40f758ec6b8756a3113ece5cb61 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 19 Sep 2024 03:45:34 +0330 Subject: [PATCH 07/28] Add config of 601.hello-world (A simple java benchmark) --- benchmarks/600.java/601.hello-world/config.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 benchmarks/600.java/601.hello-world/config.json diff --git a/benchmarks/600.java/601.hello-world/config.json b/benchmarks/600.java/601.hello-world/config.json new file mode 100644 index 00000000..0c5d480e --- /dev/null +++ b/benchmarks/600.java/601.hello-world/config.json @@ -0,0 +1,6 @@ +{ + "timeout": 120, + "memory": 512, + "languages": ["java"] + } + \ No newline at end of file From e9b72b21c62bf010abd8d51ca24f498becb58d61 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 19 Sep 2024 04:12:42 +0330 Subject: [PATCH 08/28] Init maven structure of 601.hello-world and add some codes for running java benchmarks --- benchmarks/600.java/601.hello-world/java/pom.xml | 0 .../601.hello-world/java/src/java/Function.java | 0 benchmarks/wrappers/openwhisk/java/Storage.java | 0 sebs.py | 2 +- sebs/benchmark.py | 14 ++++++++++++++ 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 benchmarks/600.java/601.hello-world/java/pom.xml create mode 100644 benchmarks/600.java/601.hello-world/java/src/java/Function.java create mode 100644 benchmarks/wrappers/openwhisk/java/Storage.java diff --git a/benchmarks/600.java/601.hello-world/java/pom.xml b/benchmarks/600.java/601.hello-world/java/pom.xml new file mode 100644 index 00000000..e69de29b diff --git a/benchmarks/600.java/601.hello-world/java/src/java/Function.java b/benchmarks/600.java/601.hello-world/java/src/java/Function.java new file mode 100644 index 00000000..e69de29b diff --git a/benchmarks/wrappers/openwhisk/java/Storage.java b/benchmarks/wrappers/openwhisk/java/Storage.java new file mode 100644 index 00000000..e69de29b diff --git a/sebs.py b/sebs.py index 80fb11ed..9334c6f6 100755 --- a/sebs.py +++ b/sebs.py @@ -64,7 +64,7 @@ def simplified_common_params(func): @click.option( "--language", default=None, - type=click.Choice(["python", "nodejs"]), + type=click.Choice(["python", "nodejs", "java"]), help="Benchmark language", ) @click.option("--language-version", default=None, type=str, help="Benchmark language version") diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 42adb4e7..4205fb54 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -316,6 +316,8 @@ def copy_code(self, output_dir): FILES = { "python": ["*.py", "requirements.txt*"], "nodejs": ["*.js", "package.json"], + "java": ["pom.xml"], + } path = os.path.join(self.benchmark_path, self.language_name) for file_type in FILES[self.language_name]: @@ -358,6 +360,16 @@ def add_deployment_files(self, output_dir): for file in handlers: shutil.copy2(file, os.path.join(output_dir)) + def add_deployment_package_java(self, output_dir): + # append to the end of requirements file + packages = self._system_config.deployment_packages( + self._deployment_name, self.language_name + ) + if len(packages): + with open(os.path.join(output_dir, "requirements.txt"), "a") as out: + for package in packages: + out.write(package) + def add_deployment_package_python(self, output_dir): destination_file = f"requirements.txt.{self._language_version}" @@ -406,6 +418,8 @@ def add_deployment_package(self, output_dir): self.add_deployment_package_python(output_dir) elif self.language == Language.NODEJS: self.add_deployment_package_nodejs(output_dir) + elif self.language == Language.JAVA: + self.add_deployment_package_java(output_dir) else: raise NotImplementedError From 8d2e0157f95d4a345ccdda1e49b9fa1e838aac58 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Fri, 20 Sep 2024 00:46:17 +0330 Subject: [PATCH 09/28] Sync hello-world maven paroject with the wrapper of openwhisk --- .../600.java/601.hello-world/my-app/pom.xml | 18 +++++++++ .../main/java/com/example/project/App.java | 17 +++++++++ .../java/com/example/project/AppTest.java | 38 +++++++++++++++++++ benchmarks/wrappers/openwhisk/java/Main.java | 4 +- 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 benchmarks/600.java/601.hello-world/my-app/pom.xml create mode 100644 benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java create mode 100644 benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java diff --git a/benchmarks/600.java/601.hello-world/my-app/pom.xml b/benchmarks/600.java/601.hello-world/my-app/pom.xml new file mode 100644 index 00000000..f61b9e7f --- /dev/null +++ b/benchmarks/600.java/601.hello-world/my-app/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + com.example.project + my-app + jar + 1.0-SNAPSHOT + my-app + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + diff --git a/benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java b/benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java new file mode 100644 index 00000000..f59864ee --- /dev/null +++ b/benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java @@ -0,0 +1,17 @@ +package com.example.project; +import com.google.gson.JsonObject; + +/** + * Hello world! + * + */ +public class App +{ + + + public JsonObject handler( String[] args ) + { + JsonObject jsonResult = new JsonObject(); + jsonObject.put("my string=", "heloooo worlddd!"); + } +} diff --git a/benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java b/benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java new file mode 100644 index 00000000..b3a7066d --- /dev/null +++ b/benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java @@ -0,0 +1,38 @@ +package com.example.project; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/benchmarks/wrappers/openwhisk/java/Main.java b/benchmarks/wrappers/openwhisk/java/Main.java index d21960ae..828aa64f 100644 --- a/benchmarks/wrappers/openwhisk/java/Main.java +++ b/benchmarks/wrappers/openwhisk/java/Main.java @@ -1,5 +1,5 @@ import com.google.gson.JsonObject; -import java.util.faas.Function; +import com.example.project.App ; import java.time.Instant; import java.time.Duration; import java.io.File; @@ -13,7 +13,7 @@ public static JsonObject main(JsonObject args) { // logger.setLevel(Level.INFO); Gson gson = new Gson(); - Function function = new Function(); + App function = new App(); Instant begin = Instant.now(); JsonObject result = function.handler(args); From 8dcddefe7b9820a29dd3d5c3f90b4602ae1635aa Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 24 Sep 2024 01:21:32 +0330 Subject: [PATCH 10/28] Example config file for running 601.hello-world on openwhisk --- config/example2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/example2.json b/config/example2.json index 1cf21c81..3575d601 100644 --- a/config/example2.json +++ b/config/example2.json @@ -10,7 +10,7 @@ }, "type": "invocation-overhead", "perf-cost": { - "benchmark": "110.dynamic-html", + "benchmark": "601.hello-world", "experiments": ["cold", "warm", "burst", "sequential"], "input-size": "test", "repetitions": 50, From b6c8bb799f2ae6b91baae9fe0c3a44927b2c9647 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 24 Sep 2024 01:23:03 +0330 Subject: [PATCH 11/28] Correct Structure of maven project in 601.hello-world benchmark --- .../600.java/601.hello-world/java/pom.xml | 38 +++++++++++++++++++ .../java/src/java/Function.java | 0 .../java/src/main/java/faas/App.java | 7 ++++ .../600.java/601.hello-world/my-app/pom.xml | 18 --------- .../main/java/com/example/project/App.java | 17 --------- .../java/com/example/project/AppTest.java | 38 ------------------- 6 files changed, 45 insertions(+), 73 deletions(-) delete mode 100644 benchmarks/600.java/601.hello-world/java/src/java/Function.java create mode 100644 benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java delete mode 100644 benchmarks/600.java/601.hello-world/my-app/pom.xml delete mode 100644 benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java delete mode 100644 benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java diff --git a/benchmarks/600.java/601.hello-world/java/pom.xml b/benchmarks/600.java/601.hello-world/java/pom.xml index e69de29b..eb4f359e 100644 --- a/benchmarks/600.java/601.hello-world/java/pom.xml +++ b/benchmarks/600.java/601.hello-world/java/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + faas + 601.hello-world + 1.0-SNAPSHOT + jar + + + + 1.8 + 1.8 + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + faas.App + + + + + + + + diff --git a/benchmarks/600.java/601.hello-world/java/src/java/Function.java b/benchmarks/600.java/601.hello-world/java/src/java/Function.java deleted file mode 100644 index e69de29b..00000000 diff --git a/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java b/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java new file mode 100644 index 00000000..365a6201 --- /dev/null +++ b/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java @@ -0,0 +1,7 @@ +package faas; + +public class App { + public static void main(String[] args) { + System.out.println("Hellooooooooooooooooooo, World!"); + } +} \ No newline at end of file diff --git a/benchmarks/600.java/601.hello-world/my-app/pom.xml b/benchmarks/600.java/601.hello-world/my-app/pom.xml deleted file mode 100644 index f61b9e7f..00000000 --- a/benchmarks/600.java/601.hello-world/my-app/pom.xml +++ /dev/null @@ -1,18 +0,0 @@ - - 4.0.0 - com.example.project - my-app - jar - 1.0-SNAPSHOT - my-app - http://maven.apache.org - - - junit - junit - 3.8.1 - test - - - diff --git a/benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java b/benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java deleted file mode 100644 index f59864ee..00000000 --- a/benchmarks/600.java/601.hello-world/my-app/src/main/java/com/example/project/App.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.project; -import com.google.gson.JsonObject; - -/** - * Hello world! - * - */ -public class App -{ - - - public JsonObject handler( String[] args ) - { - JsonObject jsonResult = new JsonObject(); - jsonObject.put("my string=", "heloooo worlddd!"); - } -} diff --git a/benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java b/benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java deleted file mode 100644 index b3a7066d..00000000 --- a/benchmarks/600.java/601.hello-world/my-app/src/test/java/com/example/project/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.example.project; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} From 303297834b2927e3980b9d1f89ddf1a0a8806ca7 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 24 Sep 2024 01:23:55 +0330 Subject: [PATCH 12/28] Expand add_code functions for maven java rojects --- sebs/benchmark.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 4205fb54..6e50f01c 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -317,12 +317,23 @@ def copy_code(self, output_dir): "python": ["*.py", "requirements.txt*"], "nodejs": ["*.js", "package.json"], "java": ["pom.xml"], - } path = os.path.join(self.benchmark_path, self.language_name) + for file_type in FILES[self.language_name]: for f in glob.glob(os.path.join(path, file_type)): shutil.copy2(os.path.join(path, f), output_dir) + + # copy src folder of java (java benchmarks are maven project and need directories) + if self.language_name == "java": + output_src_dir = os.path.join(output_dir, "src") + + if os.path.exists(output_src_dir): + # If src dir in output exist, remove the directory and all its contents + shutil.rmtree(output_src_dir) + #To have contents of src directory in the direcory named src located in output + shutil.copytree(os.path.join(path, "src"), output_src_dir) + # support node.js benchmarks with language specific packages nodejs_package_json = os.path.join(path, f"package.json.{self.language_version}") if os.path.exists(nodejs_package_json): From 32235f50c119ed81c41f341eaf6a6e07c519e311 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 24 Sep 2024 01:26:57 +0330 Subject: [PATCH 13/28] Exclude Java main wrapper from Docker directory created in runtimes. --- sebs/openwhisk/openwhisk.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sebs/openwhisk/openwhisk.py b/sebs/openwhisk/openwhisk.py index 9c196fe2..68aa42d0 100644 --- a/sebs/openwhisk/openwhisk.py +++ b/sebs/openwhisk/openwhisk.py @@ -115,6 +115,7 @@ def package_code( CONFIG_FILES = { "python": ["__main__.py"], "nodejs": ["index.js"], + "nodejs": ["Main.java"], } package_config = CONFIG_FILES[language_name] From 7f8eb2f8d7cefe455e28e472030cbc4fafde3ca4 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Wed, 25 Sep 2024 13:31:49 +0330 Subject: [PATCH 14/28] Fix a big --- sebs/openwhisk/openwhisk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sebs/openwhisk/openwhisk.py b/sebs/openwhisk/openwhisk.py index 68aa42d0..a8e23925 100644 --- a/sebs/openwhisk/openwhisk.py +++ b/sebs/openwhisk/openwhisk.py @@ -115,7 +115,7 @@ def package_code( CONFIG_FILES = { "python": ["__main__.py"], "nodejs": ["index.js"], - "nodejs": ["Main.java"], + "java": ["Main.java"], } package_config = CONFIG_FILES[language_name] From 67bb80cc07142dd90e69b6d78d6cea2da6b2342e Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 16 Mar 2025 01:00:20 +0330 Subject: [PATCH 15/28] Add required changes from PR222 to enable benchmarking of java codes. --- .gitignore | 3 ++ benchmarks/wrappers/openwhisk/java/Main.java | 29 +++++++++------ .../openwhisk/java/Dockerfile.function | 6 ++-- sebs/benchmark.py | 27 +++++++++++++- sebs/openwhisk/openwhisk.py | 36 ++++++++++++++----- 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 0712f6d7..d158df83 100644 --- a/.gitignore +++ b/.gitignore @@ -188,3 +188,6 @@ cache # IntelliJ IDEA files .idea *.iml + +# Visual Studio Code files +.vscode/ \ No newline at end of file diff --git a/benchmarks/wrappers/openwhisk/java/Main.java b/benchmarks/wrappers/openwhisk/java/Main.java index 828aa64f..e10d9e11 100644 --- a/benchmarks/wrappers/openwhisk/java/Main.java +++ b/benchmarks/wrappers/openwhisk/java/Main.java @@ -1,9 +1,13 @@ +import faas.App; +import com.google.gson.Gson; import com.google.gson.JsonObject; -import com.example.project.App ; +import util.SessionBlob; +import util.ShaSecurityProvider; import java.time.Instant; import java.time.Duration; import java.io.File; import java.io.IOException; +//import jakarta.ws.rs.core.Response; public class Main { @@ -15,12 +19,17 @@ public static JsonObject main(JsonObject args) { Gson gson = new Gson(); App function = new App(); + long start_nano = System.nanoTime(); + Instant begin = Instant.now(); JsonObject result = function.handler(args); Instant end = Instant.now(); - long computeTime = Duration.between(begin, end).toNanos() / 1000; // Convert nanoseconds to microseconds + long end_nano = System.nanoTime(); + + // long computeTime = Duration.between(begin, end).toNanos() / 1000; // Convert nanoseconds to microseconds + long computeTime = end_nano - start_nano; boolean isCold = false; String fileName = "/cold_run"; @@ -41,15 +50,13 @@ public static JsonObject main(JsonObject args) { String requestId = System.getenv("__OW_ACTIVATION_ID"); JsonObject jsonResult = new JsonObject(); - jsonObject.put("begin", formattedBegin); - jsonObject.put("end", formattedEnd); - jsonObject.put("request_id", "requestId"); - jsonObject.put("compute_time", computeTime); - jsonObject.put("is_cold", isCold); - jsonObject.put("result", result); + jsonResult.addProperty("begin", formattedBegin); + jsonResult.addProperty("end", formattedEnd); + jsonResult.addProperty("request_id", requestId); + jsonResult.addProperty("compute_time", computeTime); + jsonResult.addProperty("is_cold", isCold); + jsonResult.addProperty("result", result.toString()); return jsonResult; } -} - - \ No newline at end of file +} diff --git a/dockerfiles/openwhisk/java/Dockerfile.function b/dockerfiles/openwhisk/java/Dockerfile.function index d86cd461..b72ceb15 100644 --- a/dockerfiles/openwhisk/java/Dockerfile.function +++ b/dockerfiles/openwhisk/java/Dockerfile.function @@ -2,7 +2,7 @@ ARG BASE_IMAGE FROM $BASE_IMAGE COPY . /function/ -RUN apt-get update && apt-get install -y maven +# RUN apt-get update && apt-get install -y maven -# Check if pom.xml exists before running Maven -RUN if [ -f ./pom.xml ]; then mvn clean install; else echo "pom.xml not found, aborting build." && exit 1; fi +# # Check if pom.xml exists before running Maven +# RUN if [ -f ./pom.xml ]; then mvn clean install; else echo "pom.xml not found, aborting build." && exit 1; fi diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 6e50f01c..edb1cf32 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -1,6 +1,7 @@ import glob import hashlib import json +import subprocess import os import shutil import subprocess @@ -252,8 +253,9 @@ def hash_directory(directory: str, deployment: str, language: str): FILES = { "python": ["*.py", "requirements.txt*"], "nodejs": ["*.js", "package.json"], + "java": ["*.java", "pom.xml"], } - WRAPPERS = {"python": "*.py", "nodejs": "*.js"} + WRAPPERS = {"python": "*.py", "nodejs": "*.js", "java": "*.java"} NON_LANG_FILES = ["*.sh", "*.json"] selected_files = FILES[language] + NON_LANG_FILES for file_type in selected_files: @@ -339,6 +341,28 @@ def copy_code(self, output_dir): if os.path.exists(nodejs_package_json): shutil.copy2(nodejs_package_json, os.path.join(output_dir, "package.json")) + #This is for making jar file and add it to docker directory + def add_java_output(self, code_dir): + + if self.language_name == "java": + + # Step 1: Move Main.java o src directory + src_dir = os.path.join(code_dir, "src", "main", "java") + if os.path.exists(code_dir): + main_java_path = os.path.join(code_dir, "Main.java") + if os.path.exists(main_java_path): + shutil.move(main_java_path, src_dir) + + # Step 2: Run mvn clean install + try: + # Navigate to the code directory where the pom.xml file is located + subprocess.run(['mvn', 'clean', 'install'], cwd=code_dir, check=True, text=True, capture_output=True) + print("Maven build successful!") + except subprocess.CalledProcessError as e: + print(f"Error during Maven build:\n{e.stdout}\n{e.stderr}") + return + + def add_benchmark_data(self, output_dir): cmd = "/bin/bash {benchmark_path}/init.sh {output_dir} false {architecture}" paths = [ @@ -617,6 +641,7 @@ def build( self.copy_code(self._output_dir) self.add_benchmark_data(self._output_dir) self.add_deployment_files(self._output_dir) + self.add_java_output(self._output_dir) self.add_deployment_package(self._output_dir) self.install_dependencies(self._output_dir) diff --git a/sebs/openwhisk/openwhisk.py b/sebs/openwhisk/openwhisk.py index a8e23925..0dc61b75 100644 --- a/sebs/openwhisk/openwhisk.py +++ b/sebs/openwhisk/openwhisk.py @@ -110,14 +110,14 @@ def package_code( directory, language_name, language_version, architecture, benchmark, is_cached ) - # We deploy Minio config in code package since this depends on local - # deployment - it cannnot be a part of Docker image - CONFIG_FILES = { - "python": ["__main__.py"], - "nodejs": ["index.js"], - "java": ["Main.java"], - } - package_config = CONFIG_FILES[language_name] + if language_name != 'java': + # We deploy Minio config in code package since this depends on local + # deployment - it cannnot be a part of Docker image + CONFIG_FILES = { + "python": ["__main__.py"], + "nodejs": ["index.js"], + } + package_config = CONFIG_FILES[language_name] benchmark_archive = os.path.join(directory, f"{benchmark}.zip") subprocess.run( @@ -208,6 +208,25 @@ def create_function( code_package.language_version, code_package.architecture, ) + run_arguments = [ + *self.get_wsk_cmd(), + "action", + "create", + func_name, + "--web", + "true", + "--docker", + docker_image, + "--memory", + str(code_package.benchmark_config.memory), + "--timeout", + str(code_package.benchmark_config.timeout * 1000), + *self.storage_arguments(), + code_package.code_location, + ] + if code_package.language_name == 'java': + run_arguments.extend(["--main", "Main"]) + subprocess.run( [ *self.get_wsk_cmd(), @@ -229,6 +248,7 @@ def create_function( stdout=subprocess.PIPE, check=True, ) + function_cfg.docker_image = docker_image res = OpenWhiskFunction( func_name, code_package.benchmark, code_package.hash, function_cfg From 19c572d9ced40d623a776d467c37e24cbe582904 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 18 Mar 2025 14:23:54 +0330 Subject: [PATCH 16/28] Fix bug: Java simple benchmark (601.hello_world) now works correctly --- .../600.java/601.hello-world/config.json | 8 +-- benchmarks/600.java/601.hello-world/input.py | 5 ++ .../600.java/601.hello-world/java/pom.xml | 53 +++++++++++-------- .../java/src/main/java/faas/App.java | 8 ++- benchmarks/wrappers/openwhisk/java/Main.java | 5 +- 5 files changed, 47 insertions(+), 32 deletions(-) create mode 100644 benchmarks/600.java/601.hello-world/input.py diff --git a/benchmarks/600.java/601.hello-world/config.json b/benchmarks/600.java/601.hello-world/config.json index 0c5d480e..e3d6f85f 100644 --- a/benchmarks/600.java/601.hello-world/config.json +++ b/benchmarks/600.java/601.hello-world/config.json @@ -1,6 +1,6 @@ { - "timeout": 120, - "memory": 512, + "timeout": 60, + "memory": 256, "languages": ["java"] - } - \ No newline at end of file +} + diff --git a/benchmarks/600.java/601.hello-world/input.py b/benchmarks/600.java/601.hello-world/input.py new file mode 100644 index 00000000..136f8bc5 --- /dev/null +++ b/benchmarks/600.java/601.hello-world/input.py @@ -0,0 +1,5 @@ +def buckets_count(): + return (0, 0) + +def generate_input(data_dir, size, benchmarks_bucket, input_paths, output_paths, upload_func): + return { } \ No newline at end of file diff --git a/benchmarks/600.java/601.hello-world/java/pom.xml b/benchmarks/600.java/601.hello-world/java/pom.xml index eb4f359e..f5e1e781 100644 --- a/benchmarks/600.java/601.hello-world/java/pom.xml +++ b/benchmarks/600.java/601.hello-world/java/pom.xml @@ -1,38 +1,45 @@ - - + + 4.0.0 - faas - 601.hello-world - 1.0-SNAPSHOT - jar + benchmark + 1 - - 1.8 - 1.8 + 8 + 8 + UTF-8 - + + + + com.google.code.gson + gson + 2.11.0 + + org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - faas.App - - - + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + - diff --git a/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java b/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java index 365a6201..367cc204 100644 --- a/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java +++ b/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java @@ -1,7 +1,11 @@ package faas; +import com.google.gson.JsonObject; public class App { - public static void main(String[] args) { - System.out.println("Hellooooooooooooooooooo, World!"); + public JsonObject handler(JsonObject args) { + + JsonObject jsonResult = new JsonObject(); + jsonResult.addProperty("Hello", "World"); + return jsonResult; } } \ No newline at end of file diff --git a/benchmarks/wrappers/openwhisk/java/Main.java b/benchmarks/wrappers/openwhisk/java/Main.java index e10d9e11..138b7dfe 100644 --- a/benchmarks/wrappers/openwhisk/java/Main.java +++ b/benchmarks/wrappers/openwhisk/java/Main.java @@ -1,13 +1,11 @@ import faas.App; import com.google.gson.Gson; import com.google.gson.JsonObject; -import util.SessionBlob; -import util.ShaSecurityProvider; import java.time.Instant; import java.time.Duration; import java.io.File; import java.io.IOException; -//import jakarta.ws.rs.core.Response; + public class Main { @@ -60,3 +58,4 @@ public static JsonObject main(JsonObject args) { } } + From ab2bc2dcce7dd6d3e8fc86b8b70f285c8a54befb Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 18 Mar 2025 14:54:57 +0330 Subject: [PATCH 17/28] Use language enum instead of hardcoded 'java' --- sebs/benchmark.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sebs/benchmark.py b/sebs/benchmark.py index edb1cf32..839feed2 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -315,6 +315,8 @@ def query_cache(self): self._is_cached_valid = False def copy_code(self, output_dir): + from sebs.faas.function import Language + FILES = { "python": ["*.py", "requirements.txt*"], "nodejs": ["*.js", "package.json"], @@ -327,7 +329,7 @@ def copy_code(self, output_dir): shutil.copy2(os.path.join(path, f), output_dir) # copy src folder of java (java benchmarks are maven project and need directories) - if self.language_name == "java": + if self.language == Language.JAVA: output_src_dir = os.path.join(output_dir, "src") if os.path.exists(output_src_dir): @@ -343,8 +345,8 @@ def copy_code(self, output_dir): #This is for making jar file and add it to docker directory def add_java_output(self, code_dir): - - if self.language_name == "java": + from sebs.faas.function import Language + if self.language == Language.JAVA: # Step 1: Move Main.java o src directory src_dir = os.path.join(code_dir, "src", "main", "java") From 37c37aee43d9e1be8c93ecfe7b3d69defc32c282 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Tue, 18 Mar 2025 16:33:14 +0330 Subject: [PATCH 18/28] Remove unused parts from the Java benchmarks wrapper for OpenWhisk. --- benchmarks/wrappers/openwhisk/java/Main.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/benchmarks/wrappers/openwhisk/java/Main.java b/benchmarks/wrappers/openwhisk/java/Main.java index 138b7dfe..161dc7bd 100644 --- a/benchmarks/wrappers/openwhisk/java/Main.java +++ b/benchmarks/wrappers/openwhisk/java/Main.java @@ -1,5 +1,4 @@ import faas.App; -import com.google.gson.Gson; import com.google.gson.JsonObject; import java.time.Instant; import java.time.Duration; @@ -7,14 +6,9 @@ import java.io.IOException; - public class Main { public static JsonObject main(JsonObject args) { - - // Logger logger = Logger.getLogger(FunctionHandler.class.getName()); - // logger.setLevel(Level.INFO); - Gson gson = new Gson(); App function = new App(); long start_nano = System.nanoTime(); From 601903f2dd396aaee8c91388aa21c2a00e672aed Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Wed, 19 Mar 2025 15:36:53 +0330 Subject: [PATCH 19/28] Change the directory where the file is created in container for detecting cold starts --- benchmarks/wrappers/openwhisk/java/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/wrappers/openwhisk/java/Main.java b/benchmarks/wrappers/openwhisk/java/Main.java index 161dc7bd..59a33ee3 100644 --- a/benchmarks/wrappers/openwhisk/java/Main.java +++ b/benchmarks/wrappers/openwhisk/java/Main.java @@ -23,7 +23,7 @@ public static JsonObject main(JsonObject args) { long computeTime = end_nano - start_nano; boolean isCold = false; - String fileName = "/cold_run"; + String fileName = "/tmp/cold_run"; File file = new File(fileName); if (!file.exists()) { From 2c59db1a79eac7f6dc9bb0d99e2f5371826a8abb Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 4 May 2025 13:55:24 +0330 Subject: [PATCH 20/28] Add Java wrapper for AWS Lambda benchmarks --- benchmarks/wrappers/aws/java/Handler.java | 75 +++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 benchmarks/wrappers/aws/java/Handler.java diff --git a/benchmarks/wrappers/aws/java/Handler.java b/benchmarks/wrappers/aws/java/Handler.java new file mode 100644 index 00000000..b51de44c --- /dev/null +++ b/benchmarks/wrappers/aws/java/Handler.java @@ -0,0 +1,75 @@ +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.type.TypeReference; + +import faas.App; + +import java.io.File; +import java.io.IOException; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +public class Handler implements RequestHandler, String> { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + public String handleRequest(Map event, Context context) { + + Map inputData = event; + + // Extract input if trigger is API Gateway (body is a string) + if (event.containsKey("body") && event.get("body") instanceof String) + try { + inputData = mapper.readValue((String) event.get("body"),new TypeReference>() {}); + } catch (IOException e) { + throw new RuntimeException("Failed to parse JSON body", e); + } + + App function = new App(); + + Instant begin = Instant.now(); + long start_nano = System.nanoTime(); + + Map functionOutput = function.handler(inputData); + + long end_nano = System.nanoTime(); + Instant end = Instant.now(); + + + long computeTime = end_nano - start_nano; + // Detect cold start + boolean isCold = false; + String fileName = "/tmp/cold_run"; + + File file = new File(fileName); + if (!file.exists()) { + isCold = true; + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // Convert to Unix timestamp in seconds.microseconds + String formattedBegin = String.format("%d.%06d", begin.getEpochSecond(), begin.getNano() / 1000); // Convert nanoseconds to microseconds + String formattedEnd = String.format("%d.%06d", end.getEpochSecond(), end.getNano() / 1000); + + + Map result = new HashMap<>(); + result.put("begin", formattedBegin); + result.put("end", formattedEnd); + result.put("request_id", context.getAwsRequestId()); + result.put("compute_time", computeTime); + result.put("is_cold", isCold); + result.put("result", functionOutput); + try { + return mapper.writeValueAsString(result); + } catch (IOException e) { + throw new RuntimeException("Failed to serialize result of benchmark to JSON in Wrapper", e); + } + + } +} From 726e07a6c7fd73f04bcd8cdc14eff8ab40410b7f Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 11 May 2025 01:05:22 +0330 Subject: [PATCH 21/28] Add the nosql_func argument to the signature of generate_input --- benchmarks/600.java/601.hello-world/input.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/benchmarks/600.java/601.hello-world/input.py b/benchmarks/600.java/601.hello-world/input.py index 136f8bc5..52536abf 100644 --- a/benchmarks/600.java/601.hello-world/input.py +++ b/benchmarks/600.java/601.hello-world/input.py @@ -1,5 +1,13 @@ def buckets_count(): return (0, 0) -def generate_input(data_dir, size, benchmarks_bucket, input_paths, output_paths, upload_func): +def generate_input( + data_dir, + size, + benchmarks_bucket, + input_paths, + output_paths, + upload_func, + nosql_func=None +): return { } \ No newline at end of file From 4f88a8dfabc7efd6fa55e4b20146d8c767541d31 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 11 May 2025 01:16:43 +0330 Subject: [PATCH 22/28] Update hello_world benchmark input/output) for platform-independence Removed gson package, which was only suitable for OpenWhisk. --- .../java/src/main/java/faas/App.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java b/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java index 367cc204..fe0b2096 100644 --- a/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java +++ b/benchmarks/600.java/601.hello-world/java/src/main/java/faas/App.java @@ -1,11 +1,13 @@ package faas; -import com.google.gson.JsonObject; +import java.util.HashMap; +import java.util.Map; public class App { - public JsonObject handler(JsonObject args) { + public Map handler(Map input) { - JsonObject jsonResult = new JsonObject(); - jsonResult.addProperty("Hello", "World"); - return jsonResult; + Map result = new HashMap<>(); + result.put("Hello", "World"); + return result; } -} \ No newline at end of file +} + From 139cf2992f983f77df9911f63824605a1feccf8a Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 11 May 2025 01:21:23 +0330 Subject: [PATCH 23/28] Update system.json for Java on AWS --- config/systems.json | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/config/systems.json b/config/systems.json index 3bbb5acb..b00fe237 100644 --- a/config/systems.json +++ b/config/systems.json @@ -121,10 +121,32 @@ "uuid": "3.4.0" } } - } + }, + "java": { + "base_images": { + "x64": { + "11": "amazon/aws-lambda-java:11" + }, + "arm64": { + "11": "amazon/aws-lambda-java:11" + } + }, + "images": [ + "build" + ], + "deployment": { + "files": [ + "Handler.java" + ], + "packages": { + "com.amazonaws:aws-lambda-java-core": "1.2.3", + "com.fasterxml.jackson.core:jackson-databind": "2.15.2" + } + } + } }, "architecture": ["x64", "arm64"], - "deployments": ["package", "container"] + "deployments": ["package"] }, "azure": { "languages": { From b1a0a0f95eddab5d72f47d37c5a1885a34be1edb Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sun, 11 May 2025 01:24:27 +0330 Subject: [PATCH 24/28] Platform-related dependencies for Java benchmarks are now added dynamically at runtime to pom.xml --- .../600.java/601.hello-world/java/pom.xml | 90 +++++++++++-------- sebs/benchmark.py | 47 +++++++--- 2 files changed, 88 insertions(+), 49 deletions(-) diff --git a/benchmarks/600.java/601.hello-world/java/pom.xml b/benchmarks/600.java/601.hello-world/java/pom.xml index f5e1e781..d504d9bc 100644 --- a/benchmarks/600.java/601.hello-world/java/pom.xml +++ b/benchmarks/600.java/601.hello-world/java/pom.xml @@ -1,45 +1,59 @@ - - 4.0.0 + 4.0.0 - faas - benchmark - 1 + faas + benchmark + 1.0 - - 8 - 8 - UTF-8 - + + UTF-8 + ${env.JAVA_VERSION} + - - - - com.google.code.gson - gson - 2.11.0 - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - false - - - - - - + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + shade + + + + *:* + + module-info.class + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 839feed2..5c264fd9 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -397,15 +397,6 @@ def add_deployment_files(self, output_dir): for file in handlers: shutil.copy2(file, os.path.join(output_dir)) - def add_deployment_package_java(self, output_dir): - # append to the end of requirements file - packages = self._system_config.deployment_packages( - self._deployment_name, self.language_name - ) - if len(packages): - with open(os.path.join(output_dir, "requirements.txt"), "a") as out: - for package in packages: - out.write(package) def add_deployment_package_python(self, output_dir): @@ -448,6 +439,40 @@ def add_deployment_package_nodejs(self, output_dir): with open(package_config, "w") as package_file: json.dump(package_json, package_file, indent=2) + # Dependencies in system.json are in "group:artifact": version format; + # this function converts them to proper Maven blocks. + def format_maven_dependency(self, group_artifact: str, version: str) -> str: + group_id, artifact_id = group_artifact.split(":") + return f""" + + {group_id} + {artifact_id} + {version} + """ + + def add_deployment_package_java(self, output_dir): + + pom_path = os.path.join(output_dir, "pom.xml") + with open(pom_path, "r") as f: + pom_content = f.read() + + packages = self._system_config.deployment_packages(self._deployment_name, self.language_name) + + dependency_blocks = "" + if len(packages): + for key, val in packages.items(): + dependency_name = key.strip('"').strip("'") + dependency_version = val.strip('"').strip("'") + dependency_blocks += self.format_maven_dependency(dependency_name, dependency_version) + "\n" + + if "" not in pom_content: + raise ValueError("pom.xml template is missing placeholder") + + pom_content = pom_content.replace("", dependency_blocks.strip()) + + with open(pom_path, "w") as f: + f.write(pom_content) + def add_deployment_package(self, output_dir): from sebs.faas.function import Language @@ -514,7 +539,7 @@ def install_dependencies(self, output_dir): } # run Docker container to install packages - PACKAGE_FILES = {"python": "requirements.txt", "nodejs": "package.json"} + PACKAGE_FILES = {"python": "requirements.txt", "nodejs": "package.json", "java" : "pom.xml"} file = os.path.join(output_dir, PACKAGE_FILES[self.language_name]) if os.path.exists(file): try: @@ -643,7 +668,7 @@ def build( self.copy_code(self._output_dir) self.add_benchmark_data(self._output_dir) self.add_deployment_files(self._output_dir) - self.add_java_output(self._output_dir) +# self.add_java_output(self._output_dir) self.add_deployment_package(self._output_dir) self.install_dependencies(self._output_dir) From 5625faf5098593ffa0ddc49c7b82420a86d1072c Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 15 May 2025 00:12:15 +0330 Subject: [PATCH 25/28] Add dockefile and installer for java on AWS --- dockerfiles/aws/java/Dockerfile.build | 22 ++++++++++++++++++++++ dockerfiles/java_installer.sh | 8 ++++++++ 2 files changed, 30 insertions(+) create mode 100644 dockerfiles/aws/java/Dockerfile.build create mode 100644 dockerfiles/java_installer.sh diff --git a/dockerfiles/aws/java/Dockerfile.build b/dockerfiles/aws/java/Dockerfile.build new file mode 100644 index 00000000..2b2a09e0 --- /dev/null +++ b/dockerfiles/aws/java/Dockerfile.build @@ -0,0 +1,22 @@ +ARG BASE_IMAGE +FROM ${BASE_IMAGE} +ARG VERSION +ENV JAVA_VERSION=${VERSION} + + +# useradd, groupmod + maven +RUN yum install -y shadow-utils maven +ENV GOSU_VERSION 1.14 +# https://github.com/tianon/gosu/releases/tag/1.14 +# key https://keys.openpgp.org/search?q=tianon%40debian.org +RUN curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" \ + && chmod +x /usr/local/bin/gosu +RUN mkdir -p /sebs/ +COPY dockerfiles/java_installer.sh /sebs/installer.sh +COPY dockerfiles/entrypoint.sh /sebs/entrypoint.sh +RUN chmod +x /sebs/entrypoint.sh + +# useradd and groupmod is installed in /usr/sbin which is not in PATH +ENV PATH=/usr/sbin:$PATH +CMD /bin/bash /sebs/installer.sh +ENTRYPOINT ["/sebs/entrypoint.sh"] \ No newline at end of file diff --git a/dockerfiles/java_installer.sh b/dockerfiles/java_installer.sh new file mode 100644 index 00000000..1cc221f3 --- /dev/null +++ b/dockerfiles/java_installer.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd /mnt/function + +mvn clean install + + + From f784af3f748aa2df0febdca315e95b3d5cee83eb Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 15 May 2025 00:19:21 +0330 Subject: [PATCH 26/28] Adopt the output dir of Java wrappers for compatibility with Maven's structure --- sebs/benchmark.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 5c264fd9..ac429c1d 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -394,8 +394,17 @@ def add_deployment_files(self, output_dir): self._deployment_name, self.language_name ) ] + + final_path = output_dir + + # For Java, use Maven structure: put handler files in src/main/java/ + if self.language_name == 'java': + final_path = os.path.join(output_dir, 'src', 'main', 'java') + os.makedirs(final_path, exist_ok=True) # make sure the path exists + for file in handlers: - shutil.copy2(file, os.path.join(output_dir)) + shutil.copy2(file, final_path) + def add_deployment_package_python(self, output_dir): From e7fad85fd806b3b1b47a405846cebe402f5f2457 Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Thu, 15 May 2025 02:55:49 +0330 Subject: [PATCH 27/28] Update hashing to adapt to Java benchmark directory structure --- sebs/benchmark.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sebs/benchmark.py b/sebs/benchmark.py index ac429c1d..48797228 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -253,16 +253,18 @@ def hash_directory(directory: str, deployment: str, language: str): FILES = { "python": ["*.py", "requirements.txt*"], "nodejs": ["*.js", "package.json"], - "java": ["*.java", "pom.xml"], + # Use recursive Java scan since *.java files are located in subfolders. + "java": ["**/*.java", "pom.xml"], } WRAPPERS = {"python": "*.py", "nodejs": "*.js", "java": "*.java"} NON_LANG_FILES = ["*.sh", "*.json"] selected_files = FILES[language] + NON_LANG_FILES for file_type in selected_files: - for f in glob.glob(os.path.join(directory, file_type)): - path = os.path.join(directory, f) - with open(path, "rb") as opened_file: - hash_sum.update(opened_file.read()) + for f in glob.glob(os.path.join(directory, file_type), recursive=True): + if os.path.isfile(f): + path = os.path.join(directory, f) + with open(path, "rb") as opened_file: + hash_sum.update(opened_file.read()) # wrappers wrappers = project_absolute_path( "benchmarks", "wrappers", deployment, language, WRAPPERS[language] From f1796c1ca4cc43e2b45d9b9c124aeb614b0d3c2d Mon Sep 17 00:00:00 2001 From: mahlashrifi Date: Sat, 17 May 2025 08:21:42 +0330 Subject: [PATCH 28/28] Java HelloWorld benchmark now runs on AWS (requires enhancement) --- sebs/aws/aws.py | 72 ++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/sebs/aws/aws.py b/sebs/aws/aws.py index 243a6f0f..abea416e 100644 --- a/sebs/aws/aws.py +++ b/sebs/aws/aws.py @@ -134,33 +134,46 @@ def package_code( directory, language_name, language_version, architecture, benchmark, is_cached ) - CONFIG_FILES = { - "python": ["handler.py", "requirements.txt", ".python_packages"], - "nodejs": ["handler.js", "package.json", "node_modules"], - } - package_config = CONFIG_FILES[language_name] - function_dir = os.path.join(directory, "function") - os.makedirs(function_dir) - # move all files to 'function' except handler.py - for file in os.listdir(directory): - if file not in package_config: - file = os.path.join(directory, file) - shutil.move(file, function_dir) - # FIXME: use zipfile - # create zip with hidden directory but without parent directory - execute("zip -qu -r9 {}.zip * .".format(benchmark), shell=True, cwd=directory) - benchmark_archive = "{}.zip".format(os.path.join(directory, benchmark)) - self.logging.info("Created {} archive".format(benchmark_archive)) - - bytes_size = os.path.getsize(os.path.join(directory, benchmark_archive)) - mbytes = bytes_size / 1024.0 / 1024.0 - self.logging.info("Zip archive size {:2f} MB".format(mbytes)) - - return ( - os.path.join(directory, "{}.zip".format(benchmark)), - bytes_size, - container_uri, - ) + if (language_name == 'java'): + + jar_path = os.path.join(directory, "target", "benchmark-1.0.jar") + bytes_size = os.path.getsize(jar_path) + + return ( + jar_path, + bytes_size, + container_uri, + ) + + else: + # so no need to add anything here + CONFIG_FILES = { + "python": ["handler.py", "requirements.txt", ".python_packages"], + "nodejs": ["handler.js", "package.json", "node_modules"], + } + package_config = CONFIG_FILES[language_name] + function_dir = os.path.join(directory, "function") + os.makedirs(function_dir) + # move all files to 'function' except handler.py + for file in os.listdir(directory): + if file not in package_config: + file = os.path.join(directory, file) + shutil.move(file, function_dir) + # FIXME: use zipfile + # create zip with hidden directory but without parent directory + execute("zip -qu -r9 {}.zip * .".format(benchmark), shell=True, cwd=directory) + benchmark_archive = "{}.zip".format(os.path.join(directory, benchmark)) + self.logging.info("Created {} archive".format(benchmark_archive)) + + bytes_size = os.path.getsize(os.path.join(directory, benchmark_archive)) + mbytes = bytes_size / 1024.0 / 1024.0 + self.logging.info("Zip archive size {:2f} MB".format(mbytes)) + + return ( + os.path.join(directory, "{}.zip".format(benchmark)), + bytes_size, + container_uri, + ) def _map_architecture(self, architecture: str) -> str: @@ -254,7 +267,10 @@ def create_function( create_function_params["Runtime"] = "{}{}".format( language, self._map_language_runtime(language, language_runtime) ) - create_function_params["Handler"] = "handler.handler" + if language == "java": + create_function_params["Handler"] = "Handler::handleRequest" + else: + create_function_params["Handler"] = "handler.handler" create_function_params = { k: v for k, v in create_function_params.items() if v is not None