realm / realm-kotlin

Kotlin Multiplatform and Android SDK for the Realm Mobile Database: Build Better Apps Faster.
Apache License 2.0
954 stars 58 forks source link

Issues when migrating from realm-java-sdk to realm-kotlin-sdk #1278

Closed moldovanpeter89 closed 1 year ago

moldovanpeter89 commented 1 year ago

Hello,

We have a situation that we are facing, we are trying to migrate our Android application from realm-java to realm-kotlin, but there are 2 issues that we can't find information about, so I created a new project to showcase the errors:

  1. The live application is very big so it is not possible to migrate every DAO in one shoot, we want to do it step by step this is a simplified version of our case: I have 2 RealmObjects one was migrated to use realm-kotlin, the other one uses still realm-java, when I try to compile the project this is the error:

Using the realm-kotlin for this

import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

class Inspection() : RealmObject {
    @PrimaryKey
    var id: String? = null
    var name: String? = null
    var addedWith: String? = null
}

Using the realm-java for this

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey

open class Checklist : RealmObject() {
    @PrimaryKey
    var id: String? = null
    var name: String? = null
    var addedWith: String? = null
}

The compilation error:

org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering
File being compiled: /Users/peter.moldovan/Workspace/AndroidWorkspace/RealmMigrationTest/app/src/main/java/com/example/mobile/kmm/formengine/realmmigrationtest/realmjava/Checklist.kt
The root cause java.lang.RuntimeException was thrown at: org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
    at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException(CodegenUtil.kt:239)
    at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException$default(CodegenUtil.kt:235)
    at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:68)
    at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:55)
    at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:41)
    at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96)
    at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:29)
    at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96)
    at org.jetbrains.kotlin.backend.common.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:43)
    at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.invokeCodegen(JvmIrCodegenFactory.kt:305)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.runCodegen(KotlinToJVMBytecodeCompiler.kt:356)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:134)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:58)
    at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:158)
    at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:53)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:99)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:47)
    at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
    at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:475)
    at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:125)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:373)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally$default(IncrementalCompilerRunner.kt:318)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.rebuild(IncrementalCompilerRunner.kt:114)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:207)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:79)
    at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:625)
    at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:101)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1746)
    at jdk.internal.reflect.GeneratedMethodAccessor107.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
    at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
    at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: Exception while generating code for:
FUN name:io_realm_kotlin_schema visibility:public modality:OPEN <> ($this:com.example.mobile.kmm.formengine.realmmigrationtest.realmjava.Checklist.Companion) returnType:kotlin.Any
  $this: VALUE_PARAMETER name:<this> type:com.example.mobile.kmm.formengine.realmmigrationtest.realmjava.Checklist.Companion

    at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
    at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate$default(FunctionCodegen.kt:41)
    at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethodNode(ClassCodegen.kt:392)
    at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethod(ClassCodegen.kt:409)
    at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:152)
    at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:165)
    at org.jetbrains.kotlin.backend.jvm.FileCodegen.lower(JvmPhases.kt:44)
    at org.jetbrains.kotlin.backend.common.phaser.FileLoweringPhaseAdapter.invoke(PhaseBuilders.kt:120)
    at org.jetbrains.kotlin.backend.common.phaser.FileLoweringPhaseAdapter.invoke(PhaseBuilders.kt:116)
    at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96)
    at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:65)
    ... 41 more
Caused by: java.lang.IllegalStateException: Function has no body: FUN name:io_realm_kotlin_schema visibility:public modality:OPEN <> ($this:com.example.mobile.kmm.formengine.realmmigrationtest.realmjava.Checklist.Companion) returnType:kotlin.Any
    at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.generate(ExpressionCodegen.kt:238)
    at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.doGenerate(FunctionCodegen.kt:118)
    at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:45)
    ... 51 more
  1. Even if I by-pass the first error still I can't open the same realm database with both sdks simultaneously (trying to simulate the case that we could/have in the live app with both realms opened) :
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.mobile.kmm.formengine.realmmigrationtest, PID: 29031
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.mobile.kmm.formengine.realmmigrationtest/com.example.mobile.kmm.formengine.realmmigrationtest.MainActivity}: java.lang.IllegalArgumentException: Could not open Realm with the given configuration: path=/data/user/0/com.example.mobile.kmm.formengine.realmmigrationtest/files/realm-java.db
     name=realm-java.db
     maxNumberOfActiveVersions=9223372036854775807
     schemaVersion=1
     schemaMode=RLM_SCHEMA_MODE_AUTOMATIC
     schema=[class com.example.mobile.kmm.formengine.realmmigrationtest.realmjava.Inspection]: RealmCoreException([2]: Realm file is currently open in another process which cannot share access with this process. All processes sharing a single file must be the same architecture.)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3685)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3842)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2252)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
     Caused by: java.lang.IllegalArgumentException: Could not open Realm with the given configuration: path=/data/user/0/com.example.mobile.kmm.formengine.realmmigrationtest/files/realm-java.db
     name=realm-java.db
     maxNumberOfActiveVersions=9223372036854775807
     schemaVersion=1
     schemaMode=RLM_SCHEMA_MODE_AUTOMATIC
     schema=[class com.example.mobile.kmm.formengine.realmmigrationtest.realmjava.Inspection]: RealmCoreException([2]: Realm file is currently open in another process which cannot share access with this process. All processes sharing a single file must be the same architecture.)
        at io.realm.kotlin.internal.CoreExceptionConverter.genericRealmCoreExceptionHandler(RealmInteropBridge.kt:165)
        at io.realm.kotlin.internal.CoreExceptionConverter.convertToPublicException(RealmInteropBridge.kt:137)
        at io.realm.kotlin.internal.CoreExceptionConverter.convertToPublicException$default(RealmInteropBridge.kt:127)
        at io.realm.kotlin.internal.RealmImpl$Companion.create$io_realm_kotlin_library(RealmImpl.kt:298)
        at io.realm.kotlin.Realm$Companion.open(Realm.kt:82)
        at com.example.mobile.kmm.formengine.realmmigrationtest.MainActivity.openRealmKotlin(MainActivity.kt:49)
        at com.example.mobile.kmm.formengine.realmmigrationtest.MainActivity.onCreate(MainActivity.kt:16)
        at android.app.Activity.performCreate(Activity.java:8054)
        at android.app.Activity.performCreate(Activity.java:8034)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1341)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3666)
            ... 12 more
     Caused by: io.realm.kotlin.internal.interop.RealmCoreOtherException: [2]: Realm file is currently open in another process which cannot share access with this process. All processes sharing a single file must be the same architecture.
        at io.realm.kotlin.internal.interop.CoreErrorUtils.coreErrorAsThrowable(CoreErrorUtils.kt:28)
        at io.realm.kotlin.internal.interop.realmcJNI.open_realm_with_scheduler(Native Method)
        at io.realm.kotlin.internal.interop.realmc.open_realm_with_scheduler(realmc.java:1703)
        at io.realm.kotlin.internal.interop.RealmInterop.realm_open(RealmInterop.kt:193)
E/AndroidRuntime:     at io.realm.kotlin.internal.interop.RealmInterop.realm_open$default(RealmInterop.kt:181)
        at io.realm.kotlin.internal.ConfigurationImpl.openRealm$suspendImpl(ConfigurationImpl.kt:105)
        at io.realm.kotlin.internal.ConfigurationImpl.openRealm(Unknown Source:0)
        at io.realm.kotlin.internal.RealmImpl$1.invokeSuspend(RealmImpl.kt:121)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:284)
        at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
        at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
        at io.realm.kotlin.internal.platform.CoroutineUtilsSharedJvmKt.runBlocking(CoroutineUtilsSharedJvm.kt:22)
        at io.realm.kotlin.internal.platform.CoroutineUtilsSharedJvmKt.runBlocking$default(CoroutineUtilsSharedJvm.kt:21)
        at io.realm.kotlin.internal.RealmImpl.<init>(RealmImpl.kt:118)
        at io.realm.kotlin.internal.RealmImpl.<init>(Unknown Source:0)
        at io.realm.kotlin.internal.RealmImpl$Companion.create$io_realm_kotlin_library(RealmImpl.kt:296)
            ... 19 more
  1. If I did migrate everything from realm-java to realm-kotlin in my test app then there was no problem opening the realm db with realm-kotlin and query the data which was created with realm-java.

I read the documentation and the article about the migration but somehow it lacks a real-life use case or it's not that explicit for everyone and what strategy could be applied:

We are trying to migrate to KMM to extract the data layer from the apps (android/iOS), currently only testing the waters in the android side but I assume this will be the case for iOS with realm-swift..

The realm version that were used:

cmelchior commented 1 year ago

Hi @moldovanpeter89

I can reproduce your issue using our Java compatibility example: https://github.com/realm/realm-kotlin/tree/main/examples/realm-java-compatibility

It looks like it is an issue with us not handling RealmObject() correctly. I will be looking for a fix.

A workaround you can use until a fix is ready is to change open class Checklist : RealmObject() to open class Checklist : RealmModel.

Note, that while we do support using both SDK's in the same app. Having both of them operating on the same file is not recommended.

The reason for this is that we are using some internal files to control access to the underlying file and if two SDK's do not agree on the exact layout for these files, it can potentially corrupt the file. And in your case Realm Kotlin 1.6.0 uses Realm Core 13.2.0, while Realm Java 10.11.1 uses Realm Core 12.3.0.

moldovanpeter89 commented 1 year ago

@cmelchior Thank you for the clarifications!

cmelchior commented 1 year ago

A fix has been merged and is available in a 1.6.2-SNAPSHOT: https://github.com/realm/realm-kotlin#using-snapshots