Browse Source

Update Micronaut benchmark for Micronaut 1.0.1 (#4205)

Graeme Rocher 6 years ago
parent
commit
3a7497d700

+ 6 - 1
frameworks/Java/micronaut/pom.xml

@@ -9,7 +9,7 @@
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
         <exec.mainClass>benchmark.Application</exec.mainClass>
-        <micronaut.version>1.0.0</micronaut.version>
+        <micronaut.version>1.0.1</micronaut.version>
     </properties>
     <repositories>
         <repository>
@@ -63,6 +63,11 @@
             <artifactId>micronaut-runtime</artifactId>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-views</artifactId>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>

+ 28 - 77
frameworks/Java/micronaut/src/main/java/benchmark/Database.java

@@ -2,118 +2,69 @@ package benchmark;
 
 import benchmark.entity.Fortune;
 import benchmark.entity.World;
+import benchmark.repository.DbRepository;
 import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.Mustache;
 import io.micronaut.http.annotation.Controller;
 import io.micronaut.http.annotation.Get;
 import io.micronaut.http.annotation.QueryValue;
-import io.reactiverse.reactivex.pgclient.*;
+import io.micronaut.views.View;
 import io.reactivex.Flowable;
 import io.reactivex.Single;
 
-import javax.annotation.Nullable;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Comparator;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
 
+import static java.util.Comparator.comparing;
+
 @Controller("/")
 public class Database {
 
-    private static final String UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2";
-    private static final String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1";
-    private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE";
-    private final Mustache mustache;
-    private final PgPool pgPool;
 
-    public Database(PgPool pgPool) {
-        this.pgPool = pgPool;
-        this.mustache = new DefaultMustacheFactory().compile("fortunes.mustache");
+    private final DbRepository dbRepository;
+
+    public Database(DbRepository dbRepository) {
+        this.dbRepository = dbRepository;
     }
 
     @Get("/db")
     public Single<World> db() {
-        return pgPool.rxGetConnection()
-                .doAfterSuccess(PgConnection::close)
-                .flatMap((connection) -> findRandomWorld(connection.rxPrepare(SELECT_WORLD), null));
+        return dbRepository.getWorld(randomWorldNumber());
     }
 
     @Get("/queries")
     public Single<List<World>> queries(@QueryValue String queries) {
-        return findRandomWorlds(parseQueryCount(queries)).toList();
+        Flowable<World>[] worlds = new Flowable[parseQueryCount(queries)];
+        Arrays.setAll(worlds, i -> db().toFlowable());
+
+        return Flowable.merge(Arrays.asList(worlds)).toList();
     }
 
     @Get(value = "/fortunes", produces = "text/html;charset=utf-8")
-    public Writer fortune() {
-        return mustache.execute(new StringWriter(), findFortunes());
+    @View("fortunes")
+    public Single<List<Fortune>> fortune() {
+        return dbRepository.fortunes().toList().flatMap(fortunes -> {
+            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+            fortunes.sort(comparing(fortune -> fortune.message));
+            return Single.just(fortunes);
+        });
     }
 
     @Get("/updates")
     public Single<List<World>> updates(@QueryValue String queries) {
-        return updateRandomWorlds(parseQueryCount(queries));
-    }
+        Flowable<World>[] worlds = new Flowable[parseQueryCount(queries)];
 
-    private Single<World> findRandomWorld(Single<PgPreparedQuery> preparedQuery, @Nullable Integer id) {
-        return preparedQuery
-                .flatMap(query -> query.rxExecute(Tuple.of(id != null ? id : nextNumber())))
-                .map((result) -> {
-                    Row row = result.iterator().next();
-                    return new World(row.getInteger("id"), row.getInteger("randomnumber"));
-                });
-    }
-
-    private int nextNumber() {
-        return ThreadLocalRandom.current().nextInt(10000) + 1;
-    }
+        Arrays.setAll(worlds, i ->
+                dbRepository.findAndUpdateWorld(randomWorldNumber(), randomWorldNumber()).toFlowable()
+        );
 
-    private Flowable<World> findRandomWorlds(int count) {
-        return pgPool.rxGetConnection()
-                .toFlowable()
-                .doAfterNext(PgConnection::close)
-                .flatMap((connection) -> findRandomWorlds(connection.rxPrepare(SELECT_WORLD), count));
+        return Flowable.merge(Arrays.asList(worlds)).toList();
     }
 
-    private Flowable<World> findRandomWorlds(Single<PgPreparedQuery> preparedQuery, int count) {
-        return Flowable.range(1, count)
-                .flatMap(i -> findRandomWorld(preparedQuery, i).toFlowable());
+    private int randomWorldNumber() {
+        return 1 + ThreadLocalRandom.current().nextInt(10000);
     }
-
-    private Single<List<World>> updateRandomWorlds(int count) {
-        return pgPool.rxGetConnection()
-                .doAfterSuccess(PgConnection::close)
-                .flatMap(connection -> findRandomWorlds(connection.rxPrepare(SELECT_WORLD), count)
-                        .doOnNext(world -> world.setRandomNumber(nextNumber()))
-                        .toList(count)
-                        .flatMap(worlds -> {
-                            int worldCount = worlds.size();
-                            List<Tuple> tuples = new ArrayList<>(worldCount);
-                            for (World world : worlds) {
-                                tuples.add(Tuple.of(world.getRandomNumber(), world.getId()));
-                            }
-                            return connection.rxPreparedBatch(UPDATE_WORLD, tuples)
-                                    .map(pgRowSet -> worlds);
-                        }));
-    }
-
-    private List<Fortune> findFortunes() {
-        return pgPool.rxGetConnection()
-                .doAfterSuccess(PgConnection::close)
-                .flatMap((connection) -> connection.rxPreparedQuery(SELECT_FORTUNE).map((result) -> {
-                    PgIterator iterator = result.iterator();
-                    List<Fortune> fortunes = new ArrayList<>();
-                    while (iterator.hasNext()) {
-                        Row row = iterator.next();
-                        fortunes.add(new Fortune(row.getInteger("id"), row.getString("message")));
-                    }
-                    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-                    fortunes.sort(Comparator.comparing(Fortune::getMessage));
-
-                    return fortunes;
-                })).blockingGet();
-    }
-
     private int parseQueryCount(String textValue) {
         if (textValue == null) {
             return 1;

+ 2 - 2
frameworks/Java/micronaut/src/main/java/benchmark/entity/Fortune.java

@@ -7,8 +7,8 @@ import java.util.Objects;
  */
 public final class Fortune {
 
-    private int id;
-    private String message;
+    public int id;
+    public String message;
 
     public Fortune() {
         super();

+ 2 - 2
frameworks/Java/micronaut/src/main/java/benchmark/entity/World.java

@@ -7,8 +7,8 @@ import java.util.Objects;
  */
 public final class World {
 
-    private int id;
-    private int randomNumber;
+    public int id;
+    public int randomNumber;
 
     public World() {
         super();

+ 14 - 0
frameworks/Java/micronaut/src/main/java/benchmark/repository/DbRepository.java

@@ -0,0 +1,14 @@
+package benchmark.repository;
+
+import benchmark.entity.Fortune;
+import benchmark.entity.World;
+import io.reactivex.Flowable;
+import io.reactivex.Single;
+
+public interface DbRepository {
+    Single<World> getWorld(int id);
+
+    Single<World> findAndUpdateWorld(int id, int randomNumber);
+
+    Flowable<Fortune> fortunes();
+}

+ 53 - 0
frameworks/Java/micronaut/src/main/java/benchmark/repository/PgClientConfig.java

@@ -0,0 +1,53 @@
+package benchmark.repository;
+
+import io.micronaut.context.annotation.ConfigurationProperties;
+
+@ConfigurationProperties("database")
+public class PgClientConfig {
+    private String name;
+    private String host;
+    private int port;
+    private String username;
+    private String password;
+
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}

+ 76 - 0
frameworks/Java/micronaut/src/main/java/benchmark/repository/PgClientDbRepository.java

@@ -0,0 +1,76 @@
+package benchmark.repository;
+
+import benchmark.entity.Fortune;
+import benchmark.entity.World;
+import io.reactiverse.pgclient.PgIterator;
+import io.reactiverse.pgclient.Row;
+import io.reactiverse.pgclient.Tuple;
+import io.reactivex.BackpressureStrategy;
+import io.reactivex.Flowable;
+import io.reactivex.Single;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Singleton;
+
+@Singleton
+public class PgClientDbRepository implements DbRepository {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final PgClients pgClients;
+
+    public PgClientDbRepository(PgClients pgClients) {
+        this.pgClients = pgClients;
+    }
+
+    @Override
+    public Single<World> getWorld(int id) {
+        return Single.create(sink ->
+                pgClients.getOne().preparedQuery("SELECT * FROM world WHERE id = $1", Tuple.of(id), ar -> {
+                    if (ar.failed()) {
+                        sink.onError(ar.cause());
+                    } else {
+
+                        final Row row = ar.result().iterator().next();
+
+                        World world = new World(row.getInteger(0), row.getInteger(1));
+                        sink.onSuccess(world);
+                    }
+                }));
+    }
+
+    private Single<World> updateWorld(World world) {
+        return Single.create(sink -> pgClients.getOne().preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2", Tuple.of(world.randomNumber, world.id), ar -> {
+            if (ar.failed()) {
+                sink.onError(ar.cause());
+            } else {
+                sink.onSuccess(world);
+            }
+        }));
+    }
+
+    @Override
+    public Single<World> findAndUpdateWorld(int id, int randomNumber) {
+        return getWorld(id).flatMap(world -> {
+            world.randomNumber = randomNumber;
+            return updateWorld(world);
+        });
+    }
+
+    @Override
+    public Flowable<Fortune> fortunes() {
+        return Flowable.create(sink ->
+                pgClients.getOne().preparedQuery("SELECT * FROM fortune", ar -> {
+                    if (ar.failed()) {
+                        sink.onError(ar.cause());
+                        return;
+                    }
+
+                    PgIterator resultSet = ar.result().iterator();
+                    while (resultSet.hasNext()) {
+                        Tuple row = resultSet.next();
+                        sink.onNext(new Fortune(row.getInteger(0), row.getString(1)));
+                    }
+                    sink.onComplete();
+                }), BackpressureStrategy.BUFFER);
+    }
+}

+ 53 - 0
frameworks/Java/micronaut/src/main/java/benchmark/repository/PgClientFactory.java

@@ -0,0 +1,53 @@
+package benchmark.repository;
+
+import io.micronaut.context.annotation.Bean;
+import io.micronaut.context.annotation.Factory;
+import io.reactiverse.pgclient.PgClient;
+import io.reactiverse.pgclient.PgPool;
+import io.reactiverse.pgclient.PgPoolOptions;
+import io.vertx.core.Vertx;
+
+import javax.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+
+@Factory
+public class PgClientFactory {
+
+    private final PgClientConfig config;
+
+    public PgClientFactory(PgClientConfig config) {
+        this.config = config;
+    }
+
+    @Bean
+    @Singleton
+    public Vertx vertx() {
+        return Vertx.vertx();
+    }
+
+    @Bean
+    @Singleton
+    public PgClients pgClients(Vertx vertx) {
+        List<PgClient> clients = new ArrayList<>();
+
+        for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {
+            clients.add(pgClient(vertx));
+        }
+
+        return new PgClients(clients);
+    }
+
+
+    private PgPool pgClient(Vertx vertx) {
+        PgPoolOptions options = new PgPoolOptions();
+        options.setDatabase(config.getName());
+        options.setHost(config.getHost());
+        options.setPort(config.getPort());
+        options.setUser(config.getUsername());
+        options.setPassword(config.getPassword());
+        options.setCachePreparedStatements(true);
+        options.setMaxSize(1);
+        return PgClient.pool(vertx, options);
+    }
+}

+ 19 - 0
frameworks/Java/micronaut/src/main/java/benchmark/repository/PgClients.java

@@ -0,0 +1,19 @@
+package benchmark.repository;
+
+import io.reactiverse.pgclient.PgClient;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.stream.Stream;
+
+class PgClients {
+    private final Iterator<PgClient> iterator;
+
+    PgClients(Collection<PgClient> clients) {
+        iterator = Stream.generate(() -> clients).flatMap(Collection::stream).iterator();
+    }
+
+    synchronized PgClient getOne() {
+        return iterator.next();
+    }
+}

+ 40 - 0
frameworks/Java/micronaut/src/main/java/benchmark/view/MustacheViewRenderer.java

@@ -0,0 +1,40 @@
+package benchmark.view;
+
+
+import com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.Mustache;
+import io.micronaut.core.io.ResourceLoader;
+import io.micronaut.core.io.Writable;
+import io.micronaut.core.io.scan.ClassPathResourceLoader;
+import io.micronaut.http.MediaType;
+import io.micronaut.http.annotation.Produces;
+import io.micronaut.views.ViewsRenderer;
+
+import javax.annotation.Nullable;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+@Singleton
+@Produces(MediaType.TEXT_HTML)
+public class MustacheViewRenderer implements ViewsRenderer {
+    protected final ResourceLoader resourceLoader;
+    private final Mustache mustache;
+
+    public MustacheViewRenderer(ClassPathResourceLoader resourceLoader) {
+        this.resourceLoader = resourceLoader;
+        this.mustache = new DefaultMustacheFactory().compile("fortunes.mustache");
+    }
+
+    @Override
+    public Writable render(String viewName, @Nullable Object data) {
+        return out -> mustache.execute(out, data);
+    }
+
+    @Override
+    public boolean exists(String viewName) {
+        return viewName.equals("fortunes");
+    }
+
+}

+ 6 - 0
frameworks/Java/micronaut/src/main/resources/application-benchmark-local.yml

@@ -0,0 +1,6 @@
+database:
+  name: hello_world
+  host: localhost
+  port: 5432
+  username: benchmarkdbuser
+  password: benchmarkdbpass

+ 7 - 10
frameworks/Java/micronaut/src/main/resources/application-benchmark.yml

@@ -1,10 +1,7 @@
-postgres:
-    reactive:
-        client:
-            user: benchmarkdbuser
-            password: benchmarkdbpass
-            host: tfb-database
-            port: 5432
-            database: hello_world
-            cachePreparedStatements: true
-            maxSize: 48
+database:
+    name: hello_world
+    host: tfb-database
+    port: 5432
+    username: benchmarkdbuser
+    password: benchmarkdbpass
+

+ 2 - 0
frameworks/Java/micronaut/src/main/resources/logback.xml

@@ -11,4 +11,6 @@
     <root level="info">
         <appender-ref ref="STDOUT" />
     </root>
+
+    <!--<logger name="io.micronaut.http.server" level="debug"/>-->
 </configuration>