Browse Source

Add a benchmark for spring-mvc with mongodb (#4393)

* Add a benchmark for spring-mvc with mongodb

* Use jdk 11 in spring-mongo.dockerfile

* Use save instead findAndModify
Souen Mazouin 6 years ago
parent
commit
ebaa824e1e

+ 7 - 1
frameworks/Java/spring/README.md

@@ -2,10 +2,16 @@
 
 This is the Spring MVC portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
-An embedded tomcat is used for the web server, with nearly everything configured with default settings. The only thing changed is Hikari can use up to (2 * cores count) connections (the default is 10). See [About-Pool-Sizing](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing)
+An embedded tomcat is used for the web server, with nearly everything configured with default settings.
+The only thing changed is Hikari can use up to (2 * cores count) connections (the default is 10).
+See [About-Pool-Sizing](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing)
 
 Tomcat use a fixed thread pool that can grow up to 200 threads.
 
+There are two implementations :
+* For postgresql access, JdbcTemplate is used. See [JdbcDbRepository](src/main/java/hello/JdbcDbRepository.java).
+* For mongoDB access, MongoTemplate is used. See [MongoDbRepository](src/main/java/hello/MongoDbRepository.java).
+
 ### Plaintext Test
 
 * [Plaintext test source](src/main/java/hello/HelloController.java)

+ 22 - 1
frameworks/Java/spring/benchmark_config.json

@@ -20,9 +20,30 @@
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "spring",
+      "display_name": "spring-jdbc",
       "notes": "",
       "versus": ""
+    },
+    "mongo": {
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mongodb",
+      "framework": "spring",
+      "language": "Java",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "Tomcat",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "spring-mongo",
+      "notes": "",
+      "versus": "spring"
     }
   }]
 }

+ 4 - 0
frameworks/Java/spring/pom.xml

@@ -28,6 +28,10 @@
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-mongodb</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-jdbc</artifactId>

+ 10 - 0
frameworks/Java/spring/spring-mongo.dockerfile

@@ -0,0 +1,10 @@
+FROM maven:3.6.0-jdk-11-slim as maven
+WORKDIR /spring
+COPY src src
+COPY pom.xml pom.xml
+RUN mvn package -q
+
+FROM openjdk:11-jre-slim
+WORKDIR /spring
+COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=mongo"]

+ 1 - 1
frameworks/Java/spring/spring.dockerfile

@@ -7,4 +7,4 @@ RUN mvn package -q
 FROM openjdk:11-jre-slim
 WORKDIR /spring
 COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-jar", "app.jar"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc,postgres"]

+ 3 - 1
frameworks/Java/spring/src/main/java/hello/App.java

@@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Profile;
 
 import javax.sql.DataSource;
 
@@ -17,8 +18,9 @@ public class App {
     }
 
     @Bean
+    @Profile("jdbc")
     public DataSource datasource(DataSourceProperties dataSourceProperties) {
-        HikariDataSource dataSource = (HikariDataSource) dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
+        HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
         dataSource.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() * 2);
 
         return dataSource;

+ 10 - 37
frameworks/Java/spring/src/main/java/hello/HelloController.java → frameworks/Java/spring/src/main/java/hello/controller/HelloController.java

@@ -1,4 +1,4 @@
-package hello;
+package hello.controller;
 
 import static java.util.Comparator.comparing;
 
@@ -6,10 +6,11 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ThreadLocalRandom;
+
+import hello.model.Fortune;
+import hello.model.World;
+import hello.repository.DbRepository;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -20,7 +21,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
 public final class HelloController {
 
   @Autowired
-  JdbcTemplate jdbcTemplate;
+  private DbRepository dbRepository;
 
   @RequestMapping("/plaintext")
   @ResponseBody
@@ -36,6 +37,7 @@ public final class HelloController {
 
   @RequestMapping("/db")
   @ResponseBody
+
   World db() {
     return randomWorld();
   }
@@ -54,11 +56,7 @@ public final class HelloController {
     var worlds = new World[parseQueryCount(queries)];
     Arrays.setAll(worlds, i -> randomWorld());
     for (var world : worlds) {
-      world.randomNumber = randomWorldNumber();
-      jdbcTemplate.update(
-          "UPDATE world SET randomnumber = ? WHERE id = ?",
-          world.randomNumber,
-          world.id);
+      dbRepository.updateWorld(world, randomWorldNumber());
     }
     return worlds;
   }
@@ -66,10 +64,7 @@ public final class HelloController {
   @RequestMapping("/fortunes")
   @ModelAttribute("fortunes")
   List<Fortune> fortunes() {
-    var fortunes =
-        jdbcTemplate.query(
-            "SELECT * FROM fortune",
-            (rs, rn) -> new Fortune(rs.getInt("id"), rs.getString("message")));
+    var fortunes = dbRepository.fortunes();
 
     fortunes.add(new Fortune(0, "Additional fortune added at request time."));
     fortunes.sort(comparing(fortune -> fortune.message));
@@ -77,10 +72,7 @@ public final class HelloController {
   }
 
   private World randomWorld() {
-    return jdbcTemplate.queryForObject(
-        "SELECT * FROM world WHERE id = ?",
-        (rs, rn) -> new World(rs.getInt("id"), rs.getInt("randomnumber")),
-        randomWorldNumber());
+    return dbRepository.getWorld(randomWorldNumber());
   }
 
   private static int randomWorldNumber() {
@@ -100,23 +92,4 @@ public final class HelloController {
     return Math.min(500, Math.max(1, parsedValue));
   }
 
-  public static final class Fortune {
-    public int id;
-    public String message;
-
-    public Fortune(int id, String message) {
-      this.id = id;
-      this.message = message;
-    }
-  }
-
-  public static final class World {
-    public int id;
-    public int randomNumber;
-
-    public World(int id, int randomNumber) {
-      this.id = id;
-      this.randomNumber = randomNumber;
-    }
-  }
 }

+ 24 - 0
frameworks/Java/spring/src/main/java/hello/model/Fortune.java

@@ -0,0 +1,24 @@
+package hello.model;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document
+public final class Fortune {
+  @Id
+  public int id;
+  public String message;
+
+  public Fortune(int id, String message) {
+    this.id = id;
+    this.message = message;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+}

+ 19 - 0
frameworks/Java/spring/src/main/java/hello/model/World.java

@@ -0,0 +1,19 @@
+package hello.model;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+@Document
+public final class World {
+
+  @Id
+  public int id;
+  @Field("randomNumber")
+  public int randomnumber;
+
+  public World(int id, int randomnumber) {
+    this.id = id;
+    this.randomnumber = randomnumber;
+  }
+}

+ 14 - 0
frameworks/Java/spring/src/main/java/hello/repository/DbRepository.java

@@ -0,0 +1,14 @@
+package hello.repository;
+
+import hello.model.Fortune;
+import hello.model.World;
+
+import java.util.List;
+
+public interface DbRepository {
+    World getWorld(int id);
+
+    World updateWorld(World world, int randomNumber);
+
+    List<Fortune> fortunes();
+}

+ 52 - 0
frameworks/Java/spring/src/main/java/hello/repository/JdbcDbRepository.java

@@ -0,0 +1,52 @@
+package hello.repository;
+
+import hello.model.Fortune;
+import hello.model.World;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Profile;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+@Profile("jdbc")
+public class JdbcDbRepository implements DbRepository {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final JdbcTemplate jdbcTemplate;
+
+    public JdbcDbRepository(JdbcTemplate jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+    @Override
+    public World getWorld(int id) {
+        log.debug("getWorld({})", id);
+        return jdbcTemplate.queryForObject(
+                "SELECT * FROM world WHERE id = ?",
+                (rs, rn) -> new World(rs.getInt("id"), rs.getInt("randomnumber")),
+                id);
+    }
+
+    private World updateWorld(World world) {
+        jdbcTemplate.update(
+                "UPDATE world SET randomnumber = ? WHERE id = ?",
+                world.randomnumber,
+                world.id);
+        return world;
+    }
+
+    @Override
+    public World updateWorld(World world, int randomNumber) {
+        world.randomnumber = randomNumber;
+        return updateWorld(world);
+    }
+
+    @Override
+    public List<Fortune> fortunes() {
+        return jdbcTemplate.query(
+                "SELECT * FROM fortune",
+                (rs, rn) -> new Fortune(rs.getInt("id"), rs.getString("message")));
+    }
+}

+ 41 - 0
frameworks/Java/spring/src/main/java/hello/repository/MongoDbRepository.java

@@ -0,0 +1,41 @@
+package hello.repository;
+
+import hello.model.Fortune;
+import hello.model.World;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Profile;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+@Profile("mongo")
+public class MongoDbRepository implements DbRepository {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final MongoTemplate mongoTemplate;
+
+    public MongoDbRepository(MongoTemplate mongoTemplate) {
+        this.mongoTemplate = mongoTemplate;
+    }
+
+
+    @Override
+    public World getWorld(int id) {
+        log.debug("getWorld({})", id);
+        return mongoTemplate.findById(id, World.class);
+    }
+
+
+    @Override
+    public World updateWorld(World world, int randomNumber) {
+        world.randomnumber = randomNumber;
+        return mongoTemplate.save(world);
+    }
+
+    @Override
+    public List<Fortune> fortunes() {
+        return mongoTemplate.findAll(Fortune.class);
+    }
+}

+ 0 - 4
frameworks/Java/spring/src/main/resources/application.properties

@@ -1,4 +0,0 @@
-server.server-header=Spring
-spring.datasource.url=jdbc:postgresql://tfb-database:5432/hello_world
-spring.datasource.username=benchmarkdbuser
-spring.datasource.password=benchmarkdbpass

+ 30 - 0
frameworks/Java/spring/src/main/resources/application.yml

@@ -0,0 +1,30 @@
+---
+spring:
+  profiles: jdbc
+  datasource:
+    url: jdbc:postgresql://${database.host}:${database.port}/${database.name}
+    username: ${database.username}
+    password: ${database.password}
+
+database:
+  name: hello_world
+  host: tfb-database
+  port: 5432
+  username: benchmarkdbuser
+  password: benchmarkdbpass
+
+---
+spring:
+  profiles: mongo
+
+spring.data.mongodb:
+  host: tfb-database
+  port: 27017
+  database: hello_world
+
+---
+spring:
+  profiles:
+    active: jdbc
+
+server.server-header: Spring