Skip to content

Simplify TransactionalOperator.executeAndAwait by removing Optional #36039

@vlsi

Description

@vlsi

Current code:

suspend fun <T> TransactionalOperator.executeAndAwait(f: suspend (ReactiveTransaction) -> T): T {
val context = currentCoroutineContext().minusKey(Job.Key)
return execute { status -> mono(context) { f(status) } }.map { value -> Optional.ofNullable(value) }
.defaultIfEmpty(Optional.empty()).awaitLast().orElse(null)

Recommendation:

suspend fun <T> TransactionalOperator.executeAndAwait(f: suspend (ReactiveTransaction) -> T): T {
	val context = currentCoroutineContext().minusKey(Job.Key)
	val flux = execute { status -> mono<T & Any>(context) { f(status) } }
	// The only reason Mono<T> could produce empty result is via f(status) producing null
	// so it fine to case null to T here.
	@Suppress("UNCHECKED_CAST")
	return flux.awaitFirstOrNull() as T
}

There's no need to use Optional, and going with Optional.orElse(null) defeats null safety.
As far as I understand, Flux<T> will never produce null values (they sould produce empty Flux instead), so flux.map { Optional.ofNullable(it) } is a pure overhead.

As far as I understand, executeAndAwait might yield null for two reasons:
a) User-provided code returned null from f(status) call. In that case, awaitFirstOrNull() is perfectly valid to return null
b) Some cancellation happens (e.g. coroutine cancelation or flux cancellation/timeout) before f(status) completes. I'm not sure it is possible, however, it if is possible, then it could result in null value returned without clear indication of the reason. In any case, previously the code was silently converting empty value to null, so my suggestion does not seem to make the case worse than it was.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions