Skip to content

Commit 83abea6

Browse files
Ilya NemtsevIlya Nemtsev
authored andcommitted
fixed test and some perf bugs
1 parent 7286bd0 commit 83abea6

File tree

5 files changed

+187
-22
lines changed

5 files changed

+187
-22
lines changed

frameworks/Kotlin/ktor/ktor-r2dbc.dockerfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
FROM maven:3.9.9-amazoncorretto-21-debian-bookworm as maven
2-
WORKDIR /ktor
3-
COPY ktor/pom.xml pom.xml
4-
COPY ktor/src src
2+
WORKDIR /ktor-r2dbc
3+
COPY ktor-r2dbc/pom.xml pom.xml
4+
COPY ktor-r2dbc/src src
55
RUN mvn clean package -q
66

77
FROM amazoncorretto:21-al2023-headless
8-
WORKDIR /ktor
9-
COPY --from=maven /ktor/target/tech-empower-framework-benchmark-1.0-SNAPSHOT-netty-bundle.jar app.jar
8+
WORKDIR /ktor-r2dbc
9+
COPY --from=maven /ktor-r2dbc/target/tech-empower-framework-benchmark-1.0-SNAPSHOT-netty-bundle.jar app.jar
1010

1111
EXPOSE 9090
1212

frameworks/Kotlin/ktor/ktor-r2dbc/pom.xml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
<serialization.version>1.7.3</serialization.version>
1919
<kotlinx.html.version>0.11.0</kotlinx.html.version>
2020
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21-
<logback.version>1.2.13</logback.version>
21+
<logback.version>1.5.12</logback.version>
22+
<reactor.version>3.7.1</reactor.version>
2223
<postgresql.version>42.7.4</postgresql.version>
2324
<r2dbc.version>1.0.7.RELEASE</r2dbc.version>
25+
<r2dbc.pool.version>1.0.2.RELEASE</r2dbc.pool.version>
26+
2427
</properties>
2528

2629
<dependencies>
@@ -64,13 +67,21 @@
6467
<artifactId>ktor-server-html-builder-jvm</artifactId>
6568
<version>${ktor.version}</version>
6669
</dependency>
67-
6870
<dependency>
6971
<groupId>org.postgresql</groupId>
7072
<artifactId>r2dbc-postgresql</artifactId>
7173
<version>${r2dbc.version}</version>
7274
</dependency>
73-
75+
<dependency>
76+
<groupId>io.r2dbc</groupId>
77+
<artifactId>r2dbc-pool</artifactId>
78+
<version>${r2dbc.pool.version}</version>
79+
</dependency>
80+
<dependency>
81+
<groupId>io.projectreactor</groupId>
82+
<artifactId>reactor-core</artifactId>
83+
<version>${reactor.version}</version>
84+
</dependency>
7485
<dependency>
7586
<groupId>ch.qos.logback</groupId>
7687
<artifactId>logback-classic</artifactId>

frameworks/Kotlin/ktor/ktor-r2dbc/src/main/kotlin/org/jetbrains/ktor/benchmarks/Hello.kt

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import io.ktor.server.html.*
88
import io.ktor.server.plugins.defaultheaders.*
99
import io.ktor.server.response.*
1010
import io.ktor.server.routing.*
11+
import io.r2dbc.pool.ConnectionPool
12+
import io.r2dbc.pool.ConnectionPoolConfiguration
13+
import io.r2dbc.pool.PoolingConnectionFactoryProvider
14+
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration
15+
import io.r2dbc.postgresql.PostgresqlConnectionFactory
16+
import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider
17+
import io.r2dbc.postgresql.client.SSLMode
18+
import io.r2dbc.spi.Connection
1119
import io.r2dbc.spi.ConnectionFactories
1220
import io.r2dbc.spi.ConnectionFactory
1321
import io.r2dbc.spi.ConnectionFactoryOptions
@@ -25,6 +33,8 @@ import org.jetbrains.ktor.benchmarks.models.Message
2533
import org.jetbrains.ktor.benchmarks.models.World
2634
import reactor.core.publisher.Flux
2735
import reactor.core.publisher.Mono
36+
import reactor.netty.resources.LoopResources
37+
import java.time.Duration
2838
import kotlin.random.Random
2939

3040
fun Application.main() {
@@ -125,7 +135,7 @@ fun Application.main() {
125135
connection.createStatement(UPDATE_QUERY).bind(0, world.randomNumber).bind(1, world.id)
126136
.execute()
127137
).flatMap { Mono.from(it.rowsUpdated) }
128-
}, { connection -> connection.close() })
138+
}, Connection::close)
129139
}
130140

131141
Flux.merge(updateRequests).collectList().awaitFirstOrNull()
@@ -147,23 +157,38 @@ private fun getWorld(
147157
)
148158
})
149159
}
150-
}, { connection -> connection.close() })
160+
}, Connection::close)
151161

152162
private fun configurePostgresR2DBC(config: ApplicationConfig): ConnectionFactory {
153-
val options = ConnectionFactoryOptions.builder().option(ConnectionFactoryOptions.DRIVER, "database.driver")
154-
.option(ConnectionFactoryOptions.DATABASE, config.property("database.url").getString())
155-
.option(ConnectionFactoryOptions.USER, config.property("database.user").getString())
156-
.option(ConnectionFactoryOptions.PASSWORD, config.property("database.password").getString()).build()
157-
158-
return ConnectionFactories.get(options)
163+
val cfo = PostgresqlConnectionConfiguration.builder()
164+
.host(config.property("db.host").getString())
165+
.port(config.property("db.port").getString().toInt())
166+
.database(config.property("db.database").getString())
167+
.username(config.property("db.username").getString())
168+
.password(config.property("db.password").getString())
169+
.loopResources { NioClientEventLoopResources(Runtime.getRuntime().availableProcessors()).cacheLoops() }
170+
.sslMode(SSLMode.DISABLE)
171+
.tcpKeepAlive(true)
172+
.tcpNoDelay(true)
173+
.build()
174+
175+
val cf = PostgresqlConnectionFactory(cfo)
176+
177+
val cp = ConnectionPoolConfiguration.builder(cf)
178+
.initialSize(config.property("db.initPoolSize").getString().toInt())
179+
.maxSize(config.property("db.maxPoolSize").getString().toInt())
180+
//.maxLifeTime(Duration.ofMillis(Long.MAX_VALUE))
181+
.build()
182+
183+
return ConnectionPool(cp)
159184
}
160185

161186
private fun ApplicationCall.queries() = request.queryParameters["queries"]?.toIntOrNull()?.coerceIn(1, 500) ?: 1
162187

163188

164189
object Constants {
165-
const val WORLD_QUERY = "SELECT id, randomNumber FROM World WHERE id = ?"
190+
const val WORLD_QUERY = "SELECT id, randomnumber FROM world WHERE id = $1"
166191
const val FORTUNES_QUERY = "SELECT id, message FROM fortune"
167-
const val UPDATE_QUERY = "UPDATE World SET randomNumber = ? WHERE id = ?"
192+
const val UPDATE_QUERY = "UPDATE world SET randomnumber = $1 WHERE id = $2"
168193
const val DB_ROWS = 10000
169194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package org.jetbrains.ktor.benchmarks;
2+
3+
import io.netty.channel.Channel;
4+
import io.netty.channel.EventLoopGroup;
5+
import io.netty.channel.nio.NioEventLoopGroup;
6+
import io.netty.channel.socket.DatagramChannel;
7+
import io.netty.channel.socket.ServerSocketChannel;
8+
import io.netty.channel.socket.SocketChannel;
9+
import io.netty.channel.socket.nio.NioDatagramChannel;
10+
import io.netty.channel.socket.nio.NioServerSocketChannel;
11+
import io.netty.channel.socket.nio.NioSocketChannel;
12+
import io.netty.util.concurrent.DefaultThreadFactory;
13+
import io.netty.util.concurrent.Future;
14+
import io.netty.util.concurrent.ThreadPerTaskExecutor;
15+
import reactor.core.publisher.Mono;
16+
import reactor.netty.FutureMono;
17+
import reactor.netty.resources.LoopResources;
18+
19+
import java.time.Duration;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.concurrent.atomic.AtomicBoolean;
22+
import java.util.concurrent.atomic.AtomicReference;
23+
24+
/**
25+
* Copied from GitHub issue comment: https://github.com/r2dbc/r2dbc-pool/issues/190#issuecomment-1566845190
26+
*/
27+
public class NioClientEventLoopResources implements LoopResources {
28+
public static final String THREAD_PREFIX = "prefix-";
29+
final int threads;
30+
final AtomicReference<EventLoopGroup> loops = new AtomicReference<>();
31+
final AtomicBoolean running;
32+
33+
NioClientEventLoopResources(int threads) {
34+
this.running = new AtomicBoolean(true);
35+
this.threads = threads;
36+
}
37+
38+
39+
@Override
40+
@SuppressWarnings("unchecked")
41+
public Mono<Void> disposeLater(Duration quietPeriod, Duration timeout) {
42+
return Mono.defer(() -> {
43+
long quietPeriodMillis = quietPeriod.toMillis();
44+
long timeoutMillis = timeout.toMillis();
45+
EventLoopGroup serverLoopsGroup = loops.get();
46+
Mono<?> slMono = Mono.empty();
47+
if (running.compareAndSet(true, false)) {
48+
if (serverLoopsGroup != null) {
49+
slMono = FutureMono.from((Future) serverLoopsGroup.shutdownGracefully(
50+
quietPeriodMillis, timeoutMillis, TimeUnit.MILLISECONDS));
51+
}
52+
}
53+
return Mono.when(slMono);
54+
});
55+
}
56+
57+
@Override
58+
public boolean isDisposed() {
59+
return !running.get();
60+
}
61+
62+
@Override
63+
public EventLoopGroup onClient(boolean useNative) {
64+
return cacheLoops();
65+
}
66+
67+
@Override
68+
public EventLoopGroup onServer(boolean useNative) {
69+
throw new UnsupportedOperationException("This event loop is designed only for client DB calls.");
70+
}
71+
72+
@Override
73+
public EventLoopGroup onServerSelect(boolean useNative) {
74+
throw new UnsupportedOperationException("This event loop is designed only for client DB calls.");
75+
}
76+
77+
@Override
78+
public <CHANNEL extends Channel> CHANNEL onChannel(Class<CHANNEL> channelType, EventLoopGroup group) {
79+
if (channelType.equals(SocketChannel.class)) {
80+
return (CHANNEL) new NioSocketChannel();
81+
}
82+
if (channelType.equals(ServerSocketChannel.class)) {
83+
return (CHANNEL) new NioServerSocketChannel();
84+
}
85+
if (channelType.equals(DatagramChannel.class)) {
86+
return (CHANNEL) new NioDatagramChannel();
87+
}
88+
throw new IllegalArgumentException("Unsupported channel type: " + channelType.getSimpleName());
89+
}
90+
91+
@Override
92+
public <CHANNEL extends Channel> Class<? extends CHANNEL> onChannelClass(Class<CHANNEL> channelType,
93+
EventLoopGroup group) {
94+
if (channelType.equals(SocketChannel.class)) {
95+
return (Class<? extends CHANNEL>) NioSocketChannel.class;
96+
}
97+
if (channelType.equals(ServerSocketChannel.class)) {
98+
return (Class<? extends CHANNEL>) NioServerSocketChannel.class;
99+
}
100+
if (channelType.equals(DatagramChannel.class)) {
101+
return (Class<? extends CHANNEL>) NioDatagramChannel.class;
102+
}
103+
throw new IllegalArgumentException("Unsupported channel type: " + channelType.getSimpleName());
104+
}
105+
106+
@SuppressWarnings("FutureReturnValueIgnored")
107+
EventLoopGroup cacheLoops() {
108+
EventLoopGroup eventLoopGroup = loops.get();
109+
if (null == eventLoopGroup) {
110+
EventLoopGroup newEventLoopGroup = createNewEventLoopGroup();
111+
if (!loops.compareAndSet(null, newEventLoopGroup)) {
112+
//"FutureReturnValueIgnored" this is deliberate
113+
newEventLoopGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
114+
}
115+
eventLoopGroup = cacheLoops();
116+
}
117+
return eventLoopGroup;
118+
}
119+
120+
private NioEventLoopGroup createNewEventLoopGroup() {
121+
return new NioEventLoopGroup(threads, new ThreadPerTaskExecutor(new DefaultThreadFactory(THREAD_PREFIX)));
122+
}
123+
}

frameworks/Kotlin/ktor/ktor-r2dbc/src/main/resources/application.conf

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ ktor {
1111
}
1212
}
1313

14-
database {
15-
driver = "org.postgresql.Driver"
16-
url = "url: r2dbc:pool://tfb-database:5432/hello_world?loggerLevel=OFF&sslmode=disable"
17-
poolsize = 512
14+
db {
15+
driver = "pool"
16+
protocol = "postgresql"
17+
ssl = "false"
18+
host = "tfb-database"
19+
port = 5432
20+
database = "hello_world"
21+
initPoolSize = 512
22+
maxPoolSize = 512
1823
username = "benchmarkdbuser"
1924
password = "benchmarkdbpass"
25+
//url = "r2dbc:postgresql://"${db.host}":"${db.port}"/"${db.database}"?loggerLevel=OFF&disableColumnSanitiser=true&assumeMinServerVersion=16&sslmode=disable&maxSize="${db.poolSize}
2026
}

0 commit comments

Comments
 (0)