Skip to content

Conversation

@tyvsmith
Copy link
Member

Before pressing the "Create Pull Request" button, please provide the following:

  - [x] A description about what and why you are contributing, even if it's trivial.
This PR migrates the `simplestore` module from Java to Kotlin, replacing `ListenableFuture` with Kotlin coroutines for asynchronous operations. It includes adding coroutines dependencies, converting all Java source files in the module to Kotlin, and providing `ListenableFuture` extension functions to maintain API compatibility for Java callers. Additionally, Gradle and plugin versions were updated to resolve Java 21 compatibility issues.

  - [ ] The issue number(s) or PR number(s) in the description if you are contributing in response to those.
N/A

  - [ ] If applicable, unit tests.
Existing unit tests were run to verify the migration. Test source code migration will follow in a subsequent PR.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Member

@matt-ramotar matt-ramotar left a comment

Choose a reason for hiding this comment

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

🙌


override suspend fun put(key: String, value: ByteArray?): ByteArray {
requireOpen()
return withContext(Dispatchers.IO) {
Copy link
Member

Choose a reason for hiding this comment

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

If I'm reading this correctly, in the original Java impl, every get, put, contains call happens on a single-threaded executor. That executor guarantees that tasks are processed FIFO one at a time. The Kotlin impl is moving work off of that single queue to a shared Dispatchers.IO pool. To maintain ordering and prevent races, I think we will need a dedicated single-threaded dispatcher (or sequential executor). I don't think a Mutex would be sufficient because overall ordering of operations would still be non deterministic

*/
fun SimpleStore.getStringFuture(key: String): ListenableFuture<String> {
return kotlinx.coroutines.guava.asListenableFuture(
kotlinx.coroutines.GlobalScope.async { getString(key) }
Copy link
Member

Choose a reason for hiding this comment

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

Agree Java consumers shouldn't need to know anything about coroutines, but concerned about GlobalScope. In the Java version, lifecycle management is handled entirely inside the store impl. I think we could replicate this in Kotlin without exposing coroutines to Java callers by hiding a default scope inside the store impl. For example, defining an internal interface that exposes a scope, having our impl implement it, then having the extension cast

import java.io.Closeable

/** Fast, reliable storage. */
interface SimpleStore : Closeable {
Copy link
Member

Choose a reason for hiding this comment

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

🔥

@kurtisnelson
Copy link
Contributor

Would it be useful to break this into two PRs, one to convert to Kotlin but still use ListenableFuture? Moving straight to Kotlin coroutine scopes is risky since the original impl very purposely handled ordering internally.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants