amplify-flutter
amplify-flutter copied to clipboard
DataStore stops sync randomly
Description
I have met a huge issue that Amplify Datastore stops sync randomly and un-predictively in several different cases
My code handle the Datastore hublistener as below
try {
List<String> listhubname = ['modelsynced', 'ready'];
hubstream = Amplify.Hub.listen(HubChannel.DataStore, (hubEvent) async {
int i = listhubname.indexWhere((hubtype) => hubEvent.eventName.toLowerCase().contains(hubtype));
if (i != -1) {
if (mounted) {
setState(() {
appSyncReady = true;
});
}
});
}).whenComplete(() {
if (hubstream != null) {
throwDummy();
}
});
} on HubChannel catch (e) {
debugPrint('ERROR main.dart listenToHub error: $e');
}
This code above I put right after Amplify Configured, on main.dart
I don't run Amplify.Datastore.start()
as throwing Dummy. Amplify.Datastore.save(dummy)
When user log in with different ID compared the last time log in ID, same device, I run Amplify.Datastore.clear()
first, then run Amplify.Datastore.start()
again. After that, it get trigger from listener above so that network is ready, allowing different user can log in. With this said, I don't Amplify.Datastore.clear()
after log out
Otherwise, there's no calling clear at any point
When users start app, it called main run, listener run as above, working fine When users resume app, after a while, it triggers listener and network gets ready, working fine (only on release mode, on dev mode, it won't trigger) When users resume app, after 1 2s from inactive app, listener won't be trigger, nothing happens When app is inactive, I remove subscriptions, when app is resumed, add subscriptions and run fetch functions whenever network is ready, confirmed from listeners (is this good practice?, or keep subscriptions even app inactive?)
Here is the issue A small group of users (Testflight and APK app)
- lost Datastore sync, when resume app after a while, even restart app as well. User log out and sign in several times, issue's still the same, but after a while no touching with it, it just gets back Sync with no reason
- Sometimes, after resume, it must wait like 5-10 s to get network ready, but users when open the app, want to use immediately
- The issue happened randomly and un-predictively, which causes really bad UX it happened with Android more than iOS
- Sometimes, user open app and all content is gone, like there's no data at all, all images and text can not be fetched. and I think this is from DataStore stops sync
From Dev side, I always checked Amplify.push
after submitting new Testflight and APK version and Deploy Data
What is the best practice to handle Datastore sync?
Categories
- [ ] Analytics
- [ ] API (REST)
- [X] API (GraphQL)
- [ ] Auth
- [ ] Authenticator
- [X] DataStore
- [ ] Notifications (Push)
- [ ] Storage
Steps to Reproduce
No specific step to re-produce this issue But mostly happen when resuming app
Screenshots
No response
Platforms
- [X] iOS
- [X] Android
- [ ] Web
- [ ] macOS
- [ ] Windows
- [ ] Linux
Flutter Version
3.16.1
Amplify Flutter Version
12.9.0
Deployment Method
Amplify CLI
Schema
No response
Hi @harrynguyen2510, sorry to hear you've ran into an issue with DataStore.
I'm having a hard time following the issue, let me summarize what I think is going on and you correct me if I'm off.
App starts fine and DataStore works properly.
However, when a user minimizes the app by locking the screen, going to the home page, etc.
You listen to the app life cycle events to unsubscribe (stop DataStore?) on background events and resubscribe (start DataStore?) on resume and fetch missing data. (To answer your question, this is recommended process when working with subscriptions)
Then the issue is DataStore is taking a long time or not syncing at all when the app is resumed.
Is this correct so far?
If so, I have a couple questions:
- Do you see the issue on debug or release builds? Likewise do you see this issue on physical devices or simulator? Both?
- When all data is gone, are there any logs (hub events) or errors? Can you provide those?
- Can you clairfy how you start DataStore sync? Are you sending a dummy save event?
- Can you provide the code where you unsubscribe/subscribe on pause and resume?
- How does logging in and out effect the behavior? Does it cause the issue?
@Equartey thanks for your reply
- no issue on debug and release builds, only on Testflight or APK builds
- How can I get these logs ?
- Yes I send dummy save event as I think it will automatically start DataStore sync
- When app is inactive, I cancel all streams, and put back when it resumes, this is example of Tabcontrollerpage.dart, this logic is applied for other several pages as well
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
debugPrint('Active on APP');
provider.setAppActive(currentUser);
break;
case AppLifecycleState.inactive:
debugPrint('InActive on APP');
setCurrentUserAppInActive(currentUser);
provider.pageStreamCancel();
break;
case AppLifecycleState.paused:
case AppLifecycleState.detached:
debugPrint('InActive on APP');
setCurrentUserAppInActive(currentUser);
provider.pageStreamCancel();
break;
case AppLifecycleState.hidden:
debugPrint('InActive on APP');
setCurrentUserAppInActive(currentUser);
provider.pageStreamCancel();
break;
}
void setAppActive(currentUser) {
appJustActive = true;
NotificationCenter().subscribe('NEConfirmDatastoreHubReady-tabpage', () {
if (appJustActive) {
appJustActive = false;
resumeTask(currentUser);
}
});
}
NotificationCenter
will be notified locally from Main.dart when sync get ready
resume Task includes add back subscription as changeOnObject = Amplify.Datastore.observe (...)
and fetch queries like Amplify.Datastore.query(...)
cancel stream includes like changeOnObject?.cancel();
Do I need to call Amplify.Datastore.stop()
when app is inactive, and Amplify.Datastore.start()
when app resumes as well?. If so, I remember that Datastore start may take a few seconds to complete start, but users want to use immediately when resumes?
- Not sure how log in and out effect this issue, but as I mentioned before, I don't run
Amplify.Datastore.clear()
when user log out, because in practices, after they logout, they want to log in immediately. If runningAmplify.Datastore.clear()
after log out, it will take a while (Android is longer than iOS) to start back the sync. So to keep better UX, I only callAmplify.Datastore.clear()
when they log in with different account on same device However, the main issue only happens when resume, and pause.
@Equartey does this one relate to the issue?
[ERROR:flutter/shell/common/shell.cc(1015)] The 'com.amazonaws.amplify/datastore_observe_events' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.
Hi @harrynguyen2510
Regarding (1) no issue on debug/release builds, do you mean you cannot reproduce this issue on those builds or that you haven't tried testing there yet? If you were to deploy your app directly from your computer to device, are you able to replicate as well?
Regarding (2) logs of physical devices - assuming you have a Mac laptop you can use the "Console" application to view logs of iPhone device and "LogRabbit" application to view logs of Android device connected to your Mac computer.
@fjnoyp Hi (1) yes there's no issue at debug/release builds, only on Testflight and APK Android (sometimes happen, not always) (2) Thanks I did and console shows over than 16,000 messages, any keyword that we're looking for? when running debug mode, vscode debug consoles shows
[ERROR:flutter/shell/common/shell.cc(1015)] The 'com.amazonaws.amplify/datastore_observe_events' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.
not sure if this is the cause of issue
@Equartey has there been any updates with this out of interest?
@Equartey I put Amplify.Datastore.start() on app resume, make it got stuck you only need to put it after you setting up listen Datastore hub to verify network is Up, otherwise, no need to call start when app resume
I discovered a solution to this issue, although it seems a bit perplexing. In my case, the problem arises whenever the app is in the background.
A workaround is to call await Amplify.configure(amplifyconfig)
again before invoking any DataStore function.
However, this led to another issue where it says "Amplify is already configured" (because it's being called multiple times when the app is in the background to store some sites
). To address this, I added an isConfigured
check to ensure Amplify is only configured when it unexpectedly loses its configuration.
await AmplifyHelper.inject();
await AmplifyDB.addSite(jsonSite);
class AmplifyHelper {
static Future<void> inject() async {
if (Amplify.isConfigured) return debugPrint('Amplify already configured');
try {
final auth = AmplifyAuthCognito();
final datastore = AmplifyDataStore(modelProvider: ModelProvider.instance);
await Amplify.addPlugins([auth, datastore]);
// call Amplify.configure to use the initialized categories in your app
await Amplify.configure(amplifyconfig);
safePrint('Successfully configured Amplify');
} on Exception catch (e) {
safePrint('An error occurred configuring Amplify: $e');
}
}
}