Skip to content

Conversation

@dogi
Copy link
Member

@dogi dogi commented Nov 5, 2025

Refactored the UploadToShelfService to use a coroutine-based approach for handling data uploads. This change separates network operations from Realm database transactions to prevent long-running transactions and improve performance.

  • Replaced executeTransactionAsync with kotlinx.coroutines.launch and withContext(Dispatchers.IO).
  • Decoupled database reads, network calls, and database writes into distinct steps.
  • Removed the class-level mRealm instance in favor of short-lived, transaction-specific instances.
  • Updated uploadUserData, uploadSingleUserData, uploadToShelf, uploadSingleUserToShelf, uploadHealth, and uploadSingleUserHealth to follow the new architecture.

https://jules.google.com/session/9907850505057940493

Refactored the `UploadToShelfService` to use a coroutine-based approach for handling data uploads. This change separates network operations from Realm database transactions to prevent long-running transactions and improve performance.

- Replaced `executeTransactionAsync` with `kotlinx.coroutines.launch` and `withContext(Dispatchers.IO)`.
- Decoupled database reads, network calls, and database writes into distinct steps.
- Removed the class-level `mRealm` instance in favor of short-lived, transaction-specific instances.
- Updated `uploadUserData`, `uploadSingleUserData`, `uploadToShelf`, `uploadSingleUserToShelf`, `uploadHealth`, and `uploadSingleUserHealth` to follow the new architecture.
@qltysh
Copy link

qltysh bot commented Nov 5, 2025

❌ 6 blocking issues (6 total)

Tool Category Rule Count
qlty Structure Function with high complexity (count = 58): uploadUserData 2
qlty Structure Deeply nested control flow (level = 5) 2
qlty Duplication Found 26 lines of identical code in 2 locations (mass = 214) 2

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

val jsonElement = gson.toJsonTree(mutableObj)
val jsonObject = jsonElement.asJsonObject
val updateResponse = apiInterface.putDoc(header, "application/json", "${replacedUrl(model)}/_users/org.couchdb.user:${model.name}", jsonObject).execute()
if (updateResponse.isSuccessful) {
val updatedRev = updateResponse.body()?.get("rev")?.asString

P1 Badge Close Realm instance when updating health records

updateHealthData now calls dbService.realmInstance.executeTransaction { … } but never closes the Realm instance returned by realmInstance. Each call leaks a Realm.getDefaultInstance() and Android will eventually throw IllegalStateException: Realm instance has been leaked once the open instance limit is reached. Use dbService.realmInstance.use { it.executeTransaction { … } } or the provided withRealm helper so the instance is closed after the transaction.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 256 to 262
model.iterations = getString("iterations", fetchDataResponse.body())

if (saveKeyIv(apiInterface, model, obj)) {
updateHealthData(realm, model)
updateHealthData(model.id, model._id)
}
}
} catch (e: IOException) {

Choose a reason for hiding this comment

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

P1 Badge Persist updated user flags back to Realm

The refactor now works on unmanaged copies of RealmUserModel, but updateExistingUser only mutates the copy (model._rev/model.isUpdated) without writing those changes back to Realm. Because the caller obtained its models via realm.copyFromRealm, the database never clears the isUpdated flag nor stores the new revision, so the same users will be re-sent on every sync and the local _rev stays stale. Wrap these assignments in a Realm transaction so the managed record is updated.

Useful? React with 👍 / 👎.

Comment on lines 237 to 243
val rev = createResponse.body()?.get("rev")?.asString
model._id = id
model._rev = rev
processUserAfterCreation(apiInterface, realm, model, obj)
processUserAfterCreation(apiInterface, model, obj)
}
} catch (e: IOException) {
e.printStackTrace()

Choose a reason for hiding this comment

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

P1 Badge Save new user metadata after single-user sync

When uploadSingleUserData creates a user, uploadNewUser and processUserAfterCreation populate _id, _rev, password keys and health references on the detached RealmUserModel copy but never persist those changes. The previous version modified a managed model inside a Realm transaction, so the database was updated. After this commit, the local realm still lacks the assigned CouchDB id and security fields, which breaks follow-up shelf/health uploads for that user. The helper should write the new values to Realm before returning.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +103 to +117
dbService.realmInstance.use { realm ->
realm.executeTransaction {
val managedModel = it.where(RealmUserModel::class.java).equalTo("id", model.id).findFirst()
managedModel?._id = id
managedModel?._rev = rev
managedModel?.password_scheme = passwordScheme
managedModel?.derived_key = derivedKey
managedModel?.salt = salt
managedModel?.iterations = iterations
if (keyIvResult) {
managedModel?.key = model.key
managedModel?.iv = model.iv
}
updateHealthData(managedModel?.id, managedModel?._id)
}

Choose a reason for hiding this comment

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

P1 Badge Avoid starting a second Realm transaction inside an active one

Within uploadUserData the code updates the newly created user using realm.executeTransaction { … updateHealthData(managedModel?.id, managedModel?._id) } while updateHealthData itself now calls dbService.realmInstance.executeTransaction { … }. This nests a new write transaction (and opens a new Realm instance) while the original transaction is still running on the same thread. Realm forbids nested write transactions and will throw IllegalStateException: Cannot execute transaction while in a transaction, causing the sync to fail for every new user. The previous implementation reused the existing realm instance and avoided this error.

Useful? React with 👍 / 👎.

Comment on lines 90 to 205
val apiInterface = client?.create(ApiInterface::class.java)
mRealm = dbService.realmInstance

mRealm.executeTransactionAsync({ realm: Realm ->
val userModel = realm.where(RealmUserModel::class.java)
.equalTo("name", userName)
.findFirst()

if (userModel != null) {
try {
val password = SecurePrefs.getPassword(context, sharedPreferences) ?: ""
val header = "Basic ${Base64.encodeToString(("${userModel.name}:${password}").toByteArray(), Base64.NO_WRAP)}"
MainApplication.applicationScope.launch {
try {
val userModel = withContext(Dispatchers.IO) {
dbService.realmInstance.use { realm ->
val user = realm.where(RealmUserModel::class.java)
.equalTo("name", userName)
.findFirst()
user?.let { realm.copyFromRealm(it) }
}
}

val userExists = checkIfUserExists(apiInterface, header, userModel)
if (userModel != null) {
withContext(Dispatchers.IO) {
val password = SecurePrefs.getPassword(context, sharedPreferences) ?: ""
val header = "Basic ${Base64.encodeToString(("${userModel.name}:${password}").toByteArray(), Base64.NO_WRAP)}"
val userExists = checkIfUserExists(apiInterface, header, userModel)

if (!userExists) {
uploadNewUser(apiInterface, realm, userModel)
} else if (userModel.isUpdated) {
updateExistingUser(apiInterface, header, userModel)
if (!userExists) {
uploadNewUser(apiInterface, userModel)
} else if (userModel.isUpdated) {
updateExistingUser(apiInterface, header, userModel)
}
}
} catch (e: IOException) {
e.printStackTrace()
}

Choose a reason for hiding this comment

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

P1 Badge Persist single-user sync updates back to Realm

uploadSingleUserData copies the user from Realm (realm.copyFromRealm) and then calls uploadNewUser/updateExistingUser on that detached copy. Those helpers no longer write the updated _id, _rev, or isUpdated flags back into the database, so the local Realm is never updated after the network request. As a result, the same user will continue to appear as unsynced and shelf uploads will still see a null _id. The previous code operated on managed objects inside a transaction, so state was persisted. This path needs to write the modified user back to Realm after the network call.

Useful? React with 👍 / 👎.

Refactored the `UploadToShelfService` to use a coroutine-based approach for handling data uploads. This change separates network operations from Realm database transactions to prevent long-running transactions and improve performance.

- Replaced `executeTransactionAsync` with `kotlinx.coroutines.launch` and `withContext(Dispatchers.IO)`.
- Decoupled database reads, network calls, and database writes into distinct steps.
- Removed the class-level `mRealm` instance in favor of short-lived, transaction-specific instances.
- Updated `uploadUserData`, `uploadSingleUserData`, `uploadToShelf`, `uploadSingleUserToShelf`, `uploadHealth`, and `uploadSingleUserHealth` to follow the new architecture.
- Fixed a regression where `uploadSingleUserData` was not persisting changes to the database.
listener.onSuccess("Error during user data sync: ${e.localizedMessage}")
}
}
}
Copy link

Choose a reason for hiding this comment

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

Function with high complexity (count = 58): uploadUserData [qlty:function-complexity]

managedModel?.iterations = iterations
if (keyIvResult) {
managedModel?.key = model.key
managedModel?.iv = model.iv
Copy link

Choose a reason for hiding this comment

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

Deeply nested control flow (level = 5) [qlty:nested-control-flow]

managedModel?._rev = updatedRev
managedModel?.isUpdated = false
}
}
Copy link

Choose a reason for hiding this comment

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

Deeply nested control flow (level = 5) [qlty:nested-control-flow]

e.printStackTrace()
}
}
}
Copy link

Choose a reason for hiding this comment

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

Found 26 lines of identical code in 2 locations (mass = 214) [qlty:identical-code]

}) { error ->
mRealm.close()
listener?.onSuccess("Error uploading health data for user $userId: ${error.localizedMessage}")
}
Copy link

Choose a reason for hiding this comment

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

Function with high complexity (count = 18): uploadSingleUserHealth [qlty:function-complexity]

e.printStackTrace()
}
}
}
Copy link

Choose a reason for hiding this comment

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

Found 26 lines of identical code in 2 locations (mass = 214) [qlty:identical-code]

@dogi dogi added triage Further information is requested brainstorm experiment close? labels Nov 5, 2025
@dogi dogi closed this Nov 10, 2025
@dogi dogi reopened this Nov 10, 2025
@dogi dogi removed the close? label Nov 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

brainstorm experiment triage Further information is requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants