Skip to content

Commit 808b21e

Browse files
authored
[spring] Improve database tests (#9313)
* [spring-webflux] Improve database tests - Improve the test reliability with a better random number generation mechanism - Improve the scalability of fortune test * [spring] Fix postgres deadlock The now fixed deadlock issue was only visible under high concurrency and was generating errors and leading to lower than expected performance. Sorting the worlds before updating them allows to avoid this issue.
1 parent 072be3b commit 808b21e

File tree

4 files changed

+50
-17
lines changed

4 files changed

+50
-17
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package benchmark;
2+
3+
import java.util.concurrent.ThreadLocalRandom;
4+
import java.util.stream.IntStream;
5+
6+
abstract public class Utils {
7+
8+
private static final int MIN_WORLD_NUMBER = 1;
9+
private static final int MAX_WORLD_NUMBER_PLUS_ONE = 10_001;
10+
11+
public static int randomWorldNumber() {
12+
return ThreadLocalRandom.current().nextInt(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE);
13+
}
14+
15+
public static IntStream randomWorldNumbers() {
16+
return ThreadLocalRandom.current().ints(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE).distinct();
17+
}
18+
19+
}
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package benchmark.repository;
22

3-
import benchmark.model.Fortune;
4-
import benchmark.model.World;
53
import org.springframework.context.annotation.Profile;
64
import org.springframework.r2dbc.core.DatabaseClient;
75
import org.springframework.stereotype.Component;
6+
7+
import benchmark.model.Fortune;
8+
import benchmark.model.World;
9+
import io.r2dbc.spi.Connection;
10+
import io.r2dbc.spi.ConnectionFactory;
811
import reactor.core.publisher.Flux;
912
import reactor.core.publisher.Mono;
1013

1114
@Component
1215
@Profile("r2dbc")
1316
public class R2dbcDbRepository implements DbRepository {
17+
1418
private final DatabaseClient databaseClient;
19+
private final ConnectionFactory connectionFactory;
20+
private final ThreadLocal<Mono<? extends Connection>> conn = new ThreadLocal<>();
1521

1622
public R2dbcDbRepository(DatabaseClient databaseClient) {
1723
this.databaseClient = databaseClient;
24+
this.connectionFactory = databaseClient.getConnectionFactory();
1825
}
1926

2027
@Override
@@ -24,10 +31,9 @@ public Mono<World> getWorld(int id) {
2431
.bind("$1", id)
2532
.mapProperties(World.class)
2633
.first();
27-
2834
}
2935

30-
public Mono<World> updateWorld(World world) {
36+
private Mono<World> updateWorld(World world) {
3137
return databaseClient
3238
.sql("UPDATE world SET randomnumber=$2 WHERE id = $1")
3339
.bind("$1", world.id)
@@ -37,6 +43,8 @@ public Mono<World> updateWorld(World world) {
3743
.map(count -> world);
3844
}
3945

46+
47+
@Override
4048
public Mono<World> findAndUpdateWorld(int id, int randomNumber) {
4149
return getWorld(id).flatMap(world -> {
4250
world.randomnumber = randomNumber;
@@ -46,9 +54,16 @@ public Mono<World> findAndUpdateWorld(int id, int randomNumber) {
4654

4755
@Override
4856
public Flux<Fortune> fortunes() {
49-
return databaseClient
50-
.sql("SELECT id, message FROM fortune")
51-
.mapProperties(Fortune.class)
52-
.all();
57+
return getConnection()
58+
.flatMapMany(conn -> conn.createStatement("SELECT id, message FROM " + "fortune").execute())
59+
.flatMap(result -> result.map(r -> new Fortune(r.get(0, Integer.class), r.get(1, String.class))));
5360
}
61+
62+
private Mono<? extends Connection> getConnection() {
63+
if (this.conn.get() == null) {
64+
this.conn.set(Mono.from(connectionFactory.create()).cache());
65+
}
66+
return this.conn.get();
67+
}
68+
5469
}

frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package benchmark.web;
22

33
import java.util.List;
4-
import java.util.concurrent.ThreadLocalRandom;
54

5+
import benchmark.Utils;
66
import benchmark.model.Fortune;
77
import benchmark.model.World;
88
import benchmark.repository.DbRepository;
@@ -29,7 +29,7 @@ public DbHandler(DbRepository dbRepository) {
2929
}
3030

3131
public Mono<ServerResponse> db(ServerRequest request) {
32-
int id = randomWorldNumber();
32+
int id = Utils.randomWorldNumber();
3333
Mono<World> world = dbRepository.getWorld(id)
3434
.switchIfEmpty(Mono.error(new Exception("No World found with Id: " + id)));
3535

@@ -41,8 +41,8 @@ public Mono<ServerResponse> db(ServerRequest request) {
4141
public Mono<ServerResponse> queries(ServerRequest request) {
4242
int queries = parseQueryCount(request.queryParams().getFirst("queries"));
4343

44-
Mono<List<World>> worlds = Flux.range(0, queries)
45-
.flatMap(i -> dbRepository.getWorld(randomWorldNumber()))
44+
Mono<List<World>> worlds = Flux.fromStream(Utils.randomWorldNumbers().limit(queries).boxed())
45+
.flatMap(dbRepository::getWorld)
4646
.collectList();
4747

4848
return ServerResponse.ok()
@@ -67,8 +67,8 @@ private static int parseQueryCount(String maybeTextValue) {
6767
public Mono<ServerResponse> updates(ServerRequest request) {
6868
int queries = parseQueryCount(request.queryParams().getFirst("queries"));
6969

70-
Mono<List<World>> worlds = Flux.range(0, queries)
71-
.flatMap(i -> dbRepository.findAndUpdateWorld(randomWorldNumber(), randomWorldNumber()))
70+
Mono<List<World>> worlds = Flux.fromStream(Utils.randomWorldNumbers().limit(queries).boxed())
71+
.flatMap(i -> dbRepository.findAndUpdateWorld(i, Utils.randomWorldNumber()))
7272
.collectList();
7373

7474
return ServerResponse.ok()
@@ -87,7 +87,4 @@ public Mono<ServerResponse> fortunes(ServerRequest request) {
8787
.bodyValue(JStachio.render(new Fortunes(fortunes))));
8888
}
8989

90-
private static int randomWorldNumber() {
91-
return 1 + ThreadLocalRandom.current().nextInt(10000);
92-
}
9390
}

frameworks/Java/spring/src/main/java/hello/web/DbHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package hello.web;
22

33
import java.util.Collections;
4+
import java.util.Comparator;
45
import java.util.List;
56

67
import hello.Utils;
@@ -52,6 +53,7 @@ ServerResponse updates(ServerRequest request) {
5253
world.randomNumber = randomNumber;
5354
return world;
5455
}).limit(queries)
56+
.sorted(Comparator.comparingInt(w -> w.id))
5557
.toList();
5658
dbRepository.updateWorlds(worlds);
5759
return ServerResponse.ok()

0 commit comments

Comments
 (0)