aws-amplify / amplify-swift

A declarative library for application development using cloud services.
Apache License 2.0
455 stars 196 forks source link

Delete publisher is not triggered on Amplify #2982

Closed Umar009 closed 9 months ago

Umar009 commented 1 year ago

Describe the bug

We integrated Amplify into our project for Android, iOS, and the web portal. We use Amplify for user sign-in and to store user data. However, we encountered an issue with the iOS app. While it works perfectly fine when iOS App is in active state, creating, deleting, or editing user item entries from the web or Android app get updated real time in iOS App, there is a problem when the iOS app is in the background or closed. Specifically, when a user creates a new entry and then deletes it while the app is in the background or closed, upon opening the app either from the background or a fresh launch, the create event is triggered, but the delete event is not called. Interestingly, if a user edits an item, the event for the edit is called. It's important to note that if an item is already added, and then the app is moved to the background and the device screen is locked, deleting that item from the web portal or Android app will trigger the delete event when the iOS app comes back from the background.

Steps To Reproduce

Steps to reproduce the behavior:
1. Login in web(or android) and iOS with valid user credential 
2. in iOS move App either background and lock the screen or close the App
3. create an item in user data base and then delete that item from web portal
4. open iOS App. (That delete item is not delete from iOS App)

Expected behavior

Delete event must be triggered on amplify and item must be deleted from Amplify data store

Amplify Framework Version

1.22.2

Amplify Categories

DataStore

Dependency manager

Cocoapods

Swift version

5

CLI version

10.7.3

Xcode version

14.2

Relevant log output

<details>
<summary>Log Messages</summary>

INSERT LOG MESSAGES HERE



### Is this a regression?

No

### Regression additional context

I don't know. I just encounter this issue

### Device

iphone Se 2020

### iOS Version

15.4

### Specific to simulators

_No response_

### Additional context

_No response_
Umar009 commented 1 year ago

waiting for response

harsh62 commented 1 year ago

@Umar009 Our team is prioritizing and will try to look into the issue.

lawmicha commented 1 year ago

Hi @Umar009,

I was able to reproduce the issue. I called DataStore.start to establish the subscriptions, and then put the iOS app in the background. Then I performed a create mutation followed by a delete mutation, while the app still in the background. ie.

mutation MyMutation {
  createTodo(input: {content: "From AppSync Console"}) {
    updatedAt
    owner
    id
    createdAt
    content
    _version
    _lastChangedAt
    _deleted
  }
}
mutation MyMutation2 {
  deleteTodo(input: {_version: 1, id: "e097e308-490f-4480-aba6-bcaf6dd41eec"}) {
    _deleted
    _lastChangedAt
    _version
    content
    createdAt
    id
    owner
    updatedAt
  }
}

Once I bring the app back to the foreground, I can see both events, one for the onCreate subscription and one for the onDelete subscription:

[StarscreamAdapter] websocketDidReceiveMessage: - {"id":"B016C8B9-7D7E-4E0E-9191-7B019C7E73A2","type":"data","payload":{"data":{"onCreateTodo":{"id":"e097e308-490f-4480-aba6-bcaf6dd41eec","content":"From AppSync Console","createdAt":"2023-08-09T15:43:00.643Z","updatedAt":"2023-08-09T15:43:00.643Z","__typename":"Todo","_version":1,"_deleted":null,"_lastChangedAt":1691595780670,"owner":"username"}}}}

[StarscreamAdapter] websocketDidReceiveMessage: - {"id":"3FD67560-3755-45A0-926A-3B1CF3A11662","type":"data","payload":{"data":{"onDeleteTodo":{"id":"e097e308-490f-4480-aba6-bcaf6dd41eec","content":"From AppSync Console","createdAt":"2023-08-09T15:43:00.643Z","updatedAt":"2023-08-09T15:43:00.643Z","__typename":"Todo","_version":2,"_deleted":true,"_lastChangedAt":1691595790277,"owner":"username"}}}}

I've attached the remaining logs for a reference. logs2982.log

It appears the DataStoreReconcileAndLocalSaveOperation is started with the version 1 object, and ends up inserting it into the database, while the delete event is not removing the version 1 object.

The unexpected behavior I can see is in IncomingAsyncSubscriptionEventToAnyModelMapper. If two events comes in, and Subscribers.Demand of max(1) is not returned before another event is emitted, i suspect it's not receiving this second event. There should be two dispose(of graphQLResponse) prints, but I only see one in the logs

It looks like I am able to reproduce the behavior in a small smaple in https://gist.github.com/lawmicha/e13a7d1100ccbb618c0f86b58e93c184

Emitting value 0
Control Demand 0
Received 0
Emitting value 1
Emitting value 2
Emitting value 4
Emitting value 3
Control Demand 1
Received 1

I'm emitting the values 0 to 4 (like the subscription create and delete events). the intermediate subscriber controls the demand to receive one at a time, before reemitting it. It looks like it's due to the publisher emitting events faster than the subscriber can handle them. I can explore changing this code to perhaps use a buffer operator instead and see if it will fix the problem. Even if this problem is fixed, we can't guaranteed that the events that were sent from the server are processed by the app while it's in the background. Once the websocket connetion is closed, DataStore will eventually catch up through the delta sync process.

lawmicha commented 1 year ago

@Umar009 One thing you can try in the meantime is to see if you can hook into the app lifecycle methods when it goes into foreground, or in did become active:

func applicationDidBecomeActive(_ application: UIApplication) {
        print("application did become active")
        Task {
            try? await Amplify.DataStore.stop()
            try? await Amplify.DataStore.start()
        }
    }

Calling stop then start will cause it to re-establish the subscriptions and more importantly perform the sync query calls to catch up the data. The deleted record should be in the sync query response, and will reconcile and remove the local model.

fzy-github commented 1 year ago

We are observing very similar issue but it is not related to deletion but rather updating an objects and it does not happen when app is in the background but is fully in foreground.

Sample case:

  1. Add number of items to the tabled
  2. while datastore is syncing models update some of the just created objects - eg. change value of property.
  3. after syncing is finished we see that locally and on backend in dynamo db it happens that the update was not applied
harsh62 commented 11 months ago

Our team is still investigating the issue. We will provide an update as soon as we have more information.

harsh62 commented 9 months ago

@fzy-spyro and @Umar009 A fix has been released in https://github.com/aws-amplify/amplify-swift/releases/tag/2.26.2 ..

github-actions[bot] commented 9 months ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.