parse-community / Parse-SDK-iOS-OSX

The Apple SDK for Parse Platform (iOS, macOS, watchOS, tvOS)
https://parseplatform.org
Other
2.81k stars 871 forks source link

App crashes : objectIds cannot be changed in offline mode. #1290

Open nitikorndev opened 6 years ago

nitikorndev commented 6 years ago

Hi, my app crashes a lot in a past few week affect many users but I can't not find step to reproduce this crash. Anyone got the same issue ?

objectIds cannot be changed in offline mode. Fatal Exception: NSInternalInconsistencyException 0 CoreFoundation 0x1c27bb3d exceptionPreprocess 1 libobjc.A.dylib 0x1b503067 objc_exception_throw 2 CoreFoundation 0x1c27ba85 -[NSException initWithCoder:] 3 Parse 0x177cce5 -[PFOfflineStore updateObjectIdForObject:oldObjectId:newObjectId:] (PFOfflineStore.m:1029) 4 Parse 0x175f6f9 -[PFObject _notifyObjectIdChangedFrom:toObjectId:] (PFObject.m:1773) 5 Parse 0x175f295 -[PFObject set_state:] (PFObject.m:1729) 6 Parse 0x175bfe7 -[PFObject(Private) _mergeFromServerWithResult:decoder:completeData:] (PFObject.m:1275) 7 Parse 0x175bde7 -[PFObject(Private) _mergeAfterSaveWithResult:decoder:] (PFObject.m:1268) 8 Parse 0x175c70b 43-[PFObject(Private) handleSaveResultAsync:]_block_invoke (PFObject.m:1331) 9 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 10 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 11 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 12 Bolts 0x115b84f -[BFTask continueWithExecutor:block:cancellationToken:] (BFTask.m:368) 13 Bolts 0x115b6e9 -[BFTask continueWithExecutor:withBlock:] (BFTask.m:316) 14 Bolts 0x115aeef +[BFTask taskFromExecutor:withBlock:] (BFTask.m:227) 15 Parse 0x175c4f1 -[PFObject(Private) handleSaveResultAsync:] (PFObject.m:1327) 16 Parse 0x174ab41 -[PFInstallation(Private) handleSaveResultAsync:] (PFInstallation.m:103) 17 Parse 0x1780eff 79-[PFPinningEventuallyQueue _didFinishRunningCommand:withIdentifier:resultTask:]_block_invoke.115 (PFPinningEventuallyQueue.m:232) 18 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 19 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 20 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 21 Bolts 0x115b84f -[BFTask continueWithExecutor:block:cancellationToken:] (BFTask.m:368) 22 Bolts 0x115bccf -[BFTask continueWithBlock:] (BFTask.m:375) 23 Parse 0x1780ced 79-[PFPinningEventuallyQueue _didFinishRunningCommand:withIdentifier:resultTask:]_block_invoke (PFPinningEventuallyQueue.m:231) 24 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 25 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 26 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 27 Bolts 0x115b5df -[BFTask runContinuations] (BFTask.m:307) 28 Bolts 0x115b057 -[BFTask trySetResult:] (BFTask.m:247) 29 Bolts 0x115c661 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45) 30 Bolts 0x115baa3 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:354) 31 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 32 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 33 Bolts 0x115b5df -[BFTask runContinuations] (BFTask.m:307) 34 Bolts 0x115b057 -[BFTask trySetResult:] (BFTask.m:247) 35 Bolts 0x115c661 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45) 36 Bolts 0x115bbaf 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340) 37 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 38 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 39 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 40 Bolts 0x115b5df -[BFTask runContinuations] (BFTask.m:307) 41 Bolts 0x115b057 -[BFTask trySetResult:] (BFTask.m:247) 42 Bolts 0x115c661 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45) 43 Bolts 0x115bbaf 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340) 44 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 45 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 46 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 47 Bolts 0x115b5df -[BFTask runContinuations] (BFTask.m:307) 48 Bolts 0x115b057 -[BFTask trySetResult:] (BFTask.m:247) 49 Bolts 0x115c661 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45) 50 Bolts 0x115bbaf 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340) 51 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 52 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 53 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 54 Bolts 0x115b5df -[BFTask runContinuations] (BFTask.m:307) 55 Bolts 0x115b057 -[BFTask trySetResult:] (BFTask.m:247) 56 Bolts 0x115c661 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45) 57 Bolts 0x115bbaf 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340) 58 Bolts 0x115b9e5 55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331) 59 Bolts 0x11593e9 29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66) 60 Bolts 0x115982f -[BFExecutor execute:] (BFExecutor.m:131) 61 Bolts 0x115b5df -[BFTask runContinuations] (BFTask.m:307) 62 Bolts 0x115b057 -[BFTask trySetResult:] (BFTask.m:247) 63 Bolts 0x115c661 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45) 64 Bolts 0x115bbaf __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340) 65 Bolts 0x115ba8b __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:348) 66 libdispatch.dylib 0x1b955a35 _dispatch_barrier_sync_f_invoke 67 Parse 0x179fbcf PFThreadsafetySafeDispatchSync (PFThreadsafety.m:31) 68 libdispatch.dylib 0x1b949797 _dispatch_call_block_and_release 69 libdispatch.dylib 0x1b9582db _dispatch_root_queue_drain 70 libdispatch.dylib 0x1b95800f _dispatch_worker_thread3 71 libsystem_pthread.dylib 0x1bafe87d _pthread_wqthread 72 libsystem_pthread.dylib 0x1bafe45c start_wqthread

flovilmart commented 6 years ago

Update to the latest version, this assertion has been removed

nitikorndev commented 6 years ago

@flovilmart Do you know root cause or step to make the crash happen I want to test after I update the latest SDK Thanks.

flovilmart commented 6 years ago

The runtime assertion has been removed so there’s no real way to force it now. See https://github.com/parse-community/Parse-SDK-iOS-OSX/pull/1266

nitikorndev commented 6 years ago

@flovilmart I found the assertion still in the code

https://github.com/parse-community/Parse-SDK-iOS-OSX/pull/1266/commits/49bae5cf0316db88339a92b089426680830ac00e

PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode.");

flovilmart commented 6 years ago

So basically this happens when you attempt to set an objectid on an existing object with the local datastore enabled. Previously, bolts would swallow all exceptions, now they are not. Are you using local datastore and live Query?

nitikorndev commented 6 years ago

@flovilmart Yes, I enable isLocalDatastoreEnabled = true . but I didn't set any objectid to object. Do you know any cause that make the crash happen ?

nitikorndev commented 6 years ago

@flovilmart Seem like the app try to merge the server data to local db and found the objectId change and that cause the app crash ? Is it can be the server issue ? Thanks for looking

flovilmart commented 6 years ago

Are you using liveQuery or the PFDecoders in your code? Or even creating objects directly from JSON?

nitikorndev commented 6 years ago

@flovilmart NOW my team decide to disable offline mode. but when I change isLocalDatabase = false the user session gone I think I have to force logout Do you have any suggestion ? Thanks for take a look.

flovilmart commented 6 years ago

this is odd, the session should be stored into the keychain. But that’s probably a bad idea to disable local datastore without further investigation on your side of the implications. On my side I won’t have the time to look into this issue you encounter when disabling local datastore. I’ll probably have time to investigate the other issue at hand

speedyacme commented 6 years ago

Same issue here with 1.17.1. I use saveEventually() and get the same crash/error

andreasvestergaard commented 6 years ago

I can reproduce a similar crash by calling saveEventually() followed by saveInBackground() with the local datastore enabled on a slow connection (simulated Edge). Was experimenting with this to force an immediate save with saveEventually().

I guess a conflicting objectId is created by the subsequent call of saveInBackground()?

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'objectIds cannot be changed in offline mode.'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010ce3c1e6 __exceptionPreprocess + 294
    1   libobjc.A.dylib                     0x000000010c4d9031 objc_exception_throw + 48
    2   CoreFoundation                      0x000000010ceb1975 +[NSException raise:format:] + 197
    3   Parse                               0x0000000109c43976 -[PFOfflineStore updateObjectIdForObject:oldObjectId:newObjectId:] + 630
    4   Parse                               0x0000000109c07419 -[PFObject _notifyObjectIdChangedFrom:toObjectId:] + 265
    5   Parse                               0x0000000109c06d4d -[PFObject set_state:] + 461
    6   Parse                               0x0000000109c00f0d -[PFObject(Private) _mergeFromServerWithResult:decoder:completeData:] + 413
    7   Parse                               0x0000000109c00bf9 -[PFObject(Private) _mergeAfterSaveWithResult:decoder:] + 665
    8   Parse                               0x0000000109c01c86 __43-[PFObject(Private) handleSaveResultAsync:]_block_invoke + 150
    9   Bolts                               0x0000000106fa54c5 __37+[BFTask taskFromExecutor:withBlock:]_block_invoke + 69
    10  Bolts                               0x0000000106fa65c8 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 104
    11  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    12  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    13  Bolts                               0x0000000106fa646c -[BFTask continueWithExecutor:block:cancellationToken:] + 860
    14  Bolts                               0x0000000106fa60d4 -[BFTask continueWithExecutor:withBlock:] + 116
    15  Bolts                               0x0000000106fa5424 +[BFTask taskFromExecutor:withBlock:] + 244
    16  Parse                               0x0000000109c0190a -[PFObject(Private) handleSaveResultAsync:] + 298
    17  Parse                               0x0000000109c4c5d9 __79-[PFPinningEventuallyQueue _didFinishRunningCommand:withIdentifier:resultTask:]_block_invoke.115 + 137
    18  Bolts                               0x0000000106fa65c8 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 104
    19  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    20  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    21  Bolts                               0x0000000106fa646c -[BFTask continueWithExecutor:block:cancellationToken:] + 860
    22  Bolts                               0x0000000106fa6c94 -[BFTask continueWithBlock:] + 132
    23  Parse                               0x0000000109c4c08e __79-[PFPinningEventuallyQueue _didFinishRunningCommand:withIdentifier:resultTask:]_block_invoke + 974
    24  Bolts                               0x0000000106fa65c8 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 104
    25  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    26  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    27  Bolts                               0x0000000106fa6b4a __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke.126 + 42
    28  Bolts                               0x0000000106fa5ef2 -[BFTask runContinuations] + 754
    29  Bolts                               0x0000000106fa56cc -[BFTask trySetResult:] + 252
    30  Bolts                               0x0000000106fa7cb9 -[BFTaskCompletionSource setResult:] + 89
    31  Bolts                               0x0000000106fa676d __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 525
    32  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    33  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    34  Bolts                               0x0000000106fa6b4a __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke.126 + 42
    35  Bolts                               0x0000000106fa5ef2 -[BFTask runContinuations] + 754
    36  Bolts                               0x0000000106fa56cc -[BFTask trySetResult:] + 252
    37  Bolts                               0x0000000106fa7cb9 -[BFTaskCompletionSource setResult:] + 89
    38  Bolts                               0x0000000106fa68f1 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 + 353
    39  Bolts                               0x0000000106fa65c8 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 104
    40  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    41  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    42  Bolts                               0x0000000106fa6b4a __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke.126 + 42
    43  Bolts                               0x0000000106fa5ef2 -[BFTask runContinuations] + 754
    44  Bolts                               0x0000000106fa56cc -[BFTask trySetResult:] + 252
    45  Bolts                               0x0000000106fa7cb9 -[BFTaskCompletionSource setResult:] + 89
    46  Bolts                               0x0000000106fa68f1 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 + 353
    47  Bolts                               0x0000000106fa65c8 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 104
    48  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    49  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    50  Bolts                               0x0000000106fa6b4a __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke.126 + 42
    51  Bolts                               0x0000000106fa5ef2 -[BFTask runContinuations] + 754
    52  Bolts                               0x0000000106fa56cc -[BFTask trySetResult:] + 252
    53  Bolts                               0x0000000106fa7cb9 -[BFTaskCompletionSource setResult:] + 89
    54  Bolts                               0x0000000106fa68f1 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 + 353
    55  Bolts                               0x0000000106fa65c8 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 104
    56  Bolts                               0x0000000106fa1fd9 __29+[BFExecutor defaultExecutor]_block_invoke_2 + 185
    57  Bolts                               0x0000000106fa297e -[BFExecutor execute:] + 94
    58  Bolts                               0x0000000106fa6b4a __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke.126 + 42
    59  Bolts                               0x0000000106fa5ef2 -[BFTask runContinuations] + 754
    60  Bolts                               0x0000000106fa56cc -[BFTask trySetResult:] + 252
    61  Bolts                               0x0000000106fa7cb9 -[BFTaskCompletionSource setResult:] + 89
    62  Bolts                               0x0000000106fa68f1 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 + 353
    63  Bolts                               0x0000000106fa66d1 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 369
    64  libdispatch.dylib                   0x000000010e2d47ec _dispatch_client_callout + 8
    65  libdispatch.dylib                   0x000000010e2db55c _dispatch_queue_barrier_sync_invoke_and_complete + 374
    66  Parse                               0x0000000109c88fb7 PFThreadsafetySafeDispatchSync + 151
    67  Parse                               0x0000000109c81a63 __33-[PFSQLiteDatabase initWithPath:]_block_invoke_2 + 35
    68  libdispatch.dylib                   0x000000010e2d37ab _dispatch_call_block_and_release + 12
    69  libdispatch.dylib                   0x000000010e2d47ec _dispatch_client_callout + 8
    70  libdispatch.dylib                   0x000000010e2e061d _dispatch_root_queue_drain + 1353
    71  libdispatch.dylib                   0x000000010e2e0076 _dispatch_worker_thread3 + 132
    72  libsystem_pthread.dylib             0x000000010e7ff169 _pthread_wqthread + 1387
    73  libsystem_pthread.dylib             0x000000010e7febe9 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
flovilmart commented 6 years ago

@andreasvestergaard thanks for the report this is very interesting and yes that's likely to be the cause.

Now I'm left wondering how we can properly solve the issue. As we send two save command, it is normal that two HTTP calls are sent. The second save response is causing the issue as the first one succeeded and stored the object in the store.

My big question is which object should we keep? the last successfully saved one or the first one and dropping the second response as both are responses for the same save intent in this case.

andreasvestergaard commented 6 years ago

@flovilmart You’re very welcome. Thanks for looking into it.

I would say keep the object which is created by the last save command. That object could be successfully saved first or last as both functions are asynchronous, right?

That would ensure any updates made to the object between the first and the second call would get saved.

I ran in to this issue trying to force an immediate save to the server and the local datastore while falling back to saveEventually() if there's no connection. This is my current workaround which seems to work:

        object.pinInBackground() 
        object.saveInBackground { (success, error) in 
                if success  == false {
                    object.saveEventually() 
                }
        }
flovilmart commented 6 years ago

That’s not very convenient as a workaround. Are you willing to attempt a PR with the change? As you have the issue and can reproduce, we can attest of the fix :)

andreasvestergaard commented 6 years ago

It's definitely not pretty :) Would love to contribute and fix it but after taking a closer look in PFObject.m I probably wouldn't be able to at the moment.

My 2 cents would be to tweak saveEventually() to save objects immediately if possible. Like saveInBackground() does it.

As a user friendly long term solution i would deprecate one of the functions and have just one for saving an object to the server. This function would

flovilmart commented 6 years ago

I probably wouldn't be able to at the moment

If you don't try, you'll never know.

i would deprecate one of the functions and have just one for saving an object to the server.

I'm not sure this would solve the issue at all.

neil-degoo commented 5 years ago

I can pick up this and help with this issue. @flovilmart Just tell me what would be the best way to fix it?

I can include @andreasvestergaard workaround inside saveInBackground and pinObjectAutomatically if localStoreEnabled?

flovilmart commented 5 years ago

@neil-degoo I am not sure actually.

neil-degoo commented 5 years ago

@flovilmart who is the best candidate to discuss with?

flovilmart commented 5 years ago

As it stands in the discussion, the preferred resolution would be not to crash.

It is known for slow operations that two saves may result in a crash as the object to be saved is added twice to the local data store, instead of once.

We could check that the two objects are consistent and not crash if both are valid. We could also check the updatedAt property, and keep the most recently updated one.

tamir-maoz commented 5 years ago

Hi, I'm experiencing the same issue. Any progress made here? Thanks

noobs2ninjas commented 4 years ago

So I know that this hasn't been discussed in a long time. I'm curious if anyone is still having issues with the latest version 1.17.3?

LaChrome commented 2 years ago

I experienced this issue for the first time this week, and it's happening fairly often for me now with a newly staged server.

iOS Client code has remained the same, Parse server was upgraded from 4.2.0 to 5.2.0.

I'll do some testing to see what I can find out.