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

Crash after receiving 401 error from CBLReplication (ASSERTION FAILED: storage) #805

Closed
pahmed opened this issue Jul 5, 2015 · 14 comments
Closed
Assignees
Milestone

Comments

@pahmed
Copy link

pahmed commented Jul 5, 2015

I'm having a crash that happens in my app whenever i receive 401 from the pull replication, it didn't happen in version 1.0.3 before i update to 1.1. Here are some logs

WARNING: CBLReplication[to https://*****/*****]: Timeout waiting for background stop notification
ASSERTION FAILED: storage
Assertion failure in -[CBLQueryRow value](), /Users/jenkins/jenkins/workspace/couchbase-lite-ios-builds/couchbase-lite-ios/Source/API/CBLQueryRow.m:129
@snej
Copy link
Contributor

snej commented Jul 5, 2015

Is the backtrace of the crash available?

@snej
Copy link
Contributor

snej commented Jul 5, 2015

Also, could you show the code for the query that generates the crash? I'd like to know whether it's a view or allDocs, whether it's grouped/reduced, etc. Thanks.

@pahmed
Copy link
Author

pahmed commented Jul 6, 2015

I have a liveQuery and using it as the data source for a tableView, whenever an update happens to the liveQuery, i iterate over the rows, make some filtration if needed and put the rows in an Array and reload the tableView. This is a similar code in cellForRowAtIndexPath

CBLQueryRow *row = self.myRowsArray[indexPath.row];
NSDictionary *object = row.value;

This is the backtrace

Thread : Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x21b00fef __exceptionPreprocess
1  libobjc.A.dylib                0x30444c8b objc_exception_throw
2  CoreFoundation                 0x21b00ec5 +[NSException raise:format:]
3  Foundation                     0x22800f73 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
4  *****                        0x004d85e7 _AssertFailed (Test_Assertions.m:26)
5  *****                        0x004ea7d7 -[CBLQueryRow value] (CBLQueryRow.m:129)
6  *****                        0x00106d99 -[PXContactsVC configureCell:atIndexPath:] (PXContactsVC.m:823)
7  *****                        0x0010743b -[PXContactsVC tableView:cellForRowAtIndexPath:] (PXContactsVC.m:896)
8  UIKit                          0x2544e31b -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:]
9  UIKit                          0x2544e3df -[UITableView _createPreparedCellForGlobalRow:willDisplay:]
10 UIKit                          0x25442fc3 -[UITableView _updateVisibleCellsNow:isRecursive:]
11 UIKit                          0x25251607 -[UITableView layoutSubviews]
12 UIKit                          0x2517afd3 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
13 QuartzCore                     0x24b8ed29 -[CALayer layoutSublayers]
14 QuartzCore                     0x24b8a55d CA::Layer::layout_if_needed(CA::Transaction*)
15 UIKit                          0x2518dbb3 -[UIView(Hierarchy) layoutBelowIfNeeded]
16 *****                        0x000d0ca9 __40-[PXMainVC setLoginViewHidden:animated:]_block_invoke (PXMainVC.m:530)
17 UIKit                          0x251af147 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:]
18 UIKit                          0x251aef5b +[UIView(UIViewAnimationWithBlocks) animateWithDuration:animations:]
19 *****                        0x000d0c13 -[PXMainVC setLoginViewHidden:animated:] (PXMainVC.m:530)
20 libdispatch.dylib              0x309d52e3 _dispatch_call_block_and_release
21 libdispatch.dylib              0x309d52cf _dispatch_client_callout
22 libdispatch.dylib              0x309d8d2f _dispatch_main_queue_callback_4CF
23 CoreFoundation                 0x21ac6609 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
24 CoreFoundation                 0x21ac4d09 __CFRunLoopRun
25 CoreFoundation                 0x21a11201 CFRunLoopRunSpecific
26 CoreFoundation                 0x21a11013 CFRunLoopRunInMode
27 GraphicsServices               0x294e2201 GSEventRunModal
28 UIKit                          0x251dda09 UIApplicationMain
29 *****                        0x00125faf main (main.m:14)
30 libdyld.dylib                  0x309f6aaf start

@snej
Copy link
Contributor

snej commented Jul 6, 2015

I'll need to see the code that creates and configures the CBLQuery. Thanks.

@pahmed
Copy link
Author

pahmed commented Jul 9, 2015

This is all the configurations i make, no group or reduce.

CBLView *view = [db viewNamed:@"VIEW_NAME"];
    if (!view.mapBlock) {
        __weak id weakSelf = self;
        [view setMapBlock:^(NSDictionary *doc, CBLMapEmitBlock emit) {
            // Emit document if matching criteia 
        } reduceBlock:nil version:VIEW_VERSION];
    }
    CBLQuery *query = [view createQuery];

    NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:@"key" ascending:YES comparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
        BOOL isNumeric1 = [obj1 hasNumericPrefix];
        BOOL isNumeric2 = [obj2 hasNumericPrefix];

        if (isNumeric1 == isNumeric2) {
            return [obj1 compare:obj2 options:NSCaseInsensitiveSearch];
        } else if (isNumeric1) {
            return NSOrderedDescending;
        } else {
            return NSOrderedAscending;
        }
    }];
    query.sortDescriptors = @[sd];

@snej
Copy link
Contributor

snej commented Jul 10, 2015

(Off-topic, but the line

        __weak id weakSelf = self;

right above the map block looks like you're referencing self in the block. That's a bad idea — the map function has to be a 'pure' function that can't use any external data other than the document. If you're trying to make the map function behave differently based on the current state of the object, that's not going to work. However, it's not the cause of this crash.)

@snej
Copy link
Contributor

snej commented Jul 10, 2015

Weird … I'm going through the code and I can't see a way this could happen. You're not deleting the view, are you?

@pahmed
Copy link
Author

pahmed commented Jul 12, 2015

I'm using self only to access a helper method and it does not depend on the state of self, it depends only on the document passed from the map block and it is pure.

And sorry, I think the issue is not related to 401, it happens after deleting the database.

@snej
Copy link
Contributor

snej commented Jul 12, 2015

Sounds fine, I was just checking because it's a common mistake to reference mutable state in a map function!

it happens after deleting the database.

Can you give more detail? Particularly, is there a chance the query row is being referenced after the database is deleted? I think that might be able to trigger this crash.

@snej
Copy link
Contributor

snej commented Jul 20, 2015

@pahmed, could you confirm whether the CBLQueryRow was accessed after the database was deleted? If so, I think I understand what's going on and can add a workaround. Otherwise, I'm still stumped. Thanks.

@pahmed
Copy link
Author

pahmed commented Jul 21, 2015

Yes, and i was also able to reproduce the issue from lldb by adding breakpoint, delete the database then try accessing the value for CBLQueryRow.

@snej
Copy link
Contributor

snej commented Jul 22, 2015

How did you delete the database while the app was stopped at a breakpoint?

I can add a fix for the crash, but all it will do is make row.value return nil instead. You really shouldn't be using a query row after its database has been deleted, because there are cases where it needs to call the database to do things.

@pahmed
Copy link
Author

pahmed commented Jul 22, 2015

I have a helper method i use to delete the database, and from lldb i'm executing an
expression similar to this p [DatabaseHelper deleteDatabase]

For the normal cases i do not use CBLQueryRow while the database is deleted, but when i receive 401 i delete the database and log the user out, and it seems that at the moment just before the logout, something triggers the tableView reloadData where i configure the tableViewCell and access the row.value

@snej snej added this to the 1.2 milestone Jul 22, 2015
@snej snej self-assigned this Jul 22, 2015
@snej snej closed this as completed in d9ca410 Jul 22, 2015
@snej
Copy link
Contributor

snej commented Jul 22, 2015

OK, this won't crash anymore, but CBLQueryRow.value will now return nil if it's called for the first time after the database is deleted. (It caches the value, so if it was called once before the db was deleted it will still return the correct value afterwards.)

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

No branches or pull requests

3 participants