From 67376b8ad3c151bd1a7c22b63bc07b2ec99141e9 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 21 Jun 2017 11:01:13 +0200 Subject: [PATCH 1/9] DATAMONGO-1720 - Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index e1ddfe053b..acb15de3a3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1720-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index 4a49168713..67696ca8f2 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1720-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1720-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 750ed23aa8..64d30e66d5 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1720-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index fcef176585..713e454867 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1720-SNAPSHOT ../pom.xml From c85f3d89fe6c5b80ff1767e9b5670fe75195ffc2 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 7 Mar 2017 08:35:49 +0100 Subject: [PATCH 2/9] DATAMONGO-1720 - Add JMH based benchmarks for MappingMongoConverter. Run the benchmark via the maven profile "benchmarks": mvn -P benchmarks clean test Or run them customized: mvn -P benchmarks -DwarmupIterations=2 -DmeasurementIterations=5 -Dforks=1 clean test --- pom.xml | 2 + spring-data-mongodb-benchmarks/README.md | 36 ++++ spring-data-mongodb-benchmarks/pom.xml | 91 +++++++++ .../AbstractMicrobenchmark.java | 192 ++++++++++++++++++ .../DbRefMappingMongoConverterBenchmark.java | 109 ++++++++++ .../MappingMongoConverterBenchmark.java | 114 +++++++++++ .../src/main/resources/logback.xml | 14 ++ 7 files changed, 558 insertions(+) create mode 100644 spring-data-mongodb-benchmarks/README.md create mode 100644 spring-data-mongodb-benchmarks/pom.xml create mode 100644 spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java create mode 100644 spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java create mode 100644 spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java create mode 100644 spring-data-mongodb-benchmarks/src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index acb15de3a3..816a600fbe 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ spring-data-mongodb spring-data-mongodb-cross-store spring-data-mongodb-distribution + spring-data-mongodb-benchmarks @@ -30,6 +31,7 @@ 2.0.0.BUILD-SNAPSHOT 3.4.2 1.5.0 + 1.19 diff --git a/spring-data-mongodb-benchmarks/README.md b/spring-data-mongodb-benchmarks/README.md new file mode 100644 index 0000000000..c3a5d213e3 --- /dev/null +++ b/spring-data-mongodb-benchmarks/README.md @@ -0,0 +1,36 @@ +# Benchmarks + +Benchmarks are based on [JMH](http://openjdk.java.net/projects/code-tools/jmh/). + +# Running Benchmarks + +Running benchmarks is disabled by default and can be activated via the `benchmarks` profile. +To run the benchmarks with default settings use. + +```bash +mvn -P benchmarks clean test +``` + +A basic report will be printed to the CLI. + +```bash +# Run complete. Total time: 00:00:15 + +Benchmark Mode Cnt Score Error Units +MappingMongoConverterBenchmark.readObject thrpt 10 1920157,631 ± 64310,809 ops/s +MappingMongoConverterBenchmark.writeObject thrpt 10 782732,857 ± 53804,130 ops/s +``` + +More detailed information is stored in JSON format in the `/target/reports/performance` directory. + +# Customizing Benchmarks + +Following options can be set via command line. + +Option | Default Value +--- | --- +warmupIterations | 10 +measurementIterations | 10 +forks | 1 +benchmarkReportDir | /target/reports/performance (always relative to project root dir) + diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml new file mode 100644 index 0000000000..53999e4769 --- /dev/null +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -0,0 +1,91 @@ + + + + 4.0.0 + + + org.springframework.data + spring-data-mongodb-parent + 2.0.0.BUILD-SNAPSHOT + ../pom.xml + + + spring-data-mongodb-benchmarks + jar + + Spring Data MongoDB - Microbenchmarks + + + true + false + + + + + ${project.groupId} + spring-data-mongodb + ${project.version} + + + junit + junit + ${junit} + compile + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + + + + benchmarks + + false + + + + + + + + maven-jar-plugin + + + default-jar + never + + + + + maven-surefire-plugin + + ${project.build.sourceDirectory} + ${project.build.outputDirectory} + + **/AbstractMicrobenchmark.java + **/*$*.class + **/generated/*.class + + + **/*Benchmark* + + + ${project.build.directory}/reports/performance + ${project.version} + + + + + + diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java new file mode 100644 index 0000000000..af6a19bad1 --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java @@ -0,0 +1,192 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package spring.data.microbenchmark; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.junit.Test; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; + +/** + * @author Christoph Strobl + */ +@Warmup(iterations = AbstractMicrobenchmark.WARMUP_ITERATIONS) +@Measurement(iterations = AbstractMicrobenchmark.MEASUREMENT_ITERATIONS) +@Fork(AbstractMicrobenchmark.FORKS) +@State(Scope.Thread) +public class AbstractMicrobenchmark { + + static final int WARMUP_ITERATIONS = 5; + static final int MEASUREMENT_ITERATIONS = 10; + static final int FORKS = 1; + static final String[] JVM_ARGS = { "-server", "-XX:+HeapDumpOnOutOfMemoryError", "-Xms1024m", "-Xmx1024m", + "-XX:MaxDirectMemorySize=1024m" }; + + private final StandardEnvironment environment = new StandardEnvironment(); + + @Test + public void run() throws Exception { + new Runner(options().build()).run(); + } + + protected ChainedOptionsBuilder options() throws Exception { + + String className = org.springframework.util.ClassUtils.getShortName(getClass()); + + ChainedOptionsBuilder optionsBuilder = new OptionsBuilder().include(".*" + className + ".*").jvmArgs(jvmArgs()); + optionsBuilder = warmup(optionsBuilder); + optionsBuilder = measure(optionsBuilder); + optionsBuilder = forks(optionsBuilder); + optionsBuilder = report(optionsBuilder); + + return optionsBuilder; + } + + /** + * JVM args to apply to {@link Runner} via its {@link org.openjdk.jmh.runner.options.Options}. + * + * @return {@link #JVM_ARGS} by default. + */ + protected String[] jvmArgs() { + + String[] args = new String[JVM_ARGS.length]; + System.arraycopy(JVM_ARGS, 0, args, 0, JVM_ARGS.length); + return args; + } + + /** + * Read {@code warmupIterations} property from {@link org.springframework.core.env.Environment}. + * + * @return -1 if not set. + */ + protected int getWarmupIterations() { + return environment.getProperty("warmupIterations", Integer.class, -1); + } + + /** + * Read {@code measurementIterations} property from {@link org.springframework.core.env.Environment}. + * + * @return -1 if not set. + */ + protected int getMeasurementIterations() { + return environment.getProperty("measurementIterations", Integer.class, -1); + + } + + /** + * Read {@code forks} property from {@link org.springframework.core.env.Environment}. + * + * @return -1 if not set. + */ + protected int getForksCount() { + return environment.getProperty("forks", Integer.class, -1); + } + + /** + * Read {@code benchmarkReportDir} property from {@link org.springframework.core.env.Environment}. + * + * @return {@literal null} if not set. + */ + protected String getReportDirectory() { + return environment.getProperty("benchmarkReportDir"); + } + + private ChainedOptionsBuilder measure(ChainedOptionsBuilder optionsBuilder) { + + int measurementIterations = getMeasurementIterations(); + if (measurementIterations <= 0) { + return optionsBuilder; + } + + return optionsBuilder.measurementIterations(measurementIterations); + } + + private ChainedOptionsBuilder warmup(ChainedOptionsBuilder optionsBuilder) { + + int warmupIterations = getWarmupIterations(); + if (warmupIterations <= 0) { + return optionsBuilder; + } + + return optionsBuilder.warmupIterations(warmupIterations); + } + + private ChainedOptionsBuilder forks(ChainedOptionsBuilder optionsBuilder) { + + int forks = getForksCount(); + if (forks <= 0) { + return optionsBuilder; + } + + return optionsBuilder.forks(forks); + } + + private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throws IOException { + + String reportDir = getReportDirectory(); + if (!StringUtils.hasText(reportDir)) { + return optionsBuilder; + } + + String reportFilePath = reportDir + (reportDir.endsWith(File.separator) ? "" : File.separator) + reportFilename(); + File file = ResourceUtils.getFile(reportFilePath); + + if (file.exists()) { + file.delete(); + } else { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + + optionsBuilder.resultFormat(ResultFormatType.JSON); + optionsBuilder.result(reportFilePath); + + return optionsBuilder; + } + + /** + * {@code project.version_yyyy-MM-dd_ClassName.json} eg. {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json} + * @return + */ + private String reportFilename() { + + StringBuilder sb = new StringBuilder(); + + if (environment.containsProperty("project.version")) { + sb.append(environment.getProperty("project.version")); + sb.append("_"); + } + sb.append(new SimpleDateFormat("yyyy-MM-dd").format(new Date())); + sb.append("_"); + sb.append(org.springframework.util.ClassUtils.getShortName(getClass())); + sb.append(".json"); + return sb.toString(); + } +} diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java new file mode 100644 index 0000000000..bbbaa75551 --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java @@ -0,0 +1,109 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package spring.data.mongodb.core.convert; + +import lombok.Data; +import spring.data.microbenchmark.AbstractMicrobenchmark; + +import java.util.ArrayList; +import java.util.List; + +import org.bson.types.ObjectId; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import com.mongodb.MongoClient; +import com.mongodb.ServerAddress; + +/** + * @author Christoph Strobl + */ +@State(Scope.Benchmark) +public class DbRefMappingMongoConverterBenchmark extends AbstractMicrobenchmark { + + private static final String DB_NAME = "dbref-loading-benchmark"; + + private MongoClient client; + private MongoTemplate template; + + private Query queryObjectWithDBRef; + private Query queryObjectWithDBRefList; + + @Setup + public void setUp() throws Exception { + + client = new MongoClient(new ServerAddress()); + template = new MongoTemplate(client, DB_NAME); + + List refObjects = new ArrayList(); + for (int i = 0; i < 1; i++) { + RefObject o = new RefObject(); + template.save(o); + refObjects.add(o); + } + + ObjectWithDBRef singleDBRef = new ObjectWithDBRef(); + singleDBRef.ref = refObjects.iterator().next(); + template.save(singleDBRef); + + ObjectWithDBRef multipleDBRefs = new ObjectWithDBRef(); + multipleDBRefs.refList = refObjects; + template.save(multipleDBRefs); + + queryObjectWithDBRef = Query.query(Criteria.where("id").is(singleDBRef.id)); + queryObjectWithDBRefList = Query.query(Criteria.where("id").is(multipleDBRefs.id)); + } + + @TearDown + public void tearDown() { + + client.dropDatabase(DB_NAME); + client.close(); + } + + @Benchmark + public ObjectWithDBRef readWithSingleDbRef() { + return template.findOne(queryObjectWithDBRef, ObjectWithDBRef.class); + } + + @Benchmark + public ObjectWithDBRef readWithMultipleRefs() { + return template.findOne(queryObjectWithDBRefList, ObjectWithDBRef.class); + } + + @Data + static class ObjectWithDBRef { + + private @Id ObjectId id; + private @DBRef RefObject ref; + private @DBRef List refList; + } + + @Data + static class RefObject { + + private @Id String id; + private String someValue; + } +} diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java new file mode 100644 index 0000000000..4d01a74cb8 --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java @@ -0,0 +1,114 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package spring.data.mongodb.core.convert; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import spring.data.microbenchmark.AbstractMicrobenchmark; + +import java.util.Collections; + +import org.bson.Document; +import org.bson.types.ObjectId; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.SimpleMongoDbFactory; +import org.springframework.data.mongodb.core.convert.DbRefResolver; +import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; + +import com.mongodb.MongoClient; +import com.mongodb.ServerAddress; + +/** + * @author Christoph Strobl + */ +@State(Scope.Benchmark) +public class MappingMongoConverterBenchmark extends AbstractMicrobenchmark { + + private MongoClient client; + private MongoMappingContext mappingContext; + private MappingMongoConverter converter; + private Document plainSource, sourceWithAddress; + private Customer customer; + + @Setup + public void setUp() throws Exception { + + client = new MongoClient(new ServerAddress()); + + this.mappingContext = new MongoMappingContext(); + this.mappingContext.setInitialEntitySet(Collections.singleton(Customer.class)); + this.mappingContext.afterPropertiesSet(); + + DbRefResolver dbRefResolver = new DefaultDbRefResolver(new SimpleMongoDbFactory(client, "benchmark")); + + this.converter = new MappingMongoConverter(dbRefResolver, mappingContext); + this.plainSource = new Document("firstname", "Dave").append("lastname", "Matthews"); + + Document address = new Document("zipCode", "ABCDE").append("city", "Some Place"); + + this.sourceWithAddress = new Document("firstname", "Dave").// + append("lastname", "Matthews").// + append("address", address); + + this.customer = new Customer("Dave", "Matthews", new Address("zipCode", "City")); + } + + @TearDown + public void tearDown() { + client.close(); + } + + @Benchmark + public Customer readObject() { + return converter.read(Customer.class, plainSource); + } + + @Benchmark + public Customer readObjectWithNested() { + return converter.read(Customer.class, sourceWithAddress); + } + + @Benchmark + public Document writeObject() { + + Document sink = new Document(); + converter.write(customer, sink); + return sink; + } + + @Getter + @RequiredArgsConstructor + static class Customer { + + private @Id ObjectId id; + private final String firstname, lastname; + private final Address address; + } + + @Getter + @AllArgsConstructor + static class Address { + private String zipCode, city; + } +} diff --git a/spring-data-mongodb-benchmarks/src/main/resources/logback.xml b/spring-data-mongodb-benchmarks/src/main/resources/logback.xml new file mode 100644 index 0000000000..bccb2dc4fa --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + \ No newline at end of file From 05a3cc772f15816fdd6ca20a1f6d6d14014daebe Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 21 Jun 2017 13:23:55 +0200 Subject: [PATCH 3/9] Do NOT merge this commit - temp version number bump to match issue branch. --- spring-data-mongodb-benchmarks/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 53999e4769..9e07aa1300 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1720-SNAPSHOT ../pom.xml From 3cf71fd7c7f817ef60d1d6e7317a48dff909a1bb Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 21 Jun 2017 15:08:44 +0200 Subject: [PATCH 4/9] DATAMONGO-1720 - Pick up additional command line args for execution time & benchmark to run. Excution time can be set via -D warmupTime= -D measurementTime= Allow selecting a class or a single benchmark via -D benchmark=classname#method. Just skip the method name in order to run all benchmarks of a given class. --- spring-data-mongodb-benchmarks/README.md | 4 +- .../AbstractMicrobenchmark.java | 75 ++++++++++++++++--- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/spring-data-mongodb-benchmarks/README.md b/spring-data-mongodb-benchmarks/README.md index c3a5d213e3..dcf79d3f66 100644 --- a/spring-data-mongodb-benchmarks/README.md +++ b/spring-data-mongodb-benchmarks/README.md @@ -30,7 +30,9 @@ Following options can be set via command line. Option | Default Value --- | --- warmupIterations | 10 +warmupTime | 1 (seconds) measurementIterations | 10 +measurementTime | 1 (seconds) forks | 1 benchmarkReportDir | /target/reports/performance (always relative to project root dir) - +benchmark | .* (single benchmark via `classname#benchmark`) \ No newline at end of file diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java index af6a19bad1..ec7a069ead 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java @@ -30,6 +30,7 @@ import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; import org.springframework.core.env.StandardEnvironment; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; @@ -53,14 +54,19 @@ public class AbstractMicrobenchmark { @Test public void run() throws Exception { - new Runner(options().build()).run(); - } - protected ChainedOptionsBuilder options() throws Exception { + String includes = includes(); + + if(!includes.contains(org.springframework.util.ClassUtils.getShortName(getClass()))) { + return; + } + + new Runner(options(includes).build()).run(); + } - String className = org.springframework.util.ClassUtils.getShortName(getClass()); + protected ChainedOptionsBuilder options(String includes) throws Exception { - ChainedOptionsBuilder optionsBuilder = new OptionsBuilder().include(".*" + className + ".*").jvmArgs(jvmArgs()); + ChainedOptionsBuilder optionsBuilder = new OptionsBuilder().include(includes).jvmArgs(jvmArgs()); optionsBuilder = warmup(optionsBuilder); optionsBuilder = measure(optionsBuilder); optionsBuilder = forks(optionsBuilder); @@ -118,24 +124,52 @@ protected String getReportDirectory() { return environment.getProperty("benchmarkReportDir"); } + /** + * Read {@code measurementTime} property from {@link org.springframework.core.env.Environment}. + * + * @return -1 if not set. + */ + protected long getMeasurementTime() { + return environment.getProperty("measurementTime", Long.class, -1L); + } + + /** + * Read {@code warmupTime} property from {@link org.springframework.core.env.Environment}. + * + * @return -1 if not set. + */ + protected long getWarmupTime() { + return environment.getProperty("warmupTime", Long.class, -1L); + } + private ChainedOptionsBuilder measure(ChainedOptionsBuilder optionsBuilder) { int measurementIterations = getMeasurementIterations(); - if (measurementIterations <= 0) { - return optionsBuilder; + long measurementTime = getMeasurementTime(); + + if (measurementIterations > 0) { + optionsBuilder = optionsBuilder.measurementIterations(measurementIterations); } - return optionsBuilder.measurementIterations(measurementIterations); + if (measurementTime > 0) { + optionsBuilder = optionsBuilder.measurementTime(TimeValue.seconds(measurementTime)); + } + return optionsBuilder; } private ChainedOptionsBuilder warmup(ChainedOptionsBuilder optionsBuilder) { int warmupIterations = getWarmupIterations(); - if (warmupIterations <= 0) { - return optionsBuilder; + long warmupTime = getWarmupTime(); + + if (warmupIterations > 0) { + optionsBuilder = optionsBuilder.warmupIterations(warmupIterations); } - return optionsBuilder.warmupIterations(warmupIterations); + if (warmupTime > 0) { + optionsBuilder = optionsBuilder.warmupTime(TimeValue.seconds(warmupTime)); + } + return optionsBuilder; } private ChainedOptionsBuilder forks(ChainedOptionsBuilder optionsBuilder) { @@ -171,8 +205,25 @@ private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throw return optionsBuilder; } + private String includes() { + + String tests = environment.getProperty("benchmark", String.class); + if (!StringUtils.hasText(tests)) { + return ".*" + org.springframework.util.ClassUtils.getShortName(getClass()) + ".*"; + } + + if (!tests.contains("#")) { + return ".*" + tests + ".*"; + } + + String[] args = tests.split("#"); + return ".*" + args[0] + "." + args[1]; + } + /** - * {@code project.version_yyyy-MM-dd_ClassName.json} eg. {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json} + * {@code project.version_yyyy-MM-dd_ClassName.json} eg. + * {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json} + * * @return */ private String reportFilename() { From ffbb34862dc8bf0e3dfcd83eecfd1e1f4530bb3e Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 22 Jun 2017 10:17:11 +0200 Subject: [PATCH 5/9] DATAMONGO-1720 - Add read/write mapping benchmarks with more fields. --- .../MappingMongoConverterBenchmark.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java index 4d01a74cb8..8820ed5f47 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java @@ -16,11 +16,17 @@ package spring.data.mongodb.core.convert; import lombok.AllArgsConstructor; +import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; import spring.data.microbenchmark.AbstractMicrobenchmark; +import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; import org.bson.Document; import org.bson.types.ObjectId; @@ -30,10 +36,13 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.springframework.data.annotation.Id; +import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import com.mongodb.MongoClient; @@ -51,6 +60,9 @@ public class MappingMongoConverterBenchmark extends AbstractMicrobenchmark { private Document plainSource, sourceWithAddress; private Customer customer; + private Document complexSource; + private SlightlyMoreComplexObject complexObject; + @Setup public void setUp() throws Exception { @@ -63,6 +75,9 @@ public void setUp() throws Exception { DbRefResolver dbRefResolver = new DefaultDbRefResolver(new SimpleMongoDbFactory(client, "benchmark")); this.converter = new MappingMongoConverter(dbRefResolver, mappingContext); + this.converter.setCustomConversions(new MongoCustomConversions(Collections.emptyList())); + this.converter.afterPropertiesSet(); + this.plainSource = new Document("firstname", "Dave").append("lastname", "Matthews"); Document address = new Document("zipCode", "ABCDE").append("city", "Some Place"); @@ -72,6 +87,25 @@ public void setUp() throws Exception { append("address", address); this.customer = new Customer("Dave", "Matthews", new Address("zipCode", "City")); + + complexObject = new SlightlyMoreComplexObject(); + complexObject.id = UUID.randomUUID().toString(); + complexObject.addressList = Arrays.asList(new Address("zip-1", "city-1"), new Address("zip-2", "city-2")); + complexObject.customer = customer; + complexObject.customerMap = new LinkedHashMap<>(); + complexObject.customerMap.put("dave", customer); + complexObject.customerMap.put("deborah", new Customer("Deborah Anne", "Dyer", new Address("?", "london"))); + complexObject.customerMap.put("eddie", new Customer("Eddie", "Vedder", new Address("??", "Seattle"))); + complexObject.intOne = Integer.MIN_VALUE; + complexObject.intTwo = Integer.MAX_VALUE; + complexObject.location = new Point(-33.865143, 151.209900); + complexObject.renamedField = "supercalifragilisticexpialidocious"; + complexObject.stringOne = "¯\\_(ツ)_/¯"; + complexObject.stringTwo = " (╯°□°)╯︵ ┻━┻"; + + complexSource = Document.parse( + "{ \"_id\" : \"517f6aee-e9e0-44f0-88ed-f3694a019f27\", \"intOne\" : -2147483648, \"intTwo\" : 2147483647, \"stringOne\" : \"¯\\\\_(ツ)_/¯\", \"stringTwo\" : \" (╯°□°)╯︵ ┻━┻\", \"explicit-field-name\" : \"supercalifragilisticexpialidocious\", \"location\" : { \"x\" : -33.865143, \"y\" : 151.2099 }, \"customer\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"addressList\" : [{ \"zipCode\" : \"zip-1\", \"city\" : \"city-1\" }, { \"zipCode\" : \"zip-2\", \"city\" : \"city-2\" }], \"customerMap\" : { \"dave\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"deborah\" : { \"firstname\" : \"Deborah Anne\", \"lastname\" : \"Dyer\", \"address\" : { \"zipCode\" : \"?\", \"city\" : \"london\" } }, \"eddie\" : { \"firstname\" : \"Eddie\", \"lastname\" : \"Vedder\", \"address\" : { \"zipCode\" : \"??\", \"city\" : \"Seattle\" } } }, \"_class\" : \"spring.data.mongodb.core.convert.MappingMongoConverterBenchmark$SlightlyMoreComplexObject\" }"); + } @TearDown @@ -97,6 +131,19 @@ public Document writeObject() { return sink; } + @Benchmark + public Object complexRead() { + return converter.read(SlightlyMoreComplexObject.class, complexSource); + } + + @Benchmark + public Object complexWrite() { + + Document sink = new Document(); + converter.write(complexObject, sink); + return sink; + } + @Getter @RequiredArgsConstructor static class Customer { @@ -111,4 +158,17 @@ static class Customer { static class Address { private String zipCode, city; } + + @Data + static class SlightlyMoreComplexObject { + + @Id String id; + int intOne, intTwo; + String stringOne, stringTwo; + @Field("explicit-field-name") String renamedField; + Point location; + Customer customer; + List
addressList; + Map customerMap; + } } From 05bcb585672a32bf3942636f3c94b007c1589210 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 11 Jul 2017 09:54:30 +0200 Subject: [PATCH 6/9] DATAMONGO-1720 - Code Cleanup. Update JavaDoc, rename properties --- .../AbstractMicrobenchmark.java | 137 +++++++++++++----- ...chmark.java => DbRefMappingBenchmark.java} | 20 +-- .../MappingMongoConverterBenchmark.java | 95 ++++++------ 3 files changed, 164 insertions(+), 88 deletions(-) rename spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/{DbRefMappingMongoConverterBenchmark.java => DbRefMappingBenchmark.java} (82%) diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java index ec7a069ead..5a6b0023a5 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java @@ -52,21 +52,61 @@ public class AbstractMicrobenchmark { private final StandardEnvironment environment = new StandardEnvironment(); + /** + * Run matching {@link org.openjdk.jmh.annotations.Benchmark} methods with options collected from + * {@link org.springframework.core.env.Environment}. + * + * @throws Exception + * @see #options(String) + */ @Test public void run() throws Exception { String includes = includes(); - if(!includes.contains(org.springframework.util.ClassUtils.getShortName(getClass()))) { + if (!includes.contains(org.springframework.util.ClassUtils.getShortName(getClass()))) { return; } new Runner(options(includes).build()).run(); } + /** + * Get the regex for all benchmarks to be included in the run. By default every benchmark within classes matching the + * current ones short name.
+ * The {@literal benchmark} command line argument allows overriding the defaults using {@code #} as class / method + * name separator. + * + * @return never {@literal null}. + * @see org.springframework.util.ClassUtils#getShortName(Class) + */ + protected String includes() { + + String tests = environment.getProperty("benchmark", String.class); + + if (!StringUtils.hasText(tests)) { + return ".*" + org.springframework.util.ClassUtils.getShortName(getClass()) + ".*"; + } + + if (!tests.contains("#")) { + return ".*" + tests + ".*"; + } + + String[] args = tests.split("#"); + return ".*" + args[0] + "." + args[1]; + } + + /** + * Collect all options for the {@link Runner}. + * + * @param includes regex for matching benchmarks to be included in the run. + * @return never {@literal null}. + * @throws Exception + */ protected ChainedOptionsBuilder options(String includes) throws Exception { ChainedOptionsBuilder optionsBuilder = new OptionsBuilder().include(includes).jvmArgs(jvmArgs()); + optionsBuilder = warmup(optionsBuilder); optionsBuilder = measure(optionsBuilder); optionsBuilder = forks(optionsBuilder); @@ -142,6 +182,37 @@ protected long getWarmupTime() { return environment.getProperty("warmupTime", Long.class, -1L); } + /** + * {@code project.version_yyyy-MM-dd_ClassName.json} eg. + * {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json} + * + * @return + */ + protected String reportFilename() { + + StringBuilder sb = new StringBuilder(); + + if (environment.containsProperty("project.version")) { + + sb.append(environment.getProperty("project.version")); + sb.append("_"); + } + + sb.append(new SimpleDateFormat("yyyy-MM-dd").format(new Date())); + sb.append("_"); + sb.append(org.springframework.util.ClassUtils.getShortName(getClass())); + sb.append(".json"); + return sb.toString(); + } + + /** + * Apply measurement options to {@link ChainedOptionsBuilder}. + * + * @param optionsBuilder must not be {@literal null}. + * @return {@link ChainedOptionsBuilder} with options applied. + * @see #getMeasurementIterations() + * @see #getMeasurementTime() + */ private ChainedOptionsBuilder measure(ChainedOptionsBuilder optionsBuilder) { int measurementIterations = getMeasurementIterations(); @@ -154,9 +225,18 @@ private ChainedOptionsBuilder measure(ChainedOptionsBuilder optionsBuilder) { if (measurementTime > 0) { optionsBuilder = optionsBuilder.measurementTime(TimeValue.seconds(measurementTime)); } + return optionsBuilder; } + /** + * Apply warmup options to {@link ChainedOptionsBuilder}. + * + * @param optionsBuilder must not be {@literal null}. + * @return {@link ChainedOptionsBuilder} with options applied. + * @see #getWarmupIterations() + * @see #getWarmupTime() + */ private ChainedOptionsBuilder warmup(ChainedOptionsBuilder optionsBuilder) { int warmupIterations = getWarmupIterations(); @@ -169,12 +249,21 @@ private ChainedOptionsBuilder warmup(ChainedOptionsBuilder optionsBuilder) { if (warmupTime > 0) { optionsBuilder = optionsBuilder.warmupTime(TimeValue.seconds(warmupTime)); } + return optionsBuilder; } + /** + * Apply forks option to {@link ChainedOptionsBuilder}. + * + * @param optionsBuilder must not be {@literal null}. + * @return {@link ChainedOptionsBuilder} with options applied. + * @see #getForksCount() + */ private ChainedOptionsBuilder forks(ChainedOptionsBuilder optionsBuilder) { int forks = getForksCount(); + if (forks <= 0) { return optionsBuilder; } @@ -182,9 +271,18 @@ private ChainedOptionsBuilder forks(ChainedOptionsBuilder optionsBuilder) { return optionsBuilder.forks(forks); } + /** + * Apply report option to {@link ChainedOptionsBuilder}. + * + * @param optionsBuilder must not be {@literal null}. + * @return {@link ChainedOptionsBuilder} with options applied. + * @throws IOException if report file cannot be created. + * @see #getReportDirectory() + */ private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throws IOException { String reportDir = getReportDirectory(); + if (!StringUtils.hasText(reportDir)) { return optionsBuilder; } @@ -195,6 +293,7 @@ private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throw if (file.exists()) { file.delete(); } else { + file.getParentFile().mkdirs(); file.createNewFile(); } @@ -204,40 +303,4 @@ private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throw return optionsBuilder; } - - private String includes() { - - String tests = environment.getProperty("benchmark", String.class); - if (!StringUtils.hasText(tests)) { - return ".*" + org.springframework.util.ClassUtils.getShortName(getClass()) + ".*"; - } - - if (!tests.contains("#")) { - return ".*" + tests + ".*"; - } - - String[] args = tests.split("#"); - return ".*" + args[0] + "." + args[1]; - } - - /** - * {@code project.version_yyyy-MM-dd_ClassName.json} eg. - * {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json} - * - * @return - */ - private String reportFilename() { - - StringBuilder sb = new StringBuilder(); - - if (environment.containsProperty("project.version")) { - sb.append(environment.getProperty("project.version")); - sb.append("_"); - } - sb.append(new SimpleDateFormat("yyyy-MM-dd").format(new Date())); - sb.append("_"); - sb.append(org.springframework.util.ClassUtils.getShortName(getClass())); - sb.append(".json"); - return sb.toString(); - } } diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingBenchmark.java similarity index 82% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java rename to spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingBenchmark.java index bbbaa75551..9566123cbd 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingMongoConverterBenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingBenchmark.java @@ -15,6 +15,9 @@ */ package spring.data.mongodb.core.convert; +import static org.springframework.data.mongodb.core.query.Criteria.*; +import static org.springframework.data.mongodb.core.query.Query.*; + import lombok.Data; import spring.data.microbenchmark.AbstractMicrobenchmark; @@ -30,7 +33,6 @@ import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import com.mongodb.MongoClient; @@ -40,7 +42,7 @@ * @author Christoph Strobl */ @State(Scope.Benchmark) -public class DbRefMappingMongoConverterBenchmark extends AbstractMicrobenchmark { +public class DbRefMappingBenchmark extends AbstractMicrobenchmark { private static final String DB_NAME = "dbref-loading-benchmark"; @@ -56,7 +58,7 @@ public void setUp() throws Exception { client = new MongoClient(new ServerAddress()); template = new MongoTemplate(client, DB_NAME); - List refObjects = new ArrayList(); + List refObjects = new ArrayList<>(); for (int i = 0; i < 1; i++) { RefObject o = new RefObject(); template.save(o); @@ -71,8 +73,8 @@ public void setUp() throws Exception { multipleDBRefs.refList = refObjects; template.save(multipleDBRefs); - queryObjectWithDBRef = Query.query(Criteria.where("id").is(singleDBRef.id)); - queryObjectWithDBRefList = Query.query(Criteria.where("id").is(multipleDBRefs.id)); + queryObjectWithDBRef = query(where("id").is(singleDBRef.id)); + queryObjectWithDBRefList = query(where("id").is(multipleDBRefs.id)); } @TearDown @@ -82,13 +84,13 @@ public void tearDown() { client.close(); } - @Benchmark - public ObjectWithDBRef readWithSingleDbRef() { + @Benchmark // DATAMONGO-1720 + public ObjectWithDBRef readSingleDbRef() { return template.findOne(queryObjectWithDBRef, ObjectWithDBRef.class); } - @Benchmark - public ObjectWithDBRef readWithMultipleRefs() { + @Benchmark // DATAMONGO-1720 + public ObjectWithDBRef readMultipleDbRefs() { return template.findOne(queryObjectWithDBRefList, ObjectWithDBRef.class); } diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java index 8820ed5f47..a985d517b7 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java @@ -54,14 +54,16 @@ @State(Scope.Benchmark) public class MappingMongoConverterBenchmark extends AbstractMicrobenchmark { + private static final String DB_NAME = "mapping-mongo-converter-benchmark"; + private MongoClient client; private MongoMappingContext mappingContext; private MappingMongoConverter converter; - private Document plainSource, sourceWithAddress; - private Customer customer; + private Document documentWith2Properties, documentWith2PropertiesAnd1Nested; + private Customer objectWith2PropertiesAnd1Nested; - private Document complexSource; - private SlightlyMoreComplexObject complexObject; + private Document documentWithFlatAndComplexPropertiesPlusListAndMap; + private SlightlyMoreComplexObject objectWithFlatAndComplexPropertiesPlusListAndMap; @Setup public void setUp() throws Exception { @@ -72,75 +74,84 @@ public void setUp() throws Exception { this.mappingContext.setInitialEntitySet(Collections.singleton(Customer.class)); this.mappingContext.afterPropertiesSet(); - DbRefResolver dbRefResolver = new DefaultDbRefResolver(new SimpleMongoDbFactory(client, "benchmark")); + DbRefResolver dbRefResolver = new DefaultDbRefResolver(new SimpleMongoDbFactory(client, DB_NAME)); this.converter = new MappingMongoConverter(dbRefResolver, mappingContext); this.converter.setCustomConversions(new MongoCustomConversions(Collections.emptyList())); this.converter.afterPropertiesSet(); - this.plainSource = new Document("firstname", "Dave").append("lastname", "Matthews"); + // just a flat document + this.documentWith2Properties = new Document("firstname", "Dave").append("lastname", "Matthews"); + // document with a nested one Document address = new Document("zipCode", "ABCDE").append("city", "Some Place"); - - this.sourceWithAddress = new Document("firstname", "Dave").// + this.documentWith2PropertiesAnd1Nested = new Document("firstname", "Dave").// append("lastname", "Matthews").// append("address", address); - this.customer = new Customer("Dave", "Matthews", new Address("zipCode", "City")); - - complexObject = new SlightlyMoreComplexObject(); - complexObject.id = UUID.randomUUID().toString(); - complexObject.addressList = Arrays.asList(new Address("zip-1", "city-1"), new Address("zip-2", "city-2")); - complexObject.customer = customer; - complexObject.customerMap = new LinkedHashMap<>(); - complexObject.customerMap.put("dave", customer); - complexObject.customerMap.put("deborah", new Customer("Deborah Anne", "Dyer", new Address("?", "london"))); - complexObject.customerMap.put("eddie", new Customer("Eddie", "Vedder", new Address("??", "Seattle"))); - complexObject.intOne = Integer.MIN_VALUE; - complexObject.intTwo = Integer.MAX_VALUE; - complexObject.location = new Point(-33.865143, 151.209900); - complexObject.renamedField = "supercalifragilisticexpialidocious"; - complexObject.stringOne = "¯\\_(ツ)_/¯"; - complexObject.stringTwo = " (╯°□°)╯︵ ┻━┻"; - - complexSource = Document.parse( - "{ \"_id\" : \"517f6aee-e9e0-44f0-88ed-f3694a019f27\", \"intOne\" : -2147483648, \"intTwo\" : 2147483647, \"stringOne\" : \"¯\\\\_(ツ)_/¯\", \"stringTwo\" : \" (╯°□°)╯︵ ┻━┻\", \"explicit-field-name\" : \"supercalifragilisticexpialidocious\", \"location\" : { \"x\" : -33.865143, \"y\" : 151.2099 }, \"customer\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"addressList\" : [{ \"zipCode\" : \"zip-1\", \"city\" : \"city-1\" }, { \"zipCode\" : \"zip-2\", \"city\" : \"city-2\" }], \"customerMap\" : { \"dave\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"deborah\" : { \"firstname\" : \"Deborah Anne\", \"lastname\" : \"Dyer\", \"address\" : { \"zipCode\" : \"?\", \"city\" : \"london\" } }, \"eddie\" : { \"firstname\" : \"Eddie\", \"lastname\" : \"Vedder\", \"address\" : { \"zipCode\" : \"??\", \"city\" : \"Seattle\" } } }, \"_class\" : \"spring.data.mongodb.core.convert.MappingMongoConverterBenchmark$SlightlyMoreComplexObject\" }"); + // object equivalent of documentWith2PropertiesAnd1Nested + this.objectWith2PropertiesAnd1Nested = new Customer("Dave", "Matthews", new Address("zipCode", "City")); + + // a bit more challenging object with list & map conversion. + objectWithFlatAndComplexPropertiesPlusListAndMap = new SlightlyMoreComplexObject(); + objectWithFlatAndComplexPropertiesPlusListAndMap.id = UUID.randomUUID().toString(); + objectWithFlatAndComplexPropertiesPlusListAndMap.addressList = Arrays.asList(new Address("zip-1", "city-1"), + new Address("zip-2", "city-2")); + objectWithFlatAndComplexPropertiesPlusListAndMap.customer = objectWith2PropertiesAnd1Nested; + objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap = new LinkedHashMap<>(); + objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap.put("dave", objectWith2PropertiesAnd1Nested); + objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap.put("deborah", + new Customer("Deborah Anne", "Dyer", new Address("?", "london"))); + objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap.put("eddie", + new Customer("Eddie", "Vedder", new Address("??", "Seattle"))); + objectWithFlatAndComplexPropertiesPlusListAndMap.intOne = Integer.MIN_VALUE; + objectWithFlatAndComplexPropertiesPlusListAndMap.intTwo = Integer.MAX_VALUE; + objectWithFlatAndComplexPropertiesPlusListAndMap.location = new Point(-33.865143, 151.209900); + objectWithFlatAndComplexPropertiesPlusListAndMap.renamedField = "supercalifragilisticexpialidocious"; + objectWithFlatAndComplexPropertiesPlusListAndMap.stringOne = "¯\\_(ツ)_/¯"; + objectWithFlatAndComplexPropertiesPlusListAndMap.stringTwo = " (╯°□°)╯︵ ┻━┻"; + + // JSON equivalent of objectWithFlatAndComplexPropertiesPlusListAndMap + documentWithFlatAndComplexPropertiesPlusListAndMap = Document.parse( + "{ \"_id\" : \"517f6aee-e9e0-44f0-88ed-f3694a019f27\", \"intOne\" : -2147483648, \"intTwo\" : 2147483647, \"stringOne\" : \"¯\\\\_(ツ)_/¯\", \"stringTwo\" : \" (╯°□°)╯︵ ┻━┻\", \"explicit-field-name\" : \"supercalifragilisticexpialidocious\", \"location\" : { \"x\" : -33.865143, \"y\" : 151.2099 }, \"objectWith2PropertiesAnd1Nested\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"addressList\" : [{ \"zipCode\" : \"zip-1\", \"city\" : \"city-1\" }, { \"zipCode\" : \"zip-2\", \"city\" : \"city-2\" }], \"customerMap\" : { \"dave\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"deborah\" : { \"firstname\" : \"Deborah Anne\", \"lastname\" : \"Dyer\", \"address\" : { \"zipCode\" : \"?\", \"city\" : \"london\" } }, \"eddie\" : { \"firstname\" : \"Eddie\", \"lastname\" : \"Vedder\", \"address\" : { \"zipCode\" : \"??\", \"city\" : \"Seattle\" } } }, \"_class\" : \"spring.data.mongodb.core.convert.MappingMongoConverterBenchmark$SlightlyMoreComplexObject\" }"); } @TearDown public void tearDown() { + + client.dropDatabase(DB_NAME); client.close(); } - @Benchmark - public Customer readObject() { - return converter.read(Customer.class, plainSource); + @Benchmark // DATAMONGO-1720 + public Customer readObjectWith2Properties() { + return converter.read(Customer.class, documentWith2Properties); } - @Benchmark - public Customer readObjectWithNested() { - return converter.read(Customer.class, sourceWithAddress); + @Benchmark // DATAMONGO-1720 + public Customer readObjectWith2tPropertiesAnd1NestedObject() { + return converter.read(Customer.class, documentWith2PropertiesAnd1Nested); } - @Benchmark - public Document writeObject() { + @Benchmark // DATAMONGO-1720 + public Document writeObjectWith2PropertiesAnd1NestedObject() { Document sink = new Document(); - converter.write(customer, sink); + converter.write(objectWith2PropertiesAnd1Nested, sink); return sink; } - @Benchmark - public Object complexRead() { - return converter.read(SlightlyMoreComplexObject.class, complexSource); + @Benchmark // DATAMONGO-1720 + public Object readObjectWithListAndMapsOfComplexType() { + return converter.read(SlightlyMoreComplexObject.class, documentWithFlatAndComplexPropertiesPlusListAndMap); } - @Benchmark - public Object complexWrite() { + @Benchmark // DATAMONGO-1720 + public Object writeObjectWithListAndMapsOfComplexType() { Document sink = new Document(); - converter.write(complexObject, sink); + converter.write(objectWithFlatAndComplexPropertiesPlusListAndMap, sink); return sink; } From 39808e63173f28241919536f3e75385674bd0e2c Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 11 Jul 2017 13:08:56 +0200 Subject: [PATCH 7/9] DATAMONGO-1720 - Allow direct publication of results to http / mongodb endpoint. --- spring-data-mongodb-benchmarks/README.md | 42 +++++- .../AbstractMicrobenchmark.java | 24 +++- .../microbenchmark/HttpResultsWriter.java | 62 +++++++++ .../microbenchmark/MongoResultsWriter.java | 126 ++++++++++++++++++ .../data/microbenchmark/ResultsWriter.java | 63 +++++++++ 5 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java create mode 100644 spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java create mode 100644 spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java diff --git a/spring-data-mongodb-benchmarks/README.md b/spring-data-mongodb-benchmarks/README.md index dcf79d3f66..e11925b7fd 100644 --- a/spring-data-mongodb-benchmarks/README.md +++ b/spring-data-mongodb-benchmarks/README.md @@ -21,7 +21,44 @@ MappingMongoConverterBenchmark.readObject thrpt 10 1920157,631 ± 64310,809 MappingMongoConverterBenchmark.writeObject thrpt 10 782732,857 ± 53804,130 ops/s ``` -More detailed information is stored in JSON format in the `/target/reports/performance` directory. +## Running all Benchmarks of a specific class + +To run all Benchmarks of a specific class, just provide its simple class name via the `benchmark` command line argument. + +```bash +mvn -P benchmarks clean test -D benchmark=MappingMongoConverterBenchmark +``` + +## Running a single Benchmark + +To run a single Benchmark provide its containing class simple name followed by `#` and the method name via the `benchmark` command line argument. + +```bash +mvn -P benchmarks clean test -D benchmark=MappingMongoConverterBenchmark#readObjectWith2Properties +``` + +# Saving Benchmark Results + +A detailed benchmark report is stored in JSON format in the `/target/reports/performance` directory. +To store the report in a different location use the `benchmarkReportDir` command line argument. + +## MongoDB + +Results can be directly piped to MongoDB by providing a valid [Connection String](https://docs.mongodb.com/manual/reference/connection-string/) via the `publishTo` command line argument. + +```bash +mvn -P benchmarks clean test -D publishTo=mongodb://127.0.0.1:27017 +``` + +NOTE: If the uri does not explicitly define a database the default `spring-data-mongodb-benchmarks` is used. + +## HTTP Endpoint + +The benchmark report can also be posted as `application/json` to an HTTP Endpoint by providing a valid URl via the `publishTo` command line argument. + +```bash +mvn -P benchmarks clean test -D publishTo=http://127.0.0.1:8080/capture-benchmarks +``` # Customizing Benchmarks @@ -35,4 +72,5 @@ measurementIterations | 10 measurementTime | 1 (seconds) forks | 1 benchmarkReportDir | /target/reports/performance (always relative to project root dir) -benchmark | .* (single benchmark via `classname#benchmark`) \ No newline at end of file +benchmark | .* (single benchmark via `classname#benchmark`) +publishTo | \[not set\] (mongodb-uri or http-endpoint) \ No newline at end of file diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java index 5a6b0023a5..9496835602 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; import org.junit.Test; @@ -26,12 +27,14 @@ import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.results.RunResult; import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.TimeValue; import org.springframework.core.env.StandardEnvironment; +import org.springframework.util.CollectionUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; @@ -68,7 +71,7 @@ public void run() throws Exception { return; } - new Runner(options(includes).build()).run(); + publishResults(new Runner(options(includes).build()).run()); } /** @@ -303,4 +306,23 @@ private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throw return optionsBuilder; } + + /** + * Publish results to an external system. + * + * @param results must not be {@literal null}. + */ + private void publishResults(Collection results) { + + if (CollectionUtils.isEmpty(results) || !environment.containsProperty("publishTo")) { + return; + } + + String uri = environment.getProperty("publishTo"); + try { + ResultsWriter.forUri(uri).write(results); + } catch (Exception e) { + System.err.println(String.format("Cannot save benchmark results to '%s'. Error was %s.", uri, e)); + } + } } diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java new file mode 100644 index 0000000000..dbd45802e5 --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package spring.data.microbenchmark; + +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collection; + +import org.openjdk.jmh.results.RunResult; +import org.springframework.util.CollectionUtils; + +/** + * {@link ResultsWriter} implementation of {@link URLConnection}. + * + * @since 2.0 + */ +class HttpResultsWriter implements ResultsWriter { + + private final String url; + + HttpResultsWriter(String url) { + this.url = url; + } + + @Override + public void write(Collection results) { + + if (CollectionUtils.isEmpty(results)) { + return; + } + + try { + + URLConnection connection = new URL(url).openConnection(); + connection.setConnectTimeout((int) Duration.ofSeconds(1).toMillis()); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json"); + + try (OutputStream output = connection.getOutputStream()) { + output.write(ResultsWriter.jsonifyResults(results).getBytes(StandardCharsets.UTF_8)); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java new file mode 100644 index 0000000000..9fa8d4f756 --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java @@ -0,0 +1,126 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package spring.data.microbenchmark; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.bson.Document; +import org.openjdk.jmh.results.RunResult; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import com.mongodb.BasicDBObject; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientURI; +import com.mongodb.client.MongoDatabase; +import com.mongodb.util.JSON; + +/** + * MongoDB specific {@link ResultsWriter} implementation. + * + * @author Christoph Strobl + * @since 2.0 + */ +class MongoResultsWriter implements ResultsWriter { + + private final String uri; + + MongoResultsWriter(String uri) { + this.uri = uri; + } + + @Override + public void write(Collection results) { + + Date now = new Date(); + StandardEnvironment env = new StandardEnvironment(); + + String projectVersion = env.getProperty("project.version", "unknown"); + + MongoClientURI uri = new MongoClientURI(this.uri); + MongoClient client = new MongoClient(uri); + + String dbName = StringUtils.hasText(uri.getDatabase()) ? uri.getDatabase() : "spring-data-mongodb-benchmarks"; + MongoDatabase db = client.getDatabase(dbName); + + for (BasicDBObject dbo : (List) JSON.parse(ResultsWriter.jsonifyResults(results))) { + + String collectionName = extractClass(dbo.get("benchmark").toString()); + + Document sink = new Document(); + sink.append("_version", projectVersion); + sink.append("_method", extractBenchmarkName(dbo.get("benchmark").toString())); + sink.append("_date", now); + sink.append("_snapshot", projectVersion.toLowerCase().contains("snapshot")); + + sink.putAll(dbo); + + db.getCollection(collectionName).insertOne(fixDocumentKeys(sink)); + } + + client.close(); + + } + + /** + * Replace {@code .} by {@code ,}. + * + * @param doc + * @return + */ + private Document fixDocumentKeys(Document doc) { + + Document sanitized = new Document(); + + for (Object key : doc.keySet()) { + + Object value = doc.get(key); + if (value instanceof Document) { + value = fixDocumentKeys((Document) value); + } else if (value instanceof BasicDBObject) { + value = fixDocumentKeys(new Document((BasicDBObject) value)); + } + + if (key instanceof String) { + + String newKey = (String) key; + if (newKey.contains(".")) { + newKey = newKey.replace('.', ','); + } + + sanitized.put(newKey, value); + } else { + sanitized.put(ObjectUtils.nullSafeToString(key).replace('.', ','), value); + } + } + + return sanitized; + } + + private String extractClass(String source) { + + String tmp = source.substring(0, source.lastIndexOf('.')); + return tmp.substring(tmp.lastIndexOf(".") + 1); + } + + private String extractBenchmarkName(String source) { + return source.substring(source.lastIndexOf(".") + 1); + } + +} diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java new file mode 100644 index 0000000000..8431a236b4 --- /dev/null +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java @@ -0,0 +1,63 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package spring.data.microbenchmark; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.util.Collection; + +import org.openjdk.jmh.results.RunResult; +import org.openjdk.jmh.results.format.ResultFormatFactory; +import org.openjdk.jmh.results.format.ResultFormatType; + +/** + * @author Christoph Strobl + * @since 2.0 + */ +interface ResultsWriter { + + /** + * Write the {@link RunResult}s. + * + * @param results can be {@literal null}. + */ + void write(Collection results); + + /** + * Get the uri specific {@link ResultsWriter}. + * + * @param uri must not be {@literal null}. + * @return + */ + static ResultsWriter forUri(String uri) { + return uri.startsWith("mongodb:") ? new MongoResultsWriter(uri) : new HttpResultsWriter(uri); + } + + /** + * Convert {@link RunResult}s to JMH Json representation. + * + * @param results + * @return json string representation of results. + * @see org.openjdk.jmh.results.format.JSONResultFormat + */ + static String jsonifyResults(Collection results) { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ResultFormatFactory.getInstance(ResultFormatType.JSON, new PrintStream(baos)).writeOut(results); + return new String(baos.toByteArray(), StandardCharsets.UTF_8); + } +} From 2ead763f5c752d2c515755b155a029efad8f82b5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 12 Jul 2017 12:00:41 +0200 Subject: [PATCH 8/9] DATAMONGO-1720 - Polishing. Enhance benchmark statistics with Git/working tree details. Specify byte encoding for JSON to byte encoder. Add status code check to HttpResultsWriter to verify that the results were accepted. Convert spaces to tabs in pom.xml. --- spring-data-mongodb-benchmarks/pom.xml | 174 ++++++++++-------- .../microbenchmark/HttpResultsWriter.java | 39 +++- .../microbenchmark/MongoResultsWriter.java | 7 +- .../data/microbenchmark/ResultsWriter.java | 6 +- .../MappingMongoConverterBenchmark.java | 2 +- 5 files changed, 136 insertions(+), 92 deletions(-) diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 9e07aa1300..588f24813f 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -1,91 +1,107 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - 4.0.0 + 4.0.0 - - org.springframework.data - spring-data-mongodb-parent - 2.0.0.DATAMONGO-1720-SNAPSHOT - ../pom.xml - + + org.springframework.data + spring-data-mongodb-parent + 2.0.0.DATAMONGO-1720-SNAPSHOT + ../pom.xml + - spring-data-mongodb-benchmarks - jar + spring-data-mongodb-benchmarks + jar - Spring Data MongoDB - Microbenchmarks + Spring Data MongoDB - Microbenchmarks - - true - false - + + true + + false + - - - ${project.groupId} - spring-data-mongodb - ${project.version} - - - junit - junit - ${junit} - compile - - - org.openjdk.jmh - jmh-core - ${jmh.version} - - - org.openjdk.jmh - jmh-generator-annprocess - ${jmh.version} - provided - - + + + ${project.groupId} + spring-data-mongodb + ${project.version} + + + junit + junit + ${junit} + compile + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + - + - - benchmarks - - false - - - + + benchmarks + + false + + + - - - - maven-jar-plugin - - - default-jar - never - - - - - maven-surefire-plugin - - ${project.build.sourceDirectory} - ${project.build.outputDirectory} - - **/AbstractMicrobenchmark.java - **/*$*.class - **/generated/*.class - - - **/*Benchmark* - - - ${project.build.directory}/reports/performance - ${project.version} - - - - - + + + + pl.project13.maven + git-commit-id-plugin + 2.2.2 + + + + revision + + + + + + maven-jar-plugin + + + default-jar + never + + + + + maven-surefire-plugin + + ${project.build.sourceDirectory} + ${project.build.outputDirectory} + + **/AbstractMicrobenchmark.java + **/*$*.class + **/generated/*.class + + + **/*Benchmark* + + + ${project.build.directory}/reports/performance + ${project.version} + ${git.dirty} + ${git.commit.id} + ${git.branch} + + + + + diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java index dbd45802e5..33fa075d2b 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java @@ -15,7 +15,10 @@ */ package spring.data.microbenchmark; +import lombok.SneakyThrows; + import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; @@ -23,6 +26,7 @@ import java.util.Collection; import org.openjdk.jmh.results.RunResult; +import org.springframework.core.env.StandardEnvironment; import org.springframework.util.CollectionUtils; /** @@ -39,24 +43,39 @@ class HttpResultsWriter implements ResultsWriter { } @Override + @SneakyThrows public void write(Collection results) { if (CollectionUtils.isEmpty(results)) { return; } - try { + StandardEnvironment env = new StandardEnvironment(); + + String projectVersion = env.getProperty("project.version", "unknown"); + String gitBranch = env.getProperty("git.branch", "unknown"); + String gitDirty = env.getProperty("git.dirty", "no"); + String gitCommitId = env.getProperty("git.commit.id", "unknown"); + + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setConnectTimeout((int) Duration.ofSeconds(1).toMillis()); + connection.setReadTimeout((int) Duration.ofSeconds(1).toMillis()); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); - URLConnection connection = new URL(url).openConnection(); - connection.setConnectTimeout((int) Duration.ofSeconds(1).toMillis()); - connection.setDoOutput(true); - connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("X-Project-Version", projectVersion); + connection.addRequestProperty("X-Git-Branch", gitBranch); + connection.addRequestProperty("X-Git-Dirty", gitDirty); + connection.addRequestProperty("X-Git-Commit-Id", gitCommitId); + + try (OutputStream output = connection.getOutputStream()) { + output.write(ResultsWriter.jsonifyResults(results).getBytes(StandardCharsets.UTF_8)); + } - try (OutputStream output = connection.getOutputStream()) { - output.write(ResultsWriter.jsonifyResults(results).getBytes(StandardCharsets.UTF_8)); - } - } catch (Exception e) { - throw new RuntimeException(e); + if (connection.getResponseCode() >= 400) { + throw new IllegalStateException( + String.format("Status %d %s", connection.getResponseCode(), connection.getResponseMessage())); } } } diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java index 9fa8d4f756..c24814ff63 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java @@ -52,6 +52,9 @@ public void write(Collection results) { StandardEnvironment env = new StandardEnvironment(); String projectVersion = env.getProperty("project.version", "unknown"); + String gitBranch = env.getProperty("git.branch", "unknown"); + String gitDirty = env.getProperty("git.dirty", "no"); + String gitCommitId = env.getProperty("git.commit.id", "unknown"); MongoClientURI uri = new MongoClientURI(this.uri); MongoClient client = new MongoClient(uri); @@ -65,6 +68,9 @@ public void write(Collection results) { Document sink = new Document(); sink.append("_version", projectVersion); + sink.append("_branch", gitBranch); + sink.append("_commit", gitCommitId); + sink.append("_dirty", gitDirty); sink.append("_method", extractBenchmarkName(dbo.get("benchmark").toString())); sink.append("_date", now); sink.append("_snapshot", projectVersion.toLowerCase().contains("snapshot")); @@ -75,7 +81,6 @@ public void write(Collection results) { } client.close(); - } /** diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java index 8431a236b4..e1c2b54e7b 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java @@ -15,6 +15,8 @@ */ package spring.data.microbenchmark; +import lombok.SneakyThrows; + import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; @@ -54,10 +56,12 @@ static ResultsWriter forUri(String uri) { * @return json string representation of results. * @see org.openjdk.jmh.results.format.JSONResultFormat */ + @SneakyThrows static String jsonifyResults(Collection results) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ResultFormatFactory.getInstance(ResultFormatType.JSON, new PrintStream(baos)).writeOut(results); + ResultFormatFactory.getInstance(ResultFormatType.JSON, new PrintStream(baos, true, "UTF-8")).writeOut(results); + return new String(baos.toByteArray(), StandardCharsets.UTF_8); } } diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java index a985d517b7..7f169f6db9 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java @@ -130,7 +130,7 @@ public Customer readObjectWith2Properties() { } @Benchmark // DATAMONGO-1720 - public Customer readObjectWith2tPropertiesAnd1NestedObject() { + public Customer readObjectWith2PropertiesAnd1NestedObject() { return converter.read(Customer.class, documentWith2PropertiesAnd1Nested); } From 001fcb06e743a8099be28a7278526891e9087911 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 13 Jul 2017 14:18:25 +0200 Subject: [PATCH 9/9] DATAMONGO-1720 - Update package names. --- .../mongodb/core/convert/DbRefMappingBenchmark.java | 4 ++-- .../core/convert/MappingMongoConverterBenchmark.java | 10 +++------- .../microbenchmark/AbstractMicrobenchmark.java | 2 +- .../mongodb}/microbenchmark/HttpResultsWriter.java | 2 +- .../mongodb}/microbenchmark/MongoResultsWriter.java | 2 +- .../data/mongodb}/microbenchmark/ResultsWriter.java | 2 +- 6 files changed, 9 insertions(+), 13 deletions(-) rename spring-data-mongodb-benchmarks/src/main/java/{spring => org/springframework}/data/mongodb/core/convert/DbRefMappingBenchmark.java (95%) rename spring-data-mongodb-benchmarks/src/main/java/{spring => org/springframework}/data/mongodb/core/convert/MappingMongoConverterBenchmark.java (93%) rename spring-data-mongodb-benchmarks/src/main/java/{spring/data => org/springframework/data/mongodb}/microbenchmark/AbstractMicrobenchmark.java (99%) rename spring-data-mongodb-benchmarks/src/main/java/{spring/data => org/springframework/data/mongodb}/microbenchmark/HttpResultsWriter.java (97%) rename spring-data-mongodb-benchmarks/src/main/java/{spring/data => org/springframework/data/mongodb}/microbenchmark/MongoResultsWriter.java (98%) rename spring-data-mongodb-benchmarks/src/main/java/{spring/data => org/springframework/data/mongodb}/microbenchmark/ResultsWriter.java (97%) diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/core/convert/DbRefMappingBenchmark.java similarity index 95% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingBenchmark.java rename to spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/core/convert/DbRefMappingBenchmark.java index 9566123cbd..4db027c1a4 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/DbRefMappingBenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/core/convert/DbRefMappingBenchmark.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package spring.data.mongodb.core.convert; +package org.springframework.data.mongodb.core.convert; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; import lombok.Data; -import spring.data.microbenchmark.AbstractMicrobenchmark; import java.util.ArrayList; import java.util.List; @@ -34,6 +33,7 @@ import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.microbenchmark.AbstractMicrobenchmark; import com.mongodb.MongoClient; import com.mongodb.ServerAddress; diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterBenchmark.java similarity index 93% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java rename to spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterBenchmark.java index 7f169f6db9..b51c8ccfca 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/mongodb/core/convert/MappingMongoConverterBenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterBenchmark.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package spring.data.mongodb.core.convert; +package org.springframework.data.mongodb.core.convert; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; -import spring.data.microbenchmark.AbstractMicrobenchmark; import java.util.Arrays; import java.util.Collections; @@ -38,12 +37,9 @@ import org.springframework.data.annotation.Id; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.microbenchmark.AbstractMicrobenchmark; import com.mongodb.MongoClient; import com.mongodb.ServerAddress; @@ -113,7 +109,7 @@ public void setUp() throws Exception { // JSON equivalent of objectWithFlatAndComplexPropertiesPlusListAndMap documentWithFlatAndComplexPropertiesPlusListAndMap = Document.parse( - "{ \"_id\" : \"517f6aee-e9e0-44f0-88ed-f3694a019f27\", \"intOne\" : -2147483648, \"intTwo\" : 2147483647, \"stringOne\" : \"¯\\\\_(ツ)_/¯\", \"stringTwo\" : \" (╯°□°)╯︵ ┻━┻\", \"explicit-field-name\" : \"supercalifragilisticexpialidocious\", \"location\" : { \"x\" : -33.865143, \"y\" : 151.2099 }, \"objectWith2PropertiesAnd1Nested\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"addressList\" : [{ \"zipCode\" : \"zip-1\", \"city\" : \"city-1\" }, { \"zipCode\" : \"zip-2\", \"city\" : \"city-2\" }], \"customerMap\" : { \"dave\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"deborah\" : { \"firstname\" : \"Deborah Anne\", \"lastname\" : \"Dyer\", \"address\" : { \"zipCode\" : \"?\", \"city\" : \"london\" } }, \"eddie\" : { \"firstname\" : \"Eddie\", \"lastname\" : \"Vedder\", \"address\" : { \"zipCode\" : \"??\", \"city\" : \"Seattle\" } } }, \"_class\" : \"spring.data.mongodb.core.convert.MappingMongoConverterBenchmark$SlightlyMoreComplexObject\" }"); + "{ \"_id\" : \"517f6aee-e9e0-44f0-88ed-f3694a019f27\", \"intOne\" : -2147483648, \"intTwo\" : 2147483647, \"stringOne\" : \"¯\\\\_(ツ)_/¯\", \"stringTwo\" : \" (╯°□°)╯︵ ┻━┻\", \"explicit-field-name\" : \"supercalifragilisticexpialidocious\", \"location\" : { \"x\" : -33.865143, \"y\" : 151.2099 }, \"objectWith2PropertiesAnd1Nested\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"addressList\" : [{ \"zipCode\" : \"zip-1\", \"city\" : \"city-1\" }, { \"zipCode\" : \"zip-2\", \"city\" : \"city-2\" }], \"customerMap\" : { \"dave\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"deborah\" : { \"firstname\" : \"Deborah Anne\", \"lastname\" : \"Dyer\", \"address\" : { \"zipCode\" : \"?\", \"city\" : \"london\" } }, \"eddie\" : { \"firstname\" : \"Eddie\", \"lastname\" : \"Vedder\", \"address\" : { \"zipCode\" : \"??\", \"city\" : \"Seattle\" } } }, \"_class\" : \"org.springframework.data.mongodb.core.convert.MappingMongoConverterBenchmark$SlightlyMoreComplexObject\" }"); } diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/AbstractMicrobenchmark.java similarity index 99% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java rename to spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/AbstractMicrobenchmark.java index 9496835602..807f960a23 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/AbstractMicrobenchmark.java +++ b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/AbstractMicrobenchmark.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package spring.data.microbenchmark; +package org.springframework.data.mongodb.microbenchmark; import java.io.File; import java.io.IOException; diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/HttpResultsWriter.java similarity index 97% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java rename to spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/HttpResultsWriter.java index 33fa075d2b..8c95041029 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/HttpResultsWriter.java +++ b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/HttpResultsWriter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package spring.data.microbenchmark; +package org.springframework.data.mongodb.microbenchmark; import lombok.SneakyThrows; diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/MongoResultsWriter.java similarity index 98% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java rename to spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/MongoResultsWriter.java index c24814ff63..d4c7f4646d 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/MongoResultsWriter.java +++ b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/MongoResultsWriter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package spring.data.microbenchmark; +package org.springframework.data.mongodb.microbenchmark; import java.util.Collection; import java.util.Date; diff --git a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/ResultsWriter.java similarity index 97% rename from spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java rename to spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/ResultsWriter.java index e1c2b54e7b..3aaaee665d 100644 --- a/spring-data-mongodb-benchmarks/src/main/java/spring/data/microbenchmark/ResultsWriter.java +++ b/spring-data-mongodb-benchmarks/src/main/java/org/springframework/data/mongodb/microbenchmark/ResultsWriter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package spring.data.microbenchmark; +package org.springframework.data.mongodb.microbenchmark; import lombok.SneakyThrows;