Fixed
Status Update
Comments
yb...@google.com <yb...@google.com>
ya...@google.com <ya...@google.com> #2
I ran tests in the #1 zip file, but they all seem to pass on N5X running API 26.
I wrote a simple test like below, but it also seems to pass.
@Test
public void close() {
assertFalse(mDatabase.isOpen());
mDatabase.getUserDao().insert(TestUtil.createUser(3));
assertTrue(mDatabase.isOpen());
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
assertTrue(db.isOpen());
mDatabase.close();
assertFalse(mDatabase.isOpen());
assertFalse(db.isOpen());
}
Am I missing something?
I wrote a simple test like below, but it also seems to pass.
@Test
public void close() {
assertFalse(mDatabase.isOpen());
mDatabase.getUserDao().insert(TestUtil.createUser(3));
assertTrue(mDatabase.isOpen());
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
assertTrue(db.isOpen());
mDatabase.close();
assertFalse(mDatabase.isOpen());
assertFalse(db.isOpen());
}
Am I missing something?
mm...@commonsware.com <mm...@commonsware.com> #3
If I run gradle connectedCheck from the command line, the tests in the sample project run fine. If I import the project into Android Studio 2.3.3, and run the tests, I get the aforementioned crash pretty reliably. versionedThingy() runs first clean, then customer() crashes.
I don't know what triggers InvalidationTracker to run and therefore whether your @Test from #2 will trigger it.
I don't know what triggers InvalidationTracker to run and therefore whether your @Test from #2 will trigger it.
ya...@google.com <ya...@google.com> #4
OK, I can see the issue with the sample now, but only very occasionally. I needed to rerun the tests repeatedly.
There's a similar report on the sqlcipher page. Could you try upgrading sqlcipher to 3.5.7 and see if the issue persists?
https://github.com/sqlcipher/android-database-sqlcipher/issues/176
I am still trying to reproduce this in Room code base.
There's a similar report on the sqlcipher page. Could you try upgrading sqlcipher to 3.5.7 and see if the issue persists?
I am still trying to reproduce this in Room code base.
mm...@commonsware.com <mm...@commonsware.com> #5
OK, on the one hand, bumping SQLCipher for Android to 3.5.7 allowed the first run of the tests from Android Studio 2.3.3 to go cleanly. However, repeatedly running the tests would result in other crashes; running the tests after those initial crashes would bring up the same SQLiteMisuseException as originally reported.
On one batch of tests, after 2-3 sequential clean runs, I got:
net.sqlcipher.database.SQLiteException: no such table: room_table_modification_log: , while compiling: SELECT * FROM room_table_modification_log WHERE version > ? ORDER BY version ASC;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:89)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
(and, after that, the SQLiteMisuseException on every run until I uninstalled the app)
On another batch of tests, after 2-3 sequential clean runs, I got:
java.lang.IllegalStateException: database :memory: already closed
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:58)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:89)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
followed immediately by:
net.sqlcipher.database.SQLiteException: not an error: handle 0x75a2383148
at net.sqlcipher.database.SQLiteProgram.native_bind_long(Native Method)
at net.sqlcipher.database.SQLiteProgram.bindLong(SQLiteProgram.java:215)
at net.sqlcipher.database.SQLiteQuery.bindLong(SQLiteQuery.java:176)
at com.commonsware.cwac.saferoom.Program.bindLong(Program.java:34)
at android.arch.persistence.db.SimpleSQLiteQuery.bind(SimpleSQLiteQuery.java:85)
at android.arch.persistence.db.SimpleSQLiteQuery.bind(SimpleSQLiteQuery.java:70)
at android.arch.persistence.db.SimpleSQLiteQuery.bindTo(SimpleSQLiteQuery.java:54)
at com.commonsware.cwac.saferoom.Database$1.newCursor(Database.java:183)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:74)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
(and, after that, the SQLiteMisuseException on every run until I uninstalled the app)
In all cases, the crashes are being triggered by InvalidationTracker, and occur when I close() the SQLiteDatabase in my SupportSQLiteOpenHelper implementation.
On one batch of tests, after 2-3 sequential clean runs, I got:
net.sqlcipher.database.SQLiteException: no such table: room_table_modification_log: , while compiling: SELECT * FROM room_table_modification_log WHERE version > ? ORDER BY version ASC;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:89)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
(and, after that, the SQLiteMisuseException on every run until I uninstalled the app)
On another batch of tests, after 2-3 sequential clean runs, I got:
java.lang.IllegalStateException: database :memory: already closed
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:58)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:89)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
followed immediately by:
net.sqlcipher.database.SQLiteException: not an error: handle 0x75a2383148
at net.sqlcipher.database.SQLiteProgram.native_bind_long(Native Method)
at net.sqlcipher.database.SQLiteProgram.bindLong(SQLiteProgram.java:215)
at net.sqlcipher.database.SQLiteQuery.bindLong(SQLiteQuery.java:176)
at com.commonsware.cwac.saferoom.Program.bindLong(Program.java:34)
at android.arch.persistence.db.SimpleSQLiteQuery.bind(SimpleSQLiteQuery.java:85)
at android.arch.persistence.db.SimpleSQLiteQuery.bind(SimpleSQLiteQuery.java:70)
at android.arch.persistence.db.SimpleSQLiteQuery.bindTo(SimpleSQLiteQuery.java:54)
at com.commonsware.cwac.saferoom.Database$1.newCursor(Database.java:183)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:74)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1867)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
(and, after that, the SQLiteMisuseException on every run until I uninstalled the app)
In all cases, the crashes are being triggered by InvalidationTracker, and occur when I close() the SQLiteDatabase in my SupportSQLiteOpenHelper implementation.
ya...@google.com <ya...@google.com> #6
I think I am starting to understand the issue.
In InvalidationTracker, we catch exceptions from table monitoring query when database is closing.
We catch android.database.sqlite.SQLiteException, but other implementation such as sqlcipher might throw other exception types, such as net.sqlcipher.database.SQLiteException. That's why we can't see this in Room with framework sqlite.
We have to come up with a better way to handle db closing in InvalidationTracker.
In InvalidationTracker, we catch exceptions from table monitoring query when database is closing.
We catch android.database.sqlite.SQLiteException, but other implementation such as sqlcipher might throw other exception types, such as net.sqlcipher.database.SQLiteException. That's why we can't see this in Room with framework sqlite.
We have to come up with a better way to handle db closing in InvalidationTracker.
Description
Version used: 1.0.0-alpha3
Devices/Android versions reproduced on: Nexus 5X (ODP3)
The implementation of FrameworkSQLiteOpenHelper does not close the actual SQLite database. The static OpenHelper class' close() method is:
@Override
public synchronized void close() {
super.close();
mWrappedDb = null;
}
(based on the code distributed along with the 1.0.0-alpha3 artifact)
AFAICT, there should be an mWrappedDb.close() in there, as I do not see anything else that will ever close that FrameworkSQLiteDatabase.
I am back to working on SafeRoom, my SQLCipher for Android implementation of the Support* classes. If I add close() to my SupportSQLiteHelper.Factory as shown above, I then run into a related crash:
E/AndroidRuntime: FATAL EXCEPTION: pool-1-thread-1
Process: com.commonsware.cwac.saferoom.test, PID: 9294
net.sqlcipher.database.SQLiteMisuseException: library routine called out of sequence: , while compiling: SELECT * FROM room_table_modification_log WHERE version > ? ORDER BY version ASC;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:84)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:49)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:42)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1820)
at com.commonsware.cwac.saferoom.Database.query(Database.java:176)
at com.commonsware.cwac.saferoom.Database.query(Database.java:145)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:156)
at android.arch.persistence.room.InvalidationTracker$2.run(InvalidationTracker.java:354)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
This is stemming from InvalidationTracker, and it is occurring sometime after I call close() on the RoomDatabase. If I either do not close() the RoomDatabase or I do not close() the actual database, this crash does not appear. My best guess is that close() on RoomDatabase is not shutting down related InvalidationTrackers, so those objects continue tracking, then get cranky when their database is closed.
Attached is a sample project. Running the instrumentation tests should reproduce the problem.