#125 - First draft of R2DBC benchmarks.

TODOs:

- Review interaction with the JDBC auto-configuration
- Wait for tcp protocol of H2 to be supported
- Wait for Postgres to accept credentialless connections (see [0])

[0] https://github.com/r2dbc/r2dbc-postgresql/issues/125
This commit is contained in:
Oliver Drotbohm
2019-08-13 11:05:53 +02:00
committed by Mark Paluch
parent ba8d634fd8
commit bdf518841b
9 changed files with 287 additions and 0 deletions

View File

@@ -13,6 +13,10 @@
<artifactId>spring-data-benchmark-relational</artifactId>
<name>Spring Data Benchmarks - Relational Microbenchmarks</name>
<properties>
<spring-boot-data-r2dbc.version>0.1.0.BUILD-SNAPSHOT</spring-boot-data-r2dbc.version>
</properties>
<dependencies>
@@ -41,6 +45,28 @@
<artifactId>postgresql</artifactId>
</dependency>
<!-- R2DBC -->
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
<version>${spring-boot-data-r2dbc.version}</version>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<version>0.8.0.M8</version>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>0.8.0.M8</version>
</dependency>
<!-- Mocking -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2019 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
*
* https://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 org.springframework.data.microbenchmark.r2dbc;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.experimental.Wither;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
/**
* @author Oliver Drotbohm
*/
@Value
@RequiredArgsConstructor(access = AccessLevel.PACKAGE, onConstructor = @__(@PersistenceConstructor))
class Book {
@Wither(AccessLevel.PACKAGE) @Id Long id;
String title;
int pages;
public Book(String title, int pages) {
this.id = null;
this.title = title;
this.pages = pages;
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2019 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
*
* https://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 org.springframework.data.microbenchmark.r2dbc;
import java.util.function.Function;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.infra.Blackhole;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.microbenchmark.common.AbstractMicrobenchmark;
import org.springframework.data.r2dbc.core.DatabaseClient;
import io.r2dbc.spi.Row;
/**
* Benchmark for R2DBC and Spring Data R2DBC
*
* @author Oliver Drotbohm
*/
public class R2dbcBenchmark extends AbstractMicrobenchmark {
private static final String FIND_ALL_SQL = "SELECT id, title, pages FROM Book";
private static final String BY_TITLE_SQL = FIND_ALL_SQL + " where title = :title";
@Param({ /* "postgres", */ "h2-in-memory" /*, "h2" */ })
String profile;
private DatabaseClient operations;
private Function<Row, Book> mapper;
private R2dbcBookRepository repository;
@Setup
public void setUp() {
R2dbcFixture fixture = new R2dbcFixture(profile);
ConfigurableApplicationContext context = fixture.getContext();
this.operations = context.getBean(DatabaseClient.class);
this.mapper = row -> new Book(row.get("id", Long.class), row.get("title", String.class), row.get("pages", Integer.class));
this.repository = context.getBean(R2dbcBookRepository.class);
}
@Benchmark
public void findByTitle(Blackhole sink) {
sink.consume(operations.execute(BY_TITLE_SQL) //
.bind("title", "title0") //
.map(mapper)
.one() //
.block());
}
@Benchmark
public void findAll(Blackhole sink) {
sink.consume(operations.execute(FIND_ALL_SQL) //
.map(mapper) //
.all() //
.collectList() //
.block());
}
@Benchmark
public void repositoryFindByTitle(Blackhole sink) {
sink.consume(repository.findByTitle("title0").block());
}
@Benchmark
public void repositoryFindTransactionalByTitle(Blackhole sink) {
sink.consume(repository.findTransactionalByTitle("title0").block());
}
@Benchmark
public void repositoryFindAll(Blackhole sink) {
sink.consume(repository.findAll().collectList().block());
}
public static void main(String[] args) {
new R2dbcFixture("postgres") //
.getContext() //
.getBean(R2dbcBookRepository.class) //
.findAll() //
.collectList() //
.block();
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2019 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
*
* https://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 org.springframework.data.microbenchmark.r2dbc;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.r2dbc.repository.query.Query;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* A repository for {@link Book} instances.
*
* @author Oliver Drotbohm
*/
interface R2dbcBookRepository extends R2dbcRepository<Book, Long> {
static final String BY_TITLE = "SELECT id, title, pages FROM Book where title = :title";
@Transactional(propagation = Propagation.NOT_SUPPORTED)
Flux<Book> findAll();
@Query(BY_TITLE)
Mono<Book> findByTitle(String title);
@Transactional(readOnly = true)
@Query(BY_TITLE)
Mono<Book> findTransactionalByTitle(String title);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2019 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
*
* https://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 org.springframework.data.microbenchmark.r2dbc;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.microbenchmark.FixtureUtils;
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;
import io.r2dbc.spi.ConnectionFactory;
import lombok.Getter;
/**
* Test fixture for JDBC and Spring Data JDBC benchmarks.
*
* @author Oliver Drotbohm
*/
public class R2dbcFixture {
private final @Getter ConfigurableApplicationContext context;
public R2dbcFixture(String database) {
this.context = FixtureUtils.createContext(R2dbcApplication.class, "r2dbc", database);
R2dbcProperties properties = context.getBean(R2dbcProperties.class);
String platform = properties.getPlatform();
Resource schema = new ClassPathResource(String.format("schema-%s.sql", platform));
Resource data = new ClassPathResource(String.format("data-%s.sql", platform));
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(schema, data);
populator.execute(context.getBean(ConnectionFactory.class)).block();
}
@SpringBootApplication (exclude = {
DataSourceAutoConfiguration.class
})
static class R2dbcApplication {}
}

View File

@@ -1,2 +1,3 @@
spring.datasource.platform=h2
spring.r2dbc.platform=h2

View File

@@ -4,3 +4,8 @@ spring.datasource.password=
spring.datasource.platform=h2
spring.datasource.initialization-mode=always
spring.r2dbc.url=r2dbc:h2:tcp://localhost:9092/~/benchmark
spring.r2dbc.username=sa
spring.r2dbc.password=
spring.r2dbc.platform=h2
spring.r2dbc.initialization-mode=always

View File

@@ -2,3 +2,6 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/benchmark
spring.datasource.platform=postgres
spring.datasource.initialization-mode=always
spring.r2dbc.url=r2dbc:postgresql://localhost:5432/benchmark
spring.r2dbc.platform=postgres
spring.r2dbc.initialization-mode=always

View File

@@ -0,0 +1,2 @@
spring.data.jpa.repositories.enabled=false
spring.data.jdbc.repositories.enabled=false