@@ -15,11 +15,6 @@ import io.r2dbc.postgresql.PostgresqlConnectionFactory
1515import io.r2dbc.postgresql.client.SSLMode
1616import io.r2dbc.spi.Connection
1717import io.r2dbc.spi.ConnectionFactory
18- import kotlinx.coroutines.async
19- import kotlinx.coroutines.awaitAll
20- import kotlinx.coroutines.coroutineScope
21- import kotlinx.coroutines.flow.Flow
22- import kotlinx.coroutines.flow.flow
2318import kotlinx.coroutines.reactive.awaitFirst
2419import kotlinx.coroutines.reactive.awaitFirstOrNull
2520import kotlinx.coroutines.reactor.awaitSingle
@@ -28,7 +23,7 @@ import reactor.core.publisher.Flux
2823import reactor.core.publisher.Mono
2924import java.time.Duration
3025import java.util.concurrent.ThreadLocalRandom
31- import kotlin.random.Random
26+ import kotlin.math.min
3227
3328const val HELLO_WORLD = " Hello, World!"
3429const val WORLD_QUERY = " SELECT id, randomnumber FROM world WHERE id = $1"
@@ -54,34 +49,18 @@ fun Application.main() {
5449 }
5550
5651 get(" /db" ) {
57- val request = getWorld(dbConnFactory)
58- val result = request.awaitFirstOrNull()
59-
60- call.respondJson(result)
52+ val world = dbConnFactory.fetchWorld()
53+ call.respondJson(world)
6154 }
6255
6356 get(" /queries" ) {
6457 val queries = call.queries()
65-
66- val result = fetchWorldsConcurrently(dbConnFactory, queries)
67-
68- call.respondJson(result)
58+ val worlds = dbConnFactory.fetchWorlds(queries)
59+ call.respondJson(worlds)
6960 }
7061
7162 get(" /fortunes" ) {
72- val result = mutableListOf<Fortune >()
73-
74- val request = Flux .usingWhen(dbConnFactory.create(), { connection ->
75- Flux .from(connection.createStatement(FORTUNES_QUERY ).execute()).flatMap { r ->
76- Flux .from(r.map { row, _ ->
77- Fortune (
78- row.get(0 , Int ::class .java)!! , row.get(1 , String ::class .java)!!
79- )
80- })
81- }
82- }, { connection -> connection.close() })
83-
84- request.collectList().awaitFirstOrNull()?.let { result.addAll(it) }
63+ val result = dbConnFactory.fetchFortunes().toMutableList()
8564
8665 result.add(Fortune (0 , " Additional fortune added at request time." ))
8766 result.sortBy { it.message }
@@ -107,7 +86,7 @@ fun Application.main() {
10786 get(" /updates" ) {
10887 val queries = call.queries()
10988
110- val worlds = fetchWorldsConcurrently( dbConnFactory, queries)
89+ val worlds = dbConnFactory.fetchWorlds( queries)
11190 val updatedWorlds = worlds.map {
11291 it.copy(randomNumber = ThreadLocalRandom .current().nextInt(1 , DB_ROWS + 1 ))
11392 }.sortedBy { it.id }
@@ -137,30 +116,55 @@ fun Application.main() {
137116 }
138117}
139118
140- private fun getWorld (
141- dbConnFactory : ConnectionFactory , random : ThreadLocalRandom = ThreadLocalRandom .current()
142- ): Mono <World > = Mono .usingWhen(dbConnFactory.create(), { connection ->
143- Mono .from(connection.createStatement(WORLD_QUERY )
144- .bind(" $1" , random.nextInt(DB_ROWS ) + 1 )
145- .execute())
146- .flatMap { result ->
147- Mono .from(result.map { row, _ ->
148- World (
149- row.get(0 , Int ::class .java)
150- ? : error(" id is null" ),
151- row.get(1 , Int ::class .java)
152- ? : error(" randomNumber is null" )
153- )
154- })
155- }
156- }, Connection ::close)
119+ private suspend fun ConnectionFactory.fetchWorld (): World =
120+ Mono .usingWhen(create(), { connection ->
121+ selectWorld(connection)
122+ }, Connection ::close).awaitSingle()
123+
124+ private suspend fun ConnectionFactory.fetchWorlds (
125+ count : Int
126+ ): List <World > {
127+ if (count <= 0 ) return emptyList()
128+ val concurrency = min(count, 32 )
129+ return Mono .usingWhen(create(), { connection ->
130+ Flux .range(0 , count)
131+ .flatMap({ selectWorldPublisher(connection) }, concurrency)
132+ .collectList()
133+ }, Connection ::close).awaitSingle()
134+ }
157135
158- suspend fun fetchWorldsConcurrently (factory : ConnectionFactory , count : Int ): List <World > =
159- coroutineScope {
160- (0 until count).map {
161- async { getWorld(factory).awaitSingle() }
162- }.awaitAll()
136+ private fun selectWorld (connection : Connection ): Mono <World > =
137+ selectWorldPublisher(connection)
138+
139+ private fun selectWorldPublisher (connection : Connection ): Mono <World > {
140+ val worldId = ThreadLocalRandom .current().nextInt(1 , DB_ROWS + 1 )
141+ return Mono .from(
142+ connection.createStatement(WORLD_QUERY )
143+ .bind(" $1" , worldId)
144+ .execute()
145+ ).flatMap { result ->
146+ Mono .from(result.map { row, _ ->
147+ World (
148+ row.get(0 , Int ::class .java) ? : error(" id is null" ),
149+ row.get(1 , Int ::class .java) ? : error(" randomNumber is null" )
150+ )
151+ })
163152 }
153+ }
154+
155+ private suspend fun ConnectionFactory.fetchFortunes (): List <Fortune > =
156+ Mono .usingWhen(create(), { connection ->
157+ Flux .from(connection.createStatement(FORTUNES_QUERY ).execute())
158+ .flatMap { result ->
159+ Flux .from(result.map { row, _ ->
160+ Fortune (
161+ row.get(0 , Int ::class .java) ? : error(" id is null" ),
162+ row.get(1 , String ::class .java) ? : error(" message is null" )
163+ )
164+ })
165+ }
166+ .collectList()
167+ }, Connection ::close).awaitSingle()
164168
165169private fun configurePostgresR2DBC (config : ApplicationConfig ): ConnectionFactory {
166170 val cfo = PostgresqlConnectionConfiguration .builder()
0 commit comments