Josiah Noel 4 місяців тому
батько
коміт
fa5004a8d3

+ 81 - 0
frameworks/Java/avaje-jex/README.md

@@ -0,0 +1,81 @@
+# Avaje Jex Benchmarking Test
+
+## Important Libraries
+The tests were run with:
+* [Java 24](https://openjdk.java.net)
+* [Avaje Http 3.3](https://github.com/avaje/avaje-http)
+* [Avaje Inject 11.5](https://github.com/avaje/avaje-inject)
+* [Avaje Jex 3.2](https://github.com/avaje/avaje-jex)
+* [HikariCP 6.3.0](https://github.com/brettwooldridge/HikariCP)
+* [jstachio 1.3.7](https://github.com/jstachio/jstachio)
+
+[Avaje Jex](https://avaje.io) is a micro web framework for Java's built-in `jdk.httpserver` API.
+
+```java
+    Jex.create()
+        .get("/", ctx -> ctx.text("hello"))
+        .start();
+```
+
+## Test Implementation Source Code
+
+### Plain Text Test
+* [Plain test source](src/main/java/benchmark/Main.java)
+
+### JSON Encoding Test
+* [JSON test source](src/main/java/benchmark/Main.java)
+
+### Single Query Test
+* [Single query test source](src/main/java/benchmark/DatabaseController.java)
+
+### Multiple Queries Test
+* [Multiple queries test source](src/main/java/benchmark/DatabaseController.java)
+
+### Database Update Test
+* [Database update test source](src/main/java/benchmark/DatabaseController.java)
+
+### Fortunes Test
+* [Fortunes test source](src/main/java/benchmark/DatabaseController.java)
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/queries?queries=
+
+### UPDATE
+
+http://localhost:8080/updates?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes
+
+## build
+
+### java's httpserver impl
+
+ `mvn clean package`
+
+### robaho's httpserver impl
+
+ `mvn clean package -P robaho`
+
+### jetty's httpserver impl
+
+ `mvn clean package -P jetty`
+    
+## run
+
+ `java -p ./target/modules/ -m avaje.techempower`

+ 8 - 0
frameworks/Java/avaje-jex/avaje-jex-jetty.dockerfile

@@ -0,0 +1,8 @@
+FROM maven:3.9.9-eclipse-temurin-24 AS build
+WORKDIR /avaje-jex
+COPY pom.xml pom.xml
+COPY src src
+RUN mvn package -q -P jetty
+EXPOSE 8080
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-p", "./target/modules/", "-m", "avaje.techempower"]

+ 8 - 0
frameworks/Java/avaje-jex/avaje-jex-robaho.dockerfile

@@ -0,0 +1,8 @@
+FROM maven:3.9.9-eclipse-temurin-24 AS build
+WORKDIR /avaje-jex
+COPY pom.xml pom.xml
+COPY src src
+RUN mvn package -q -P robaho
+EXPOSE 8080
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-p", "./target/modules/", "-m", "avaje.techempower"]

+ 8 - 0
frameworks/Java/avaje-jex/avaje-jex.dockerfile

@@ -0,0 +1,8 @@
+FROM maven:3.9.9-eclipse-temurin-24 AS build
+WORKDIR /avaje-jex
+COPY pom.xml pom.xml
+COPY src src
+RUN mvn package -q
+EXPOSE 8080
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-p", "./target/modules/", "-m", "avaje.techempower"]

+ 76 - 0
frameworks/Java/avaje-jex/benchmark_config.json

@@ -0,0 +1,76 @@
+{
+  "framework": "avaje-jex",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "avaje-jex",
+        "language": "Java",
+        "flavor": "None",
+        "platform": "httpserver",
+        "database": "Postgres",
+        "database_os": "Linux",
+        "orm": "Raw",
+        "webserver": "None",
+        "os": "Linux",
+        "notes": "avaje-jex using jdk.httpserver",
+        "display_name": "avaje-jex",
+        "versus": "httpserver"
+      },
+      "robaho": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "avaje-jex",
+        "language": "Java",
+        "flavor": "None",
+        "platform": "robaho",
+        "database": "Postgres",
+        "database_os": "Linux",
+        "orm": "Raw",
+        "webserver": "None",
+        "os": "Linux",
+        "notes": "avaje-jex using robaho's httpserver",
+        "display_name": "avaje-jex-robaho",
+        "versus": "robaho-httpserver"
+      },
+      "jetty": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "avaje-jex",
+        "language": "Java",
+        "flavor": "None",
+        "platform": "Jetty",
+        "database": "Postgres",
+        "database_os": "Linux",
+        "orm": "Raw",
+        "webserver": "None",
+        "os": "Linux",
+        "notes": "avaje-jex using Jetty",
+        "display_name": "avaje-jex-jetty",
+        "versus": "jetty"
+      }
+    }
+  ]
+}

+ 53 - 0
frameworks/Java/avaje-jex/config.toml

@@ -0,0 +1,53 @@
+[framework]
+name = "avaje-jex"
+
+[main]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
+approach = "Realistic"
+classification = "Micro"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "jdk-httpserver"
+webserver = "None"
+versus = "httpserver"
+
+[robaho]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
+approach = "Realistic"
+classification = "Micro"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "robaho-httpserver"
+webserver = "None"
+versus = "robaho-httpserver"
+
+[jetty]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
+approach = "Realistic"
+classification = "Micro"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Jetty"
+webserver = "None"
+versus = "jetty"

+ 173 - 0
frameworks/Java/avaje-jex/pom.xml

@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>io.avaje</groupId>
+    <artifactId>avaje-jex-parent</artifactId>
+    <version>3.2</version>
+  </parent>
+  <groupId>benchmark</groupId>
+  <artifactId>avaje-jex</artifactId>
+  <version>1.0</version>
+
+  <properties>
+    <maven.compiler.release>24</maven.compiler.release>
+    <slf4j.version>2.0.17</slf4j.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-config</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-http-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-inject</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-jex</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-jsonb</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.zaxxer</groupId>
+      <artifactId>HikariCP</artifactId>
+      <version>6.3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <version>42.7.5</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.jstach</groupId>
+      <artifactId>jstachio</artifactId>
+      <version>1.3.7</version>
+    </dependency>
+    
+    <!-- Annotation Processors -->
+    <dependency>
+      <groupId>io.jstach</groupId>
+      <artifactId>jstachio-apt</artifactId>
+      <version>1.3.7</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-http-jex-generator</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-inject-generator</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.avaje</groupId>
+      <artifactId>avaje-jsonb-generator</artifactId>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>io.avaje</groupId>
+        <artifactId>avaje-provides-maven-plugin</artifactId>
+        <version>2.3</version>
+      </plugin>
+      
+      <plugin>
+        <groupId>com.spotify.fmt</groupId>
+        <artifactId>fmt-maven-plugin</artifactId>
+        <version>2.25</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>format</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- Create Modular Jar and copy dependencies -->
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <outputDirectory>${project.build.directory}/modules</outputDirectory>
+          <archive>
+            <manifest>
+              <mainClass>benchmark.Main</mainClass>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-modules</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/modules</outputDirectory>
+              <includeScope>runtime</includeScope>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>robaho</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>io.github.robaho</groupId>
+          <artifactId>httpserver</artifactId>
+        </dependency>
+      </dependencies>
+    </profile>
+    <profile>
+      <id>jetty</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-http-spi</artifactId>
+          <version>12.0.19</version>
+        </dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+</project>

+ 57 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/DatabaseController.java

@@ -0,0 +1,57 @@
+package benchmark;
+
+import benchmark.model.Fortune;
+import benchmark.model.FortuneTemplate;
+import benchmark.model.World;
+import benchmark.repository.DbService;
+import io.avaje.http.api.Controller;
+import io.avaje.http.api.Get;
+import java.sql.SQLException;
+import java.util.List;
+
+@Controller
+public class DatabaseController {
+
+  private static final int MIN_QUERIES = 1;
+  private static final int MAX_QUERIES = 500;
+
+  private final DbService dbService;
+
+  public DatabaseController(DbService dbService) {
+
+    this.dbService = dbService;
+  }
+
+  @Get("/db")
+  public World handleSingleDbQuery() throws SQLException {
+    return dbService.getWorld(1).get(0);
+  }
+
+  @Get("/queries")
+  public List<World> handleMultipleDbQueries(String queries) throws SQLException {
+    int num = getBoundedRowNumber(queries);
+    return dbService.getWorld(num);
+  }
+
+  @Get("/fortunes")
+  public FortuneTemplate handleFortunes() throws SQLException {
+    List<Fortune> fortuneList = dbService.getFortune();
+    return new FortuneTemplate(fortuneList);
+  }
+
+  @Get("/updates")
+  public List<World> handleUpdates(String queries) throws SQLException {
+    int num = getBoundedRowNumber(queries);
+    return dbService.updateWorld(num);
+  }
+
+  private static int getBoundedRowNumber(String number) {
+    int num;
+    try {
+      num = Integer.parseInt(number);
+    } catch (NumberFormatException e) {
+      num = MIN_QUERIES;
+    }
+    return Math.max(MIN_QUERIES, Math.min(num, MAX_QUERIES));
+  }
+}

+ 27 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/Main.java

@@ -0,0 +1,27 @@
+package benchmark;
+
+import io.avaje.inject.BeanScope;
+import io.avaje.jex.Jex;
+import io.avaje.jex.Routing.HttpService;
+import io.avaje.jsonb.Jsonb;
+import io.avaje.jsonb.Types;
+import java.util.Map;
+
+public class Main {
+
+  public static void main(String[] args) {
+
+    var beans = BeanScope.builder().build();
+    var routes = beans.list(HttpService.class);
+    var type = Jsonb.builder().build().type(Types.mapOf(String.class));
+
+    Jex.create()
+        .config(c -> c.compression().disableCompression())
+        .get("/plaintext", ctx -> ctx.text("Hello, World!"))
+        .get("/json", ctx -> ctx.jsonb(type, Map.of("message", "Hello, World!")))
+        .before(ctx -> ctx.header("Server", "avaje-jex"))
+        .error(Exception.class, (ctx, _) -> ctx.status(503).text("503 Service Unavailable"))
+        .routing(routes)
+        .start();
+  }
+}

+ 6 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/model/Fortune.java

@@ -0,0 +1,6 @@
+package benchmark.model;
+
+import io.avaje.jsonb.Json;
+
+@Json
+public record Fortune(int id, String message) {}

+ 7 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/model/FortuneTemplate.java

@@ -0,0 +1,7 @@
+package benchmark.model;
+
+import io.jstach.jstache.JStache;
+import java.util.List;
+
+@JStache(path = "fortunes.mustache")
+public record FortuneTemplate(List<Fortune> fortunes) {}

+ 38 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/model/World.java

@@ -0,0 +1,38 @@
+package benchmark.model;
+
+import io.avaje.jsonb.Json;
+
+@Json
+public class World {
+
+  private int id;
+  private int randomNumber;
+
+  public World() {}
+
+  public World(int id, int randomNumber) {
+    this.id = id;
+    this.randomNumber = randomNumber;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public int getRandomNumber() {
+    return randomNumber;
+  }
+
+  public void setRandomNumber(int randomNumber) {
+    this.randomNumber = randomNumber;
+  }
+
+  @Override
+  public String toString() {
+    return "World{" + "id=" + id + ", randomNumber=" + randomNumber + '}';
+  }
+}

+ 33 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/repository/DbService.java

@@ -0,0 +1,33 @@
+package benchmark.repository;
+
+import benchmark.model.Fortune;
+import benchmark.model.World;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+
+public interface DbService {
+
+  int MIN_RANDOM_NUMBER = 1;
+  int MAX_RANDOM_NUMBER_PLUS_ONE = 10001;
+  int defaultFortuneId = 0;
+  String defaultFortuneMessage = "Additional fortune added at request time.";
+
+  List<World> getWorld(int num) throws SQLException;
+
+  List<Fortune> getFortune() throws SQLException;
+
+  List<World> updateWorld(int num) throws SQLException;
+
+  default int getRandomNumber() {
+    return ThreadLocalRandom.current().nextInt(MIN_RANDOM_NUMBER, MAX_RANDOM_NUMBER_PLUS_ONE);
+  }
+
+  default Set<Integer> getRandomNumberSet(int num) {
+    Set<Integer> set = new HashSet<>();
+    while (set.size() < num) set.add(getRandomNumber());
+    return set;
+  }
+}

+ 29 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/repository/HikariFactory.java

@@ -0,0 +1,29 @@
+package benchmark.repository;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import io.avaje.config.Config;
+import io.avaje.inject.Bean;
+import io.avaje.inject.Factory;
+import javax.sql.DataSource;
+
+@Factory
+public class HikariFactory {
+
+  @Bean
+  DataSource dataSource() {
+    int maxPoolSize;
+    var env = System.getenv("BENCHMARK_ENV");
+    if (Config.get("physicalTag").equals(env)) {
+      maxPoolSize = Config.getInt("postgresPhysicalPoolSize");
+    } else if (Config.get("cloudTag").equals(env)) {
+      maxPoolSize = Config.getInt("postgresCloudPoolSize");
+    } else {
+      maxPoolSize = Config.getInt("postgresDefaultPoolSize");
+    }
+
+    var hikari = new HikariDataSource(new HikariConfig("hikari.properties"));
+    hikari.setMaximumPoolSize(maxPoolSize);
+    return hikari;
+  }
+}

+ 95 - 0
frameworks/Java/avaje-jex/src/main/java/benchmark/repository/JDBCDbService.java

@@ -0,0 +1,95 @@
+package benchmark.repository;
+
+import benchmark.model.Fortune;
+import benchmark.model.World;
+import jakarta.inject.Singleton;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import javax.sql.DataSource;
+
+@Singleton
+public class JDBCDbService implements DbService {
+
+  private final DataSource datasource;
+
+  public JDBCDbService(DataSource connectionFactory) {
+    this.datasource = connectionFactory;
+  }
+
+  @Override
+  public List<World> getWorld(int num) {
+
+    String select = "select id, randomNumber from World where id = ?";
+    List<World> worldList = new ArrayList<>();
+
+    try (Connection conn = datasource.getConnection();
+        PreparedStatement pstm = conn.prepareStatement(select)) {
+
+      for (int randomId : getRandomNumberSet(num)) {
+        pstm.setInt(1, randomId);
+        try (ResultSet rs = pstm.executeQuery()) {
+          rs.next();
+          worldList.add(new World(rs.getInt("id"), rs.getInt("randomNumber")));
+        }
+      }
+    } catch (SQLException e) {
+      throw new RuntimeException(e);
+    }
+
+    return worldList;
+  }
+
+  @Override
+  public List<Fortune> getFortune() throws SQLException {
+
+    String select = "select id, message from Fortune";
+    List<Fortune> fortuneList = new ArrayList<>();
+
+    try (Connection conn = datasource.getConnection();
+        PreparedStatement pstm = conn.prepareStatement(select);
+        ResultSet rs = pstm.executeQuery()) {
+
+      while (rs.next()) {
+        fortuneList.add(new Fortune(rs.getInt("id"), rs.getString("message")));
+      }
+      fortuneList.add(new Fortune(defaultFortuneId, defaultFortuneMessage));
+    }
+
+    fortuneList.sort(Comparator.comparing(Fortune::message));
+    return fortuneList;
+  }
+
+  @Override
+  public List<World> updateWorld(int num) throws SQLException {
+
+    String update = "update World set randomNumber = ? where id = ?";
+    List<World> worldList = getWorld(num);
+
+    try (Connection conn = datasource.getConnection();
+        PreparedStatement pstm = conn.prepareStatement(update)) {
+
+      conn.setAutoCommit(false);
+      for (World world : worldList) {
+        int newRandomNumber;
+        do {
+          newRandomNumber = getRandomNumber();
+        } while (newRandomNumber == world.getRandomNumber());
+
+        pstm.setInt(1, newRandomNumber);
+        pstm.setInt(2, world.getId());
+        pstm.addBatch();
+
+        world.setRandomNumber(newRandomNumber);
+      }
+      pstm.executeBatch();
+      conn.commit();
+    }
+
+    return worldList;
+  }
+}

+ 19 - 0
frameworks/Java/avaje-jex/src/main/java/module-info.java

@@ -0,0 +1,19 @@
+module avaje.techempower {
+  // jdk.httpserver wrapper
+  requires jdk.httpserver;
+  requires io.avaje.jex;
+  /// Postgres driver and pool
+  requires java.sql;
+  requires org.postgresql.jdbc;
+  requires com.zaxxer.hikari;
+  // template engine
+  requires io.jstach.jstachio;
+  /// Configuration
+  requires io.avaje.config;
+  // controller generation
+  requires io.avaje.http.api;
+  /// Dependency Injection
+  requires io.avaje.inject;
+  /// Json
+  requires io.avaje.jsonb;
+}

+ 6 - 0
frameworks/Java/avaje-jex/src/main/resources/application.properties

@@ -0,0 +1,6 @@
+physicalTag=Citrine
+cloudTag=Azure
+
+postgresPhysicalPoolSize=56
+postgresCloudPoolSize=16
+postgresDefaultPoolSize=10

+ 20 - 0
frameworks/Java/avaje-jex/src/main/resources/fortunes.mustache

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Fortunes</title>
+  </head>
+  <body>
+    <table>
+      <tr>
+        <th>id</th>
+        <th>message</th>
+      </tr>
+      {{#fortunes}}
+      <tr>
+        <td>{{id}}</td>
+        <td>{{message}}</td>
+      </tr>
+      {{/fortunes}}
+    </table>
+  </body>
+</html>

+ 7 - 0
frameworks/Java/avaje-jex/src/main/resources/hikari.properties

@@ -0,0 +1,7 @@
+dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
+dataSource.serverName=tfb-database
+dataSource.portNumber=5432
+dataSource.user=benchmarkdbuser
+dataSource.password=benchmarkdbpass
+dataSource.databaseName=hello_world
+