Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS DatabaseException(Error Domain=FMDatabase Code10 "disk i/o error" #71

Closed
nicolasjon opened this issue Jun 26, 2018 · 11 comments
Closed

Comments

@nicolasjon
Copy link

Hi,

First thanks for your great work on this essential library! I have everything working fine on Android.

For iOS, the app loads from Android Studio and runs OK, once the app has been closed and device has been locked/unlocked a couple of times restarting the app no data is displayed and this error is displayed -

iOS DatabaseException(Error Domain=FMDatabase Code10 "disk i/o error"

If the app is then loaded for Android Studio the following console error is shown

DB Error: 10 "disk I/O error"
DB Query: SELECT *, ((51.811834 - Latitude ) * (51.811834 - Latitude ) + (-0.219816 - Longitude) * (-0.219816 - Longitude) * 0.38222820246023875) AS DISTANCE FROM sota where DISTANCE < 7 order by Distance Limit 200
DB Path: /var/mobile/Containers/Data/Application/19B13FD4-267B-47B6-B13B-FC427E34F056/Documents/asset_sota.db

Further observations

  1. Closing the app, locking then unlocking the device consistently causes this error.
  2. Restart the device and restart the app and it works fine, until the app is closed and device locked/unlocked and app restarted then it fails.
  3. A thought - File protection has perhaps been implicated Disk I/O error when device is locked ccgus/fmdb#262

Thanks!
Nicolas

@nicolasjon nicolasjon changed the title iOS DatabaseException(Error Domain=FMDatabase Code10 "disk I/o error" iOS DatabaseException(Error Domain=FMDatabase Code10 "disk i/o error" Jun 26, 2018
@alextekartik
Copy link
Contributor

Thansk for your report. I'll try to reproduce it first, then maybe try blindly SQLITE_OPEN_FILEPROTECTION_NONE if I cannot reproduce easily (and I'm not an iOS expert so that is always where I'm not sure what I'm doing...). Some information that might help me:

  • When you mean closing the app, you mean forcing it to quit or just switching app (to know if the app remain open in the background
  • Are you using the latests sqflite 0.11.0 which by default open the database as a single (that solves maybe unrelated race condition where the database was opened twice)

@nicolasjon
Copy link
Author

  1. Closing the app by double click on home button and then swipe. Otherwise as you say it is still likely to be in background and will open the backgrounded app.
  2. Yes, using 0.11.0
  3. Closing the app and lock/unlock twice causes the issue every time.
  4. Running iPhone 5 (32bit) iOS 10.3.3

@alextekartik
Copy link
Contributor

I could not manage to reproduce the issue (on iPod 5 32 bits and iPod 6 64 bits) with the app I have (launching the app / writing some data / quitting the app / locking twice / restart the app) so hard to find a fix. The issue is definitely happening in other solution (cordova) so I guess it is there. Could you try in your startup iOS code (maybe in AppDelegate.m as a quick test) setting the file protection to none on your existing database (and if it fails as well as the containing folder, and then maybe the wal file too???) to see if that solves your issue? If yes, then we could consider adding file protection none option when opening the database (although i do want to avoid platform specific API as much as possible and don't want to make protection none the default):

NSFileManager *fileManager  = [NSFileManager defaultManager];

// Database file - 1st try
[fileManager setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey] ofItemAtPath:dbname error:nil];

// Containing folder - 2nd try if 1st try fails to solve the issue
[fileManager setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey] ofItemAtPath:dbdir error:nil];

@nicolasjon
Copy link
Author

Thanks for the feedback. I will try this and report back.

@nicolasjon
Copy link
Author

nicolasjon commented Jun 30, 2018

I have investigated further and found a very interesting effect

If we do not use routes...

home: new MyHomePage(title: 'My App'),
/*
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(title: "My App"),
'/settings': (context) => SettingsScreen(),
'/test': (context) => SettingsScreen(),
},
*/

Then in the console log from the list of the app we see

flutter: Builder Called
flutter: Query called
flutter: /var/mobile/Containers/Data/Application/E1F39CE1-B24D-44AF-B317-10C325AC5AB3/Documents/sota.db copying
flutter: 200
flutter: Builder Called
flutter: Builder Has Data

Fine! Builder is only called twice and critically Query is only called once.

NOW.... If we use routes.

// home: new MyHomePage(title: 'My App'),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(title: "My App"),
'/settings': (context) => SettingsScreen(),
'/test': (context) => SettingsScreen(),
},

Then.. Builder and query are called multiple times (which could be expensive) and the i/o errors occur...

flutter: Builder Called
flutter: Query called
flutter: Builder Called
flutter: Query called
flutter: /var/mobile/Containers/Data/Application/05074923-29D9-4C8D-9989-98C6F81FB4A3/Documents/sota.db copying
flutter: /var/mobile/Containers/Data/Application/05074923-29D9-4C8D-9989-98C6F81FB4A3/Documents/sota.db copying
flutter: Builder Called
flutter: Query called
flutter: /var/mobile/Containers/Data/Application/05074923-29D9-4C8D-9989-98C6F81FB4A3/Documents/sota.db copying
flutter: 200
flutter: 200
flutter: Builder Called
flutter: Builder Has Data
flutter: 200

Summary

  1. If defined routes (as shown) are used - Wrapping the database create section in an if file exists test prevents the I/o errors, but does not allow it to be overwritten once created(!) Build and query called multiple times unnecessarily.

  2. If defined routes (as shown) are not used but they are created in code, for example
    onTap: () {
    Navigator.push(
    context,
    MaterialPageRoute(
    builder: (context) => MyDetailScreen(
    mydata: snapshot.data[index])));
    },
    Then no database checks are needed, and the database can be overwritten at will. Build and query are only called as needed (once for query). No I/o errors.

Nicolas

@alextekartik
Copy link
Contributor

Ah it seems you have extra steps before opening the database (copying?)...You should make your whole copy/open database in a critical section (using synchronized for example)

@nicolasjon
Copy link
Author

Thanks for your feedback, I am using the async example code to do the database copy from the flutter asset to the app doc folder.
Key thing is how defining named routes causes the pages builder to be called multiple times. If the routes are created when used, the page builder is called once to launch the query, then again when there is data. I suspect this behaviour/feature is not down to SQFlite, however if there is an issue with the db copy create code as well, then the I/o error will be caused.

Just puzzled that named routes seem to cause multiple calls to the builder....

@nicolasjon
Copy link
Author

Still puzzled that named routes seem to cause multiple calls to the builder....
Have not encounter the I/o error issue again. Suggest this issue is closed.

@alextekartik
Copy link
Contributor

Ok. Most recent versions of sqflite open a single instance of a database for a given path, maybe this has helped.

@zakiso
Copy link

zakiso commented Apr 1, 2019

@nicolasjon same error. Are you solved it?

@nicolasjon
Copy link
Author

nicolasjon commented Apr 1, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants