Skip to content

[MS-1030] Implement thread-safe access to Realm instance using Mutex#1229

Merged
meladRaouf merged 1 commit into
release/2025.2.0-draftfrom
bugfix/avoid-realm-concurrent-access
Jun 18, 2025
Merged

[MS-1030] Implement thread-safe access to Realm instance using Mutex#1229
meladRaouf merged 1 commit into
release/2025.2.0-draftfrom
bugfix/avoid-realm-concurrent-access

Conversation

@meladRaouf
Copy link
Copy Markdown
Collaborator

JIRA ticket
Will be released in: 2025.2.0

Root cause analysis (for bugfixes only)

First known affected version: long time ago

  • After updating from SID 2025.1.0 to 2025.2.0, I accessed the troubleshooting screen before Realm completed its automatic migration from schema version 16 to 17.

Notable changes

  • Since Realm was not yet initialized, each call tried to open the Realm with the same file but potentially conflicting schema states, leading to:
Fatal Exception: java.lang.IllegalStateException  
[RLM_ERR_MISMATCHED_CONFIG]: Realm at path ... already opened with different schema version

Added a Mutex to synchronize access to getRealm() and ensure that only one thread initializes the Realm instance. This prevents the crash and guarantees that the schema is applied only once.

Testing guidance

  • Verified that getRealm() no longer crashes on rapid cold starts.
  • Confirmed only one Realm instance is created during initialization.

Additional work checklist

  • Effect on other features and security has been considered
  • Design document marked as "In development" (if applicable)
  • External (Gitbook) and internal (Confluence) Documentation is up to date (or ticket created)
  • Test cases in Testiny are up to date (or ticket created)
  • Other teams notified about the changes (if applicable)

@meladRaouf meladRaouf requested review from a team, BurningAXE, TristramN, alex-vt, alexandr-simprints, luhmirin-s and ybourgery and removed request for a team June 17, 2025 08:17
@cla-bot cla-bot Bot added the ... label Jun 17, 2025
private val mutex = Mutex()

private fun getRealm(): Realm {
private suspend fun getRealm(): Realm = mutex.withLock {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this required for every realm call? Once it is initialised and migrated, this could slow down the reading significantly, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, can't we move this inside the if?

@BurningAXE BurningAXE requested a review from Copilot June 17, 2025 13:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Adds coroutine-based synchronization to ensure the Realm instance is only initialized once across concurrent calls.

  • Imports Mutex and withLock from kotlinx.coroutines.sync
  • Introduces a Mutex property and converts getRealm() into a suspend function using withLock
  • Removes the non-locked return path to guarantee thread-safe access
Comments suppressed due to low confidence (3)

infra/enrolment-records/realm-store/src/main/java/com/simprints/infra/enrolment/records/realm/store/RealmWrapperImpl.kt:39

  • [nitpick] Consider renaming mutex to something more descriptive like realmInitMutex to clarify its purpose.
    private val mutex = Mutex()

infra/enrolment-records/realm-store/src/main/java/com/simprints/infra/enrolment/records/realm/store/RealmWrapperImpl.kt:41

  • Add a KDoc comment explaining that this function is suspend and uses a Mutex to ensure the Realm instance is initialized only once and is safe for concurrent calls.
    private suspend fun getRealm(): Realm = mutex.withLock {

infra/enrolment-records/realm-store/src/main/java/com/simprints/infra/enrolment/records/realm/store/RealmWrapperImpl.kt:41

  • Consider adding a unit or integration test that launches multiple coroutines calling getRealm() simultaneously to verify only one Realm instance is created and no race conditions occur.
    private suspend fun getRealm(): Realm = mutex.withLock {

Comment on lines +41 to +48
private suspend fun getRealm(): Realm = mutex.withLock {
if (!this::realm.isInitialized) {
config = createAndSaveRealmConfig()
realm = createRealm()
}
return realm
realm
Copy link

Copilot AI Jun 17, 2025

Choose a reason for hiding this comment

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

To avoid locking on every call, you could do a fast-path check before acquiring the mutex, e.g., if (::realm.isInitialized) return realm outside the lock, then synchronize only the initialization block.

Copilot uses AI. Check for mistakes.
@meladRaouf meladRaouf force-pushed the bugfix/avoid-realm-concurrent-access branch 2 times, most recently from a520889 to 0e89770 Compare June 17, 2025 15:36
@meladRaouf meladRaouf force-pushed the bugfix/avoid-realm-concurrent-access branch from 0e89770 to 62beff3 Compare June 17, 2025 15:38
@sonarqubecloud
Copy link
Copy Markdown

@meladRaouf meladRaouf merged commit 4259d77 into release/2025.2.0-draft Jun 18, 2025
12 checks passed
@meladRaouf meladRaouf deleted the bugfix/avoid-realm-concurrent-access branch June 18, 2025 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants