Closed seaders closed 9 years ago
I'm also interested in this for the use case of cleanly unlinking a user from Facebook and having cognito play nicely again with the new (non-Facebook) identity. @seaders you might want to post this in the separate Unity Cognito github as well: https://github.com/aws/amazon-cognito-unity
Sorry for the delay! I was finally able to reproduce the problem. I will try to write a fix as soon as possible and update this thread.
I wrote a patch that is meant to replace the ChangeIdentityId function in SQLiteLocalStorage, and should fix the problem. Give it a try:
https://gist.github.com/albertvaka/ab8ad731aba8777ad9fc
Note, though, that this patch will just rename the old dataset in stead of crashing. This should automatically trigger onDatasetMerge afterwards so you can decide how you want to handle the conflict, but won't resolve it for you.
(Due to many, many bugs with going to Unity 5...) I only got around to implementing this solution now and unfortunately it looks like it hasn't fixed the problem. I got the printout,
Reparenting datasets from us-east-1:<LONG_ID> to us-east-1:<OTHER_LONG_ID>
But then after that, got the same error,
AmazonClientException: UNIQUE constraint failed: datasets.identity_id, datasets.dataset_name
Amazon.Unity3D.Storage.SQLiteStatement.FinalizeStm () (at Assets/AWSUnitySDK/AWSCore/Amazon.Unity3D/Storage/SQLiteDatabase.cs:317)
Amazon.CognitoSync.SyncManager.Storage.SQLiteLocalStorage.ChangeIdentityId (System.String oldIdentityId, System.String newIdentityId) (at Assets/AWSUnitySDK/CognitoSync/Amazon.CognitoSync/SyncManager/Storage/SQLiteLocalStorage.cs:814)
I'm still working this out, but I believe it is caused by doing something like credentials.AddLogin to an already authorized ID, which will change your ID when it returns. That is all fine and intentional, but if you call something that uses those credentials RIGHT AFTER calling AddLogin (like BatchGet), it'll start that call using the old credentials because credentials.IdentityChangedEvent hasn't been called yet. So if IdentityChangedEvent triggers before you receive the response from that BatchGet call, the response triggers with the new credentials with the new ID, which throws the error.
The thing is, I don't think the IdentityChangedEvent will call if the identity doesn't change from addlogin, so it's not like you can even use that for every scenario.
I'm still trying to work it out myself, I'll let you know how it goes..
Just following up with anybody else who gets this error. What I explained above was indeed the case. Easy solution: If you can afford to, just run any functions like BatchGet a little bit later once your identity is actually changed. If you can't do that, I came across my own solution:
It's a pain but what I ended up doing was wrapping my BatchGet call in a Coroutine and just waiting for it to finish (otherwise if you try and do that from within the callback, you get errors for not running stuff on the main thread). In the callback, I check for exceptions--and if they exist, I do an exponential runoff OUTSIDE of the callback in the coroutine. In the callback, if I encounter an AmazonClientException, I check if the error message starts with "UNIQUE constraint failed" (hacky, I know but get's it done---wish it had an error code). If that returns true, then I set the coroutine to run my BatchGet oneeeee more time (after your exponential runoff delay of course). It runs perfectly fine that second time.
I wish there was a better solution, but the whole different threads issue creates some problems--at least it did for me. Really limited solutions here. Either do that, or run those async functions later...
Any other solutions would be greatly appreciated.
what version of sdk are you using and what is your unity version?
Credentials.AddLogin will not make any calls to cognito. It will only clear your credentials stored in your local reference and your identity may or may not change.
When you make a BatchGet call right after the AddLogin. since the local credentials are cleared the sdk will make calls to fetch the new credentials from cognito. As a part of the response cognito sends the new credentials and an identity id which may be diff from the identity id before and if the latter happens we trigger the identity change event.
In short the sdk ensures that the latest identity is used before making any calls.
Unity 5.1.2 with AWS 2.1.0.1
So this is how it's reproduced:
So essentially: facebookloginsuccess { credentials.addlogin; facebook.getuserdetails; facebook.getfriends(runBatchGetOnEnd); dataset.update&sync; }
Is there something wrong there?
In the console this is what I see:
thats strange this lock here is supposed to prevent exactly what you describe. I'll investigate this and let you know.
Though a simple solution would be to just make a dummy call to getIdentityasync api right after calling addlogins so you always have the right identity id
making that getidentityasync call right after AddLogin didn't work either =/
I tried putting some debug statements right before and after that lock statement AND at the end of the lock block. I outputted the time at each, up to milliseconds. When making the unauthenticated Id, the end of the lock block was significantly after the start of the lock block. HOWEVER, everytime after that had each call fire at the same time. I updated the console above with the new lines in bold:
FOR UNAUTH ID:
THEN ON AUTHENTICATION (from above): 0a. "before lock 2/9/2016 9:38:36:737255 AM" 0b. "in lock 2/9/2016 9:38:36:737255 AM" 0c. "end lock 2/9/2016 9:38:36:737255 AM" 0d. "before lock 2/9/2016 9:38:36:737255 AM" 0e. "in lock 2/9/2016 9:38:36:737255 AM" 0f. "end lock 2/9/2016 9:38:36:737255 AM"
Seems like the first call to get ID works fine, but subsequent calls probably skip that if statement inside the lock block. I'll continue looking into it
Oh and to clarify, when I ran this with the GetIdentityAsync after AddLogin, I just got the same behavior--it just happened again before the BatchGet call was made
so after seeing all that and testing all those if statements, I decided to just try calling RemoveLogin before calling AddLogin--and I'm no longer getting the error.
Now I don't know how this is going to effect anything else, but so far it's working
Ah sorry, I didn't call RemoveLogin, I called getidentityasync before AddLogin
calling it right after didn't fix it
So I'm overall asking about the strategy for when someone who had previously logged in to Facebook let their accessToken expire, and didn't bother logging in again.
I've currently gone with the strategy of recording to Unity's PlayerPrefs whether someone was authenticated with a social platform access token the last time they ran the app, and if they were logged in, but now aren't, I run
credentials.ClearCredentialsAndIdentity()
. This has to be done because it seems a credentials refresh doesn't work properly if you run it again without valid access tokens.Now, this happens ok for if someone was logged in, but now isn't, they swap from one ID to another, but if they then go to log in again afterwards, this is where the crash happens. In
SQLiteLocalStorage.cs - ChangeIdentityId
, it tries to run an update statement on the database, but it's trying to update the unauthenticatedidentity_id
to the authenticated one from earlier. Unfortunately, that is still in the database, so I get that exception,UNIQUE constraint failed: datasets.identity_id, datasets.dataset_name
.What should be done in this regard, removing the conflict at that point, ignoring it, or removing the entry when
credentials.ClearCredentialsAndIdentity()
was called?