Parse-SDK-iOS-OSX
Parse-SDK-iOS-OSX copied to clipboard
Pinning PFObject with pointer corrupts local datastore
Tested on version 1.9.1. If an object C1 pointing to P1 is pinned (and not saved), then an object C2 pointing to P2 is pinned, P1 is corrupted in the datastore. This seems to be the minimum condition to reproduce:
func letsCorruptTheDatastore(sender: AnyObject) {
// create a post with a comment and pin it
let post1 = PFObject(className:"Post")
post1["text"] = "I am post #1"
let comment1 = PFObject(className:"Comment")
comment1["post"] = post1
comment1.pinInBackground().continueWithSuccessBlock {
(task: BFTask!) -> AnyObject! in
// Make another post with comment and pin it
let post2 = PFObject(className:"Post")
post2["text"] = "I am post #2"
let comment2 = PFObject(className:"Comment")
comment2["post"] = post2
return comment2.pinInBackground()
}.continueWithSuccessBlock {
(task: BFTask!) -> AnyObject! in
// Now try to query local Post objects
let query = PFQuery(className:"Post")
query.fromLocalDatastore()
return query.findObjectsInBackground()
}.continueWithBlock {
(task: BFTask!) -> AnyObject! in
if (task.error != nil) { print(task.error) }
else if (task.result != nil) { print(task.result) }
return nil
}
}
The query fails with this error:
Error Domain=Parse Code=120 "Attempted to fetch and object offline which was never saved to the offline cache" UserInfo={error=Attempted to fetch and object offline which was never saved to the offline cache, code=120, NSLocalizedDescription=Attempted to fetch and object offline which was never saved to the offline cache}
Looking at the sqlite database, it is apparent that the data has been corrupted (nulled out), which results in queries on the corrupted data always failing.
sqlite>
sqlite> select * from ParseObjects where className = "Post";
41067A2D-5BE8-4BD8-8902-C453E790C69B|Post||{"className":"Post","__complete":true,"__operations":[{"__updatedAt":{"iso":"2015-11-10T16:58:07.687Z","__type":"Date"},"__uuid":"FD2A80A9-7E9F-469E-95DC-2DF4A0014361","text":"I am post #2"}],"isDeletingEventually":0}|0
616B0182-1A5B-4F44-BE05-204A80AAE557|Post|||0
sqlite> select * from ParseObjects where className = "Comment";
B847E0D4-725A-464D-AEA9-51D4CADCD66A|Comment||{"className":"Comment","__complete":true,"__operations":[{"__updatedAt":{"iso":"2015-11-10T16:58:07.691Z","__type":"Date"},"post":{"uuid":"616B0182-1A5B-4F44-BE05-204A80AAE557","__type":"OfflineObject"},"__uuid":"546E9AEF-63B7-460A-A130-BD547D11DB45"}],"isDeletingEventually":0}|0
AB5FAF2A-C698-4A4F-8011-1FC3602247D3|Comment||{"className":"Comment","__complete":true,"__operations":[{"__updatedAt":{"iso":"2015-11-10T16:58:07.688Z","__type":"Date"},"post":{"uuid":"41067A2D-5BE8-4BD8-8902-C453E790C69B","__type":"OfflineObject"},"__uuid":"D58AAEEE-093F-4C71-B002-F6F10B3E05D5"}],"isDeletingEventually":0}|0
sqlite>
digging a little deeper, it appears that first pin is not even done correctly, and that the bug is in pinning objects with pointers. After just saving a single Comment pointing to a Post, the sqlite database contains this data:
BD07816F-F2AA-4DC1-9807-BC191FCE58C8|_Pin||{"className":"_Pin","__complete":true,"__operations":[{"_objects":[{"uuid":"88B8BFB7-E56A-49E4-81FD-FC836897AE77","__type":"OfflineObject"}],"__uuid":"19FFB7FD-534F-4BFB-B6C8-EF17B16919C5","_name":"_currentUser","__updatedAt":{"iso":"2015-11-10T19:42:41.601Z","__type":"Date"}}],"isDeletingEventually":0}|0
88B8BFB7-E56A-49E4-81FD-FC836897AE77|_User||{"className":"_User","__complete":true,"__operations":[{"__uuid":"7A490326-1421-40B6-B867-EB13C346DA13","__updatedAt":{"iso":"2015-11-10T19:42:41.594Z","__type":"Date"}}],"isDeletingEventually":0}|0
7A2C3F1F-9DB7-4566-AFE1-0C26D02ABC1F|_Pin||{"className":"_Pin","__complete":true,"__operations":[{"_objects":[{"uuid":"8F91E10D-80CD-43DE-86E7-EFCB8BC81F88","__type":"OfflineObject"}],"__uuid":"24E5AEE3-1417-4BDE-A129-59907AA22574","_name":"_default","__updatedAt":{"iso":"2015-11-10T19:43:21.446Z","__type":"Date"}}],"isDeletingEventually":0}|0
9DD18DC7-D153-469A-8636-3DF1B5CA9B02|Post||{"className":"Post","__complete":true,"__operations":[{"__updatedAt":{"iso":"2015-11-10T19:43:21.443Z","__type":"Date"},"__uuid":"4242605C-52D6-4709-B02C-C42423C3B5E7","text":"I am another post"}],"isDeletingEventually":0}|0
8F91E10D-80CD-43DE-86E7-EFCB8BC81F88|Comment||{"className":"Comment","__complete":true,"__operations":[{"__updatedAt":{"iso":"2015-11-10T19:43:21.443Z","__type":"Date"},"post":{"uuid":"9DD18DC7-D153-469A-8636-3DF1B5CA9B02","__type":"OfflineObject"},"__uuid":"FC33D3E3-B185-45D3-B17C-EE26971B62E4"}],"isDeletingEventually":0}|0
sqlite>
The second pin (7A2C3F1F...) appears to include the Comment (8F91E10D...) in it's list of objects, but not the Post it points to (9DD18DC7...). Although maybe I'm not fulling understanding how pinning is implemented.
In the documentation it states: "Pinning a PFObject is recursive, just like saving, so any objects that are pointed to by the one you are pinning will also be pinned. "
This seems related to #239, though I don't have the slightest clue what could be causing this.
LocalDataStore is a very complex beast. and it's very possible there exists a pointer encoding bug there. I'll look into it.
And - thank you for the super clear repro case, it makes investigating this super easy.
OK, thanks. It looks like explicitly pinning referenced objects is a good workaround for now.
Hi, I've faced the same issue. I've used another workaround which is pinning with specific name. Have a nice day!
I have this problem too and I am already pinning with specific names. My object contains pointers that have pointers to other objects. When I initially pin them, they save correctly. But if I pin another object of the same type, they are all removed, and querying the local datastore returns nothing.
Hi, any news? I am experiencing the same on Android. I pin in bg an object that contains an array of pointers. Then, when I query for this kind of objects and I use include to retrieve pointers, I get error with code 120 and message Message: This object is not available in the offline cache. Thanks for your help!
Same issue here.
same issue here, really need a solution for that
Any update on this issue?
+1
+1
I'm sad with Parse local store. I need to finish an App ASAP and its very danger to persist my objects to sync later. Now I'm trying to implement my offline storage with IBM Cloudant or RealmSwift constructing a bridge on models to sync with Parse when data is committed. Please, could someone have another idea or its a good start?
Can you at least give us some info?
+1
This is killing me
I rember what fixed this for me! The problem was that I was pinning an object where some of the data hadn't been fetched. That resulted in a completely null object. So try fetching every referenced object first, and fetch the object itself, and then pin. Print out the data first to check its all there. If any object or pointed object doesn't contain data, the pin will result in a null ref.
Thanks for the suggestion, in the end it was me doing it wrong - I was including some objects in a query and not pinning all of them.
This fixed it for me, which is maddening as the documentations says...
Pinning a PFObject is recursive, just like saving, so any objects that are pointed to by the one you are pinning will also be pinned.
– https://www.parse.com/docs/ios/guide#local-datastore-pinning
Is a Pointer not considered "pointed to"? Strangely pinning an object with a property that is an Array of Pointers seems to be working fine.
I guess I should check the documentation for the OSS codebase as well.
any news on this? I'm getting the same error... I have includeKeys on both server and offline queries. The local queries work fine on the beginning, but then when I pin or unpin some objects "withName", and redo local query, I get this error. "This object is not available in the offline cache."
Same issue here. It's a terrible experience when user's data is gone.
I've put some more work into this... I'm not sure if I found a replicable pattern, but it seems like every time I pin objects back to the database, local queries work again... in my specific case, this happens:
- Server Query and pin all with name. Now local queries are working.
- unpin and deleteEventually (corrupts database and I can no longer do local queries). Note that local queries without "includeKey" still work fine.
- Sometimes, when pinning a object, it all get's working again, but I can't yet trust this behavior. My workaround, for now, is when I receive the referenced error, I query the server, then redo local query and it works again. Since it seems to only happen when unpinning objects, if user move around the app and not remove data, it will work local and no server queries. At least I get some speed when changing screens and loading data.
Well, I'm still waiting for a solution. :)
@ferrinho @wimbledon @rob5408 @Zwerge Are any of you still experiencing this issue?
Anyone still dealing with this?
Me , Horrible freaking experience , This object is not available in the offline cache is my error. Only thing that solved it for me was using specific pin names for every object. And one important thing to note, at first i was pinning using the objectId of the objects but later saw unpinning dosent work. I came to find that using objectId any string that long renders the pin label invalid thus unpin dosent work. So i suggest to keep pin labels as small as possible and to avoid pointers as much as possible
I am having similar issues with pin to local store.
In one case, I read records (with pointers) from DB, and then put them into a local set. When two distinct records bear the same logical (in business) IDs, they would count equal in the data set, and one would be discarded. This discard would ruin the local store, and subsequent query would report error saying "not available in offline cache". I removed this duplicate record (in business, not data sense), then local query starts working. This shouldn't be this fragile.
Another case I am debugging is pinned data (with pointers, if that matters) missing from data store, even do query immediately after pin. This doesn't always happen, but sure frustrating when it does. BTW, I am using parse-ios-sdk 1.17.3.
It looks I need to find a replacement for local storage, to not lose customer data. Any suggestion would be appreciated.
Had the same issue. Before get any field from the pointer call: myparseobject.fetchFromLocalDatastore();
I pinned before do that. Not know directly with network. Remeber before do pin to include the pointer name in the query