Pause on metered connection
Relevant components
- [ ] Standalone tray application (based on Qt Widgets)
- [ ] Plasmoid/applet for Plasma desktop
- [ ] Dolphin integration
- [ ] Command line tool (
syncthingctl) - [ ] Integrated Syncthing instance (
libsyncthing) - [x] Android app or mobile UI in general
- [ ] Backend libraries
Platform
I need this for Android specifically, but I guess it applies to the tray app as well.
Feature description
I would like Syncthing Tray to automatically pause everything when switching to a metered connection and then resume it after.
Problem or use case
I am aware of the functionality to stop Syncthing when switching to a metered connection. Unfortunately, this option appears to vastly increase battery drain.
Whenever Syncthing is stopped, once it is restarted it will immediately scan all folders. If you have large folders, this can be a very expensive operation, especially on Android (hello SAF) which takes a significant amount of time, compute and hence battery. This is (in my case) exasperated by the fact that I often roam between WLAN networks, causing my phone to momentarily disconnect from one network (placing me on the metered mobile plan) only to connect to another. This seems to cause constant restarts.
Because Syncthing is actually fairly efficient when idle, I think that it would be more helpful to simply pause all synchronization when switching to a metered connection and then restore the pause state later, avoiding rescans.
Alternatives or workarounds
Alternative: Per-folder or per-device version of this functionality.
There is also this issue concerning similar functionality on the Syncthing project itself, but it doesn't look like it's going anywhere any time soon and Syncthing Tray already has the "metered connection" logic built in anyway.
Additional context
If there is interest in this, I could maybe take a stab at implementing it.
I think the gist of it would be to run the following steps upon switching to a metered network:
- Store currently paused devices
- Pause all not already paused devices
And on switching (back) to a non-metered network:
- Check whether there is a stored pause state
- If yes: restore it by resuming previously un-paused devices.
I suppose an alternative approach would be to pause the folders instead, as far as I know there should be no difference in the effect of pausing all files or all folders.
One disadvantage of this approach (or really any approach that does this through the Syncthing API) is that it doesn't prevent the web interface or some outside application from resuming a device while on a metered network. Also restoring would overwrite any changes to pause state since it was saved. For my use-case (and probably most?) this is not really an issue since I exclusively manage everything through Syncthing tray anyway.
This feature request makes sense and has already been implemented in Syncthing Tray on the desktop. So I guess it won't be much effort to re-use the existing code that can pause/restore devices like that. Maybe I can implement configuring this behavior in the Android app as well next week.
Note that there is also an open feature request for Syncthing itself for skipping the initial scan. That might also be helpful with this use case but we probably shouldn't wait until it is implemented.
Implemented via https://github.com/Martchus/syncthingtray/commit/488636d9d965138233e0031c9464559217ef3fa2.
I also created a development build (2.0.5-pre2) for Android (aarch64) which can be found here: https://martchus.dyn.f3l.de/shares/apks/
It worked in my tests under GNU/Linux and Android.
Thank you, I just tested 2.0.5-pre2 on my (aarch64) phone and it seems to work perfectly on my end too.
So I've been running this since, and have run into a couple issues:
- Sometimes all my devices appear to get stuck paused, even though I am connected to an unmetered network. I have been unable to reproduce it reliably, but FWIW it just happened when I connected my phone - which was previously only connected to a metered mobile connection - to ethernet via a dock. The folders were briefly unpaused and then paused again. The same thing happened again after I disconnected from ethernet and connected to WLAN. Upon connecting it to Ethernet again (without disconnecting WLAN) it the folders didn't unpause at all, not even briefly. So it seems the incorrect state was remembered at some point.
- Syncthing's battery consumption seems to have increased massively (by 20-30%). For comparison: I was previously on v2.0.4 and not using this new functionality. I have also been seeing strange disconnects from other devices that are located within the same network, which makes me wonder whether something is generating spurious changes to the "metered" state and causing pauses / unpauses.
Looking at Syncthing's own logs from the support bundle, there wasn't anything that stood out to me. I have now increased the log level to debug. Is there any other logging / diagnostics that I can turn on to help get to the bottom of this and would you prefer if I created another issue instead of continuing here?
Let's continue this here. I'm also not sure what logs would be useful because there's probably not much logging around this feature.
Maybe it misremembers the state because it isn't saved persistently. That's definitely something to be improved.
The battery drain could be indeed due to spurious changes in the metered state. The Syncthing logs should be able to tell whether devices were paused/resumed frequently.
So do I get it right that battery usage has increased between 20 - 30 % if you compare the case of using that feature and the case of not using it?
I'm just asking because if you're comparing against a different app that was still using Syncthing v1 then the reason might also be Syncthing v1 vs. Syncthing v2, see https://forum.syncthing.net/t/syncthing-fork-v2-uses-far-too-much-battery-over-v1.
By the way, that's how I deal with that: I usually start and stop the app as needed, preferably when the phone is charging anyway. To avoid doing expensive rescans too often I usually paused big folders (which luckily also happen to be folders that don't change very often anyway).
So I guess there are two things to improve here:
- Make remembering the state of paused devices persistent.
- Resume the devices only after a configurable delay to avoid going back-and-forth too quickly.
- Add/improve unit tests for the code that deals with pausing/resuming devices when the metered state changes.
- Add a button to pause/resume devices manually as if the connection was changing the metered state. This is not really required to solve this issue but will make integration testing much easier. (And I think Syncthing-Fork has a similar feature, too.)
Nothing of these points is difficult to implement but let's see when I'll find the time (as I don't really need it myself and also need to take the time to extend/improve the automatic tests of the mobile UI).
I am comparing the battery usage of v2.0.4 of this app (which obviously didn't have the feature) to what I was seeing on v2.0.5-pre2 with the feature enabled. Both times with the integrated Syncthing version that was shipped with the app. However I have not seen this kind of battery usage since, so maybe it was some one-off thing.
Getting stuck paused however has happened a lot. I have seen some rapid pause - unpause - pause - unpause... sequences in the log now, however I can't say whether they are actually spurious or not since I have no connection log from my phone to compare to. I think the delay and persistence would probably fix that issue regardless.
I can live with just unpausing things by hand for now, so there is no rush. Thank you for your work on this.
I had it now also running continuously for a couple of days with the option to pause devices if the connection is metered. In my tests I was always still at 0.2 to 0.3 % battery usage. I also never had it stuck in the paused state but maybe that's simply because I was lucky and the service never terminated while the connection/devices was/were metered/paused. So it definitely seems a bit nicer than completely starting/stopping the service and generally works.
@Martchus May I ask in what kind of environment have you been performing your tests? In my experience, just pausing devices doesn't help much if you are on the go (e.g. on mobile data with frequent tower switching, etc.), because simply maintaining connections to the discovery server, relays, etc. still drains a lot of battery.
For this reason, I've personally been running Syncthing through Termux, relying on some custom automation routines to suspend (but not exit) the Syncthing process, e.g. when on mobile data with screen off, and the resume it later.
May I ask in what kind of environment have you been performing your tests?
A Samsung Galaxy A23. I was using mainly Wfi but sometimes also 5G while also driving around. Not sure why it didn't drain my battery notably as relying and discovery are enabled. The only special setup I have is disabling the battery optimization from the OS to avoid having the service killed and restarted repeatedly.
Suspending Syncthing is actually also an interesting idea. ~~Maybe I can just call pthread_suspend_np/pthread_unsuspend_np on all threads of the Go runtime~~ (or send SIGTOP/SIGCONT to an externally started process). EDIT: Easier said then done; these functions are not portable and the Go runtime provided no function to enumerate its threads. I could also simply disable discovery/relaying.
How did catfriend1 implement his custom sync condition in I think Kotlin or native android java wrapper cmiiw, is it vastly different from Golang code?
I could think of QT, and others. But I think Flutter is supposed to be cross platform too. Is the category different, e.g., declarative vs functional programming code differences?
Edit: OP could also use logfox to debug and get log when syncthing fail.
I'm not sure what you're saying/asking.
As far as I know (but I haven't checked recently) the app from Catfriend doesn't solve the problem this ticket is concerned with. The app has runtime conditions (which are more advanced than in Syncthing Tray) but I think the only thing it can do if the runtime conditions aren't met is completely stopping Syncthing.
Note that discussing programming languages and frameworks here makes not much sense - especially mentioning Flutter/Dart. You cannot just throw in additional programming languages/frameworks to solve a problem in an established project. There's always friction when trying to mix languages/frameworks and here we already have enough of that. So I'll only say that I occasionally check how Catfriend app does things for certain features and in some cases use or take inspiration from its Java code (see legal section in README for details).
The OP could enable the persistent Syncthing log file in the settings and check that file after a while for frequent pausing/resuming of devices to test the hypothesis that it goes quickly back and forth between these states.
By the way, I have already implemented point 1. from https://github.com/Martchus/syncthingtray/issues/392#issuecomment-3633954705 and pausing discovery/relaying on a separate branch and will merge it to master if I have implemented at least point 3.
I've now pushed an implementation of the previously mentioned points 1. and 3. on master. It also pauses discovery and relaying now. I've also created a new Android build (see https://martchus.dyn.f3l.de/shares/apks/) which I also briefly tested. With this Syncthing was resumed automatically when the connection was no longer metered in my tests - even if the app was shutdown in the middle.
Thanks for the lengthy answers. Yeah, programming paradigm is tough. The term is dependency hecc, or DLL hecc in windows. Too many dependency, reminds me of npm package removal problem, that kind of shut down some part of the internet infrastructure.
The OP could enable the persistent Syncthing log file in the settings and check that file after a while for frequent pausing/resuming of devices to test the hypothesis that it goes quickly back and forth between these states.
I was seeing some of this previously, as I had mentioned.
I've now pushed an implementation of the previously mentioned points 1. and 3. on master. It also pauses discovery and relaying now. I've also created a new Android build (see https://martchus.dyn.f3l.de/shares/apks/) which I also briefly tested. With this Syncthing was resumed automatically when the connection was no longer metered in my tests - even if the app was shutdown in the middle.
I have been testing this since yesterday evening and have had no issues with it getting stuck (which used to happen a lot). So the problem is likely solved or at least significantly reduced. I think this issue can probably be closed again. Thanks again.