Objectbox-java: Objectbox failed to initialize

Created on 26 Nov 2018  路  8Comments  路  Source: objectbox/objectbox-java

We found out that the box is failing to initialize itself and raises a "Cannot change the following flags for Property" exception.(We changed a field from non-nullable to nullable) Once we get that exception , we are trying to kill the old db and re-initialize it. Since we do not have a handle on the database we simply call the static Objectbox.DeleteAllFiles and thats where we get into sort of a deadlock(Not sure how, but we see the finalizerDemon(GC??) kicking in and trying to finalize and close the objectbox since we had a deadlock(something lingering out without a good cleanup??) and that fails with a timeout(10 seconds)... we are not closing the database anywhere in our code.

So , questions here :

is there a way to tell objectbox to recreate the schema when its changed? (We went from non-nullable to a nullable and the DB is not even able to be created. can we simply tell objectbox to not throw an exception and simply blow away current data and recreate the new schema?)

If that is not possible, how can we safely delete old files and not get into any sort of deadlocks?
Here is the code that gets deadlocked :

private fun getBoxStore(applicationContext: Context, dbDirectory: File): BoxStore {
    return try {
        buildBoxStore(applicationContext, dbDirectory)
    } catch (ex: io.objectbox.exception.DbException) {
        println("Database failure...probably some bad field? ${ex.localizedMessage}")
        BoxStore.deleteAllFiles(dbDirectory)

        // Yay! we deleted what we think is the old bad database.
        // Let's try one more time.
        buildBoxStore(applicationContext, dbDirectory)
    }
}
private fun buildBoxStore(context: Context, dbDirectory: File): BoxStore {
    val boxStore = MyObjectBox.builder()
            .androidContext(context)
            .directory(dbDirectory)
            .build()

    return boxStore
}

This code runs in the onCreate of our Application object. Our code has been pushed to production unfortunately with this issue. (So we are trying to safely recover from this)

Note : When we run the app, we get a whitescreen that times out after 10 seconds and then we see the following crash :

Fatal Exception: java.util.concurrent.TimeoutException
io.objectbox.BoxStore.finalize() timed out after 10 seconds

o.objectbox.BoxStore.close (SourceFile:415)
io.objectbox.BoxStore.finalize (SourceFile:286)
java.lang.Daemons$FinalizerDaemon.doFinalize (Daemons.java:250)
java.lang.Daemons$FinalizerDaemon.runInternal (Daemons.java:237)
java.lang.Daemons$Daemon.run (Daemons.java:103)
java.lang.Thread.run (Thread.java:764)

Most helpful comment

Same issue too. Any easy way to deal with this? Just聽like clean old files and create new database automatically.

All 8 comments

There is no need to nuke all data. Instead read up on how to change property types.
-ut

If we miss creating this UID and release to the android store, our box will not be able to initialize itself(our current issue)... We need like a backup plan(deleting the whole db) in case objectbox cannot initialize itself for whatever reason.

Also , in our current case, we have two sets of users out there at this point :

1 People who have the schema with the non-nullable field . (They never upgraded their app)

2 People who have the schema with a nullable field. (They re-installed the app)

If we add a UID at this point and release the app, I think #2 people will crash when they get our new app since they have a nullable field already...

Thats why we cannot do the UID approach at this point.

You can also rename the property (property to newProperty). This will delete the old and create a new property. This should work for either of your users (assuming for both the property is named the same). But verify that the retired UID for the property matches in your default.json file.

Anyhow, you can still use BoxStore.deleteAllFiles(dbDirectory). Just make sure the database is closed. And I suggest building/deleting the database in a different thread and not on the application main thread to avoid the timeout exception.
-ut

Encountered almost exactly the same issue, with a similar goal.

Anyhow, you can still use BoxStore.deleteAllFiles(dbDirectory). Just make sure the database is closed.

How can we achieve this?
Since creating the boxstore has thrown an exception and its impossible to check if its closed. And as stated calling deleteAllFiles causes a deadlock and a eventual crash.

@grabskimike Hm. Possibly let the app crash and set a flag, then on the next start check for the flag. Then delete all database files before building BoxStore.

Or don't do that at all and fix the crash with an app update.
-ut

Closing this issue due to inactivity. :zzz: Please re-open with more details or submit a new issue.

Same issue too. Any easy way to deal with this? Just聽like clean old files and create new database automatically.

I had the same problem. I also noticed if I called build a second time during the same run, it would hang up. For example, if you comment out the BoxStore.deleteAllFiles and attempt to build the BoxStore again in your catch block, it will hang up for 10 seconds and then crash.

If I called BoxStore.deleteAllFiles before doing anything else it would execute fine and delete all files. Only problem was, if I call BoxStore.deleteAllFiles after attempting to build the box store then I get a crash.

For a solution I ended up storing a version number in Shared Preferences. Whenever an exception is thrown building the BoxStore, I increment the version number and build a new BoxStore with the new version number. It just means old files will be left around, but shouldn't be a problem.

Note in this example it may be better to check what the exception is before building a new BoxStore.

Example:

`
init
{
App.context?.let { context: Context ->

        val keyVersionNumber: String = "cache_version"
        val preferences: SharedPreferences = context.getSharedPreferences("data_manager_preferences", Context.MODE_PRIVATE)

        var version: Int = preferences.getInt(keyVersionNumber, 0)
        var databaseFilename: String = "database_" + version.toString()

        try
        {
            this.cache = MyObjectBox.builder().androidContext(context).name(databaseFilename).build()
        }
        catch (e: Exception)
        {
            version = version + 1
            databaseFilename = "database_" + version.toString()

            val editor: SharedPreferences.Editor = preferences.edit()
            editor.putInt(keyVersionNumber, version)
            editor.commit()

            this.cache = MyObjectBox.builder().androidContext(context).name(databaseFilename).build()
        }
    }
}

`

Was this page helpful?
0 / 5 - 0 ratings

Related issues

greenrobot picture greenrobot  路  43Comments

greenrobot picture greenrobot  路  47Comments

livotov picture livotov  路  17Comments

Gaket picture Gaket  路  79Comments

greenrobot picture greenrobot  路  27Comments