r2dbc / r2dbc-proxy

R2DBC Proxying Framework
https://r2dbc.io
Apache License 2.0
143 stars 21 forks source link

QueriesExecutionContext::isAllConsumed works different depending on signature of GenericExecuteSpec#map #138

Closed Declow0 closed 9 months ago

Declow0 commented 10 months ago

Bug Report

Different variance of map method at io.r2dbc.spi.Result work inconsistently with QueryInvocationSubscriber#afterQuery

Versions

Current Behavior

io.r2dbc.spi.Result#map(java.util.function.Function<? super io.r2dbc.spi.Readable,? extends T>)

does not trigger QueryInvocationSubscriber#afterQuery

io.r2dbc.spi.Result#map(java.util.function.BiFunction<io.r2dbc.spi.Row,io.r2dbc.spi.RowMetadata,? extends T>)

triggers QueryInvocationSubscriber#afterQuery

Steps to reproduce

I have ProxyConnectionFactory with ObservationProxyExecutionListener

val url = "r2dbc:h2:mem:///testdb;DB_CLOSE_DELAY=-1"
val connectionFactory = ConnectionFactoryBuilder
    .withUrl(url)
    .build()
ProxyConnectionFactory.builder(connectionFactory)
    .listener(
        ObservationProxyExecutionListener(ObservationRegistry.create(), connectionFactory, url).apply {
            setIncludeParameterValues(true)
        }
    )
    .build()

and repository

class Repository(
    private val databaseClient: DatabaseClient
) {
    suspend fun selectReadable(): Int? = Mono.usingWhen(
        databaseClient.connectionFactory.create(),
        { conn ->
            conn.createStatement("select 1").execute().toMono()
                .flatMap { result ->
                    result.map { readable ->
                        readable.get(0, Int::class.javaObjectType)
                    }.toMono()
                }
        },
        Connection::close
    ).awaitSingleOrNull()

    suspend fun selectBiFun(): Int? = Mono.usingWhen(
        databaseClient.connectionFactory.create(),
        { conn ->
            conn.createStatement("select 1").execute().toMono()
                .flatMap { result ->
                    result.map { row, _ ->
                        row.get(0, Int::class.javaObjectType)
                    }.toMono()
                }
        },
        Connection::close
    ).awaitSingleOrNull()
}

where result processed by different signatures of GenericExecuteSpec#map: Function or BiFunction as parameter.

When selectReadable() called then QueryInvocationSubscriber#afterQuery does not call because io.r2dbc.proxy.callback.QueriesExecutionContext#isAllConsumed is false.

Otherwise when selectBiFun() called then listener publishes QueryInvocationSubscriber#afterQuery

Expected behavior/code

Both variance of io.r2dbc.spi.Result#map trigger QueryInvocationSubscriber#afterQuery

ttddyy commented 10 months ago

@Declow0 Thanks for the report.

I'm a bit confused by GenericExecuteSpec#map but it essentially boils down to the Result#map(BiFunction) and Result#map(Function). I have root caused why it doesn't call afterQuery.

It may take some time to implement this issue properly. In the meantime, as a workaround, please use Result#map(BiFunction) to trigger the afterQuery for now.