Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlin coroutine support #2524

Closed
wants to merge 1 commit into from
Closed

Conversation

hgschmie
Copy link
Contributor

@hgschmie hgschmie commented Oct 28, 2023

Support Kotlin coroutines

Allows using Jdbi objects (handles and extension objects) with kotlin coroutines. Objects can be shared between coroutines (even across threads) as long as the caller can guarantee that not multiple coroutines use them at the same time.

@hgschmie
Copy link
Contributor Author

@anderssv Feedback very much welcome. I am not exactly a Kotlin person so I kind of puzzled this together with some docs, common sense and trial-and-error. This could benefit from review / feedback from people that actually use Jdbi with Kotlin.

@hgschmie
Copy link
Contributor Author

This addresses #2522

return decoratedCallback.withHandle(h);
} finally {
threadHandleSupplier.remove();
getHandleSupplierHolder().clearHandleSupplier();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you look up the handle supplier holder here again? seems like the lookup can be done once, and you always set / clear on the same instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move the review of the handle supplier holder code to #2523 ?

@@ -521,16 +532,16 @@ public <X extends Exception> void useTransaction(final TransactionIsolationLevel
*/
public <R, E, X extends Exception> R withExtension(Class<E> extensionType, ExtensionCallback<R, E, X> callback)
throws X {
HandleSupplier handleSupplier = threadHandleSupplier.get();
HandleSupplier handleSupplier = getHandleSupplierHolder().getHandleSupplier();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, feels like we could re-use the same handleSupplierHolder

* @param <T> type parameter
*/
@Alpha
public <T extends HandleSupplierHolder> T getHandleSupplierHolder() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is not intended as public API, can we restrict visibility? Also, I think @Alpha does not need to be applied to non-public api, since it is always subject to change at any time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not. This is specifically intended to be used by the Kotlin code to allow doing this:

fun Jdbi.attachToContext(): CoroutineContext.Element = this.getHandleSupplierHolder<CoroutineAwareHandleSupplierHolder>().contextElement()

This allows attaching the Jdbi object to a coroutine context so that the handles in the internal threadlocal are moved around correctly.

The extension function above is (in terms of java code) a hidden static function (so basically it is

public static CoroutineContext.Element attachToContext() {
    return Jdbi<CoroutineAwareHandleSupplierHolder>getHandleSupplierHolder().contextElement();
}

"patched" into the Jdbi class. And that is only possible with public members (because the extension method lives in a different package (org.jdbi.v3.core.kotlin) and is not an actual member of the hierarchy, so neither protected nor package scope would work).

*/
@Alpha
public <T extends HandleSupplierHolder> T getHandleSupplierHolder() {
return (T) handleSupplierHolder.updateAndGet(x -> x != null ? x : config.get(Handles.class).getHandleSupplierHolder());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a little weird if you ever try to change the handle supplier holder, since this reference will never update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It allows changing the implementation class to be modified by calling

Jdbi jdbi = Jdbi.create(...);
jdbi.getConfig(Handles.class).setHandleSupplierHolderClass(MyMagicHandleSupplierHolder.class)
... more code

If the implementation would be created when the Jdbi is created, it could not be changed by configuration. If we allow changing at any time, there is a good chance that we might leak handles.

Open for better ideas, this is what I came up with.

@hgschmie hgschmie force-pushed the handle-coroutines branch 2 times, most recently from 4c1f145 to 32264da Compare October 29, 2023 00:00
@hgschmie hgschmie marked this pull request as draft October 29, 2023 01:19
@hgschmie
Copy link
Contributor Author

keep this in draft until we have hashed out #2523 and I figured out the sporadically failing tests.

@hgschmie hgschmie force-pushed the handle-coroutines branch 10 times, most recently from d0c4a20 to cac69ea Compare November 7, 2023 06:23
@hgschmie hgschmie force-pushed the handle-coroutines branch 3 times, most recently from cbdaa0f to e2cdcc9 Compare November 13, 2023 05:42
Allows using Jdbi objects (handles and extension objects) with kotlin
coroutines. Objects can be shared between coroutines (even across threads)
as long as the caller can guarantee that not multiple coroutines use them
at the same time.
Copy link

sonarcloud bot commented Nov 17, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@anderssv
Copy link

Awesome work guys! Sorry I didn't see the request for input earlier. I have only a rudimentary understanding of Coroutines myself, but on the surface it looks good to me.

May I suggest popping by the Kotlin Slack and see if anyone in the coroutines channel has any input? :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants