CLB iOS doesn't seem to work when app activated from SUSPENDED state

81 views
Skip to first unread message

Nick Pomfret

unread,
Nov 7, 2016, 5:04:39 AM11/7/16
to Couchbase Mobile
I understand that iOS will suspend and may eventually kill apps that are suspended.  After which it will re-start them should something like a push notification arrive.  I've noticed that couchbase lite does not always start happily under these conditions and the only response I can get back from the rest API is a `database error` with a 500 status code - with no error message at all.  It's very difficult to reproduce because I can't get Xcode to artificially suspend and purge my app.

Has anyone else noticed this?  Is there a work around?

The lack of error message doesn't help.  I can't even tell if its the database engine failing or the listener, or anything else.

I create the db manager using NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication like this:

 CBLManagerOptions options = {NO, NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication};

        NSError* error;

        manager = [[CBLManager alloc] initWithDirectory: [CBLManager defaultDirectory]

                                                        options: &options error: &error];



Jens Alfke

unread,
Nov 10, 2016, 6:36:12 PM11/10/16
to mobile-c...@googlegroups.com

On Nov 7, 2016, at 2:04 AM, Nick Pomfret <npom...@gmail.com> wrote:

I understand that iOS will suspend and may eventually kill apps that are suspended.  After which it will re-start them should something like a push notification arrive.  I've noticed that couchbase lite does not always start happily under these conditions and the only response I can get back from the rest API is a `database error` with a 500 status code - with no error message at all.

This sounds like a known problem involving filesystem encryption — by default when an app is suspended it loses access to its files; actually it can continue to use open files, but can’t open files. Then when it reactivates it gets access back. (Apple’s great iOS Security White Paper explains this in great detail.)

In the last several months we’ve gotten a bunch of bug reports that indicate that the core database calls (SQLite or ForestDB) are failing because they can’t open files. I suspect something has changed in iOS 10, having to do with when exactly the app regains file access. (It seems weird that the OS would explicitly wake up the app, but do so before it restores access to files; but we haven’t diagnosed this fully yet.) This has been on Pasin’s and my plates to track down, but we’ve been swamped with other stuff lately including 2.0 design work and preparing for the Couchbase Connect conference…

The workaround is to create the CBLManager with custom CBLManagerOptions that include a more lenient file-protection mode. IIRC the mode to use is NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication, but I have to admit I get these modes mixed up. The Apple developer docs explain them in more detail. (If you’re using PhoneGap/Cordova, I think you have to modify and rebuild the plugin to do this.)

—Jens

Nick Pomfret

unread,
Nov 12, 2016, 4:41:42 AM11/12/16
to mobile-c...@googlegroups.com
Thanks for the reply Jens.

I switched to using NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication some time ago in my CBLManagerOptions, but am still getting the error. However...

I noticed that I stopped getting the problem on my personal devices.  And the thing I do that my users don't is repeatedly reinstall the app from scratch (and therefor delete the database).  So I've asked my users (the app isn't live yet so there aren't many) to one by one delete the app and re-install.  And one by one the errors seem to be reducing.  I say 'seem to' because I don't yet have accurate figures.  So now I think I've just got one use left who's still got the problem who hasn't deleted and re-installed the app.  And I won't get her to do the same just yet - so I can reproduce the problem, sort of.

My theory was that databases created before I made the change suffered from the problem.  And when they delete the app they delete the database and the new database gets created with the new code that uses NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication.  However, I don't think that's correct now because I also have code that access the filesystem on startup, and that code never suffers from any file protection issues at all. The files weren't created with any sort of special file protection stuff and uses a normal [NSFileManager defaultManager] without any special options set.

My other theory then is that something in CBL got fixed along the way and databases created with the fixed version don't suffer from this problem?  is this possible?  I've been using the nightly builds so may have picked up a fix without realising it.

What would be really useful is a better error message.  All I get at the moment is "database error" / 500 - could something more verbose be added?





 



--
You received this message because you are subscribed to a topic in the Google Groups "Couchbase Mobile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mobile-couchbase/gyuXLuUGLlM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mobile-couchbase+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mobile-couchbase/B1CCFC93-AD04-4592-A036-294F4C7CF20A%40couchbase.com.

For more options, visit https://groups.google.com/d/optout.

Jens Alfke

unread,
Nov 14, 2016, 11:58:17 AM11/14/16
to mobile-c...@googlegroups.com

On Nov 12, 2016, at 1:40 AM, Nick Pomfret <npom...@snowmonkey.co.uk> wrote:

My theory was that databases created before I made the change suffered from the problem.  And when they delete the app they delete the database and the new database gets created with the new code that uses NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication.

Looking at the code, this setting only takes effect when the CBLManager’s root directory is created. (See CBLManager.m:220.) That would explain what you’re seeing.

You can change the mode of an existing installed app by using NSFileManager to set the NSFileProtectionKey attribute of the CBLManager's directory. I think setting it on the top-level directory is enough, because that attribute is inherited by subdirectories and files.

—Jens

Nick Pomfret

unread,
Nov 21, 2016, 5:01:45 PM11/21/16
to mobile-c...@googlegroups.com
Ok, I tried setting the file permission mode on the top level directory, but it didn't seem to work - that one client is still having problems. So maybe the solution is to set the file permission mode explicitly on every file and subdirectory.  I'm not going to bother trying it, as deleting the app and starting from scratch with the correct permissions seems to work and I only have 1 installation thats suffering from this problem now.

So in conclusion: if you want a CBL iOS app to work in the background (for things like background-fetch and push notifications), I believe that when the app is first installed you need to create the CBLManager using the NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication option:

  CBLManagerOptions options = {NO, NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication};

  manager = [[CBLManager alloc] initWithDirectory: dir options: &options error: &error];





--
You received this message because you are subscribed to a topic in the Google Groups "Couchbase Mobile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mobile-couchbase/gyuXLuUGLlM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mobile-couchbase+unsubscribe@googlegroups.com.

Jens Alfke

unread,
Nov 21, 2016, 10:05:06 PM11/21/16
to mobile-c...@googlegroups.com

On Nov 21, 2016, at 2:01 PM, Nick Pomfret <npom...@snowmonkey.co.uk> wrote:

So in conclusion: if you want a CBL iOS app to work in the background (for things like background-fetch and push notifications), I believe that when the app is first installed you need to create the CBLManager using the NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication option:

I’ve been investigating this issue today. The situation is pretty confusing. The most informative thing I’ve found is this blog post, but it describes a bunch of different issues, some of which I think are unrelated, so it’s hard to tell exactly how relevant it is.

When your app delegate gets called about the push notification, do you start a background session in addition to starting the replication? From that post it sounds like you should. I’m making some changes to the replicator to start its own bg session in this circumstance, i.e. if the replicator starts when the app is already in the background.

—Jens

Nick Pomfret

unread,
Nov 22, 2016, 12:58:04 AM11/22/16
to mobile-c...@googlegroups.com
What's a background session?

When a push notification arrives what typically happens is the app gets started from scratch by the OS because it previously got suspended. During start up the first thing the app does is a db query (it doesn't start replication until a bit after). It's that first query that fails with a 500 error and so my app won't start.

Very difficult to reproduce because there doesn't seem to be a way to reliably get the OS to suspend the backgrounded app.

This particular problem is nothing to do with replication, however...

I think there's a separate issue surrounding replication tasks that are running when the app is running in the background. In this situation, if a push request arrives, the replication tasks don't seem to leave their suspended state, and so can't be used to pull in the new data. To make matters worse, my replication tasks were started via the rest api, and there doesn't seem to be a way to manually revive them.

--
You received this message because you are subscribed to a topic in the Google Groups "Couchbase Mobile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mobile-couchbase/gyuXLuUGLlM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mobile-couchba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mobile-couchbase/7B2FF7C6-6E44-4D04-BD46-3E381868359E%40couchbase.com.

Jens Alfke

unread,
Nov 22, 2016, 1:28:48 PM11/22/16
to mobile-c...@googlegroups.com
On Nov 21, 2016, at 9:57 PM, Nick Pomfret <npom...@snowmonkey.co.uk> wrote:

What's a background session?

It’s a way the app can ask the OS for some extra time after it’s backgrounded, before it gets suspended.

When a push notification arrives what typically happens is the app gets started from scratch by the OS because it previously got suspended.

That's “terminated”, not “suspended”. (A suspended app still has a process, it’s just not getting any CPU time.)

During start up the first thing the app does is a db query (it doesn't start replication until a bit after). It's that first query that fails with a 500 error and so my app won't start.

Interesting. I’m wondering about the exact timing here. By “during start up”, which UIApplicationDelegate event are you referring to? -applicationDidFinishLaunching, -application:didReceiveRemoteNotification:, … ?

I’m a bit worried that since you’re a PhoneGap app, it may not be very predictable when your JS code starts running — it’s up to the PhoneGap container, which apparently wasn’t designed to support push notifications(?), and it’s going to be asynchronous, so your JS code is not running during the app-delegate callback but at some time after it returns.

Very difficult to reproduce because there doesn't seem to be a way to reliably get the OS to suspend the backgrounded app.

From what I’ve read, one way to get a backgrounded app to terminate is to launch one or more memory-hungry apps, so the OS will start trying to free up RAM. The blog post I mentioned earlier said Pokémon Go works well for this :)

—Jens

Nick Pomfret

unread,
Nov 22, 2016, 5:39:22 PM11/22/16
to mobile-c...@googlegroups.com
I was creating a background task, not a session:

[[UIApplication sharedApplication] beginBackgroundTaskWithName:taskName expirationHandler:^{
Am I using the wrong thing here?

Suspended / terminated, sorry yes, getting my names mixed up there.  Suspended is the state prior to terminated, right?

When I mention "during start up", the order is didFinishLaunchingWithOptions followed by didReceiveRemoteNotification.  When the app is launched from a terminated state (or otherwise)  didFinishLaunchingWithOptions will invoke the JS code.  The JS code starts a CBLManager (which works fine), and then issues its first query - it's here that it fails.  The js code dies (because of the 500 error) so the app never get's as far as being able to deal with the notification.

Just to re-iterate, this 500 error seems to have gone away for apps that were installed after I changed my code to instantiate the CBLManager using NSFileProtectionCompleteUntilFirstUserAuthentication.  Would it be possible to make a change to add an error message, it might shed some light on the issue.

Thanks for the tip about getting the OS to terminate the app.








--
You received this message because you are subscribed to a topic in the Google Groups "Couchbase Mobile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mobile-couchbase/gyuXLuUGLlM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mobile-couchbase+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mobile-couchbase/485A7E21-E24E-48B2-8022-A7CA3FE4A6BF%40couchbase.com.
Reply all
Reply to author
Forward
0 new messages