Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upgrading Room DB with migrations causes DB lock

I am going to answer my own question, this is the problem description.

So I have an app that's published on the Store, and with the new version that I wanted to release, the DB schema had changed so I naturally had to have migrations in place that took care of upgrading it from version 3 to the most recent version 5. This includes providing migrations from 3 to 4 and from 4 to 5. Or from 3 to 5 in one go.

So that's what I did, I provided those migrations and fed them to the Room databaseBuilder() and everything was in place to simulate the scenario where an upgrade of the app is happening (install Store version, run it, log in, DB created, produce production APK and install it on device through terminal, run)

Doing this was always producing the following exception:

05-19 02:38:00.363 6472-6522/co.myapp.app E/ROOM: Invalidation tracker is initialized twice :/.
05-19 02:38:00.378 6472-6549/co.myapp.app E/ROOM: Cannot run invalidation tracker. Is the db closed?
java.lang.IllegalStateException: The database '/data/user/0/co.myapp.app/databases/my_db' is not open.
at android.database.sqlite.SQLiteDatabase.throwIfNotOpenLocked(SQLiteDatabase.java:2765)
at android.database.sqlite.SQLiteDatabase.createSession(SQLiteDatabase.java:490)
at android.database.sqlite.SQLiteDatabase$1.initialValue(SQLiteDatabase.java:88)
at android.database.sqlite.SQLiteDatabase$1.initialValue(SQLiteDatabase.java:87)
at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:160)
at java.lang.ThreadLocal.get(ThreadLocal.java:150)
at android.database.sqlite.SQLiteDatabase.getThreadSession(SQLiteDatabase.java:484)
at android.database.sqlite.SQLiteProgram.getSession(SQLiteProgram.java:107)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
at android.arch.persistence.db.framework.FrameworkSQLiteStatement.executeUpdateDelete(FrameworkSQLiteStatement.java:45)
at android.arch.persistence.room.InvalidationTracker$1.run(InvalidationTracker.java:321)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:762)

The migrations were running successfully and the tables were being created as I had written, but that exception kept appearing on every single DB access operation right after Room upgrades your DB version and migrates. Every read or write after the upgrade happens will cause this exception

  • First I thought that it was a problem with the migrations but the migrations were correct (the SQL was checked and Room didn't complain about anything of the sort, usually it does so very well and tells you what didn't match).
  • Then I thought the problem was with feeding it 2 migrations at once (which I'd done before, and there was no problem) but I removed the 4 to 5 migration and placed it in the 3 to 5 one to run the whole thing in one go. No use.
  • Then I thought well, I could always give up and say ok, just do fallbackToDestructiveMigration() and let Room recreate everything, causing all users to get logged out and have their data lost, something I didn't want to do but I just wanted to see where this will lead. So I didn't feed the migrations and just ignored them. The exception still happened.

This exception was happening silently, it wasn't crashing the app, nor was it referring to anywhere in my code, it just happened in Logcat and prevented any sort of DB access the moment it occurred even one time. Regardless if you've fed migration code or not, or whether you want to fall back to destructive migration or not, the moment Room detects that you've upgraded your DB it locks it down and just prevents any access to it whatsoever.

I've tested this on Samsung S6, Nexus 5, OnePlus, Motorola Moto C and Google Pixel, exact same issue everywhere.

like image 739
Odaym Avatar asked May 19 '18 00:05

Odaym


1 Answers

I was browsing this page on adding Architecture Components to your app and I came across this beautiful line:

def room_version = "1.1.0" // or, for latest rc, use "1.1.1-rc1"

I was using Room version 1.1.0, naturally, because it should be stable and we want stable, solid software that we can rely on from Google, of course.

Bumped up the version to 1.1.1-rc1 and I can safely say that this nonsensical error that stems from Google's own code is now gone, I've retested the same scenario on all the devices I mentioned in the original question.

Be careful when exceptions occur and no mention of your code is included. This is a bug in Room 1.1.0, please confirm if you have seen it, the only other mention of it online that I've seen is this one here from 2017

like image 115
Odaym Avatar answered Oct 22 '22 17:10

Odaym