-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add "on backgrounded" function #611
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it quite hard to follow the logic here, and the overall approach doesn't "feel" quite right (sorry to be vague, maybe talking it through will help me get clear on specifics). I think what autostop is doing is stopping sync after a fixed period of time, rather than unreplicating cores when sync has finished, but I'm not sure because I don't completely understand the logic. I think it would be worth co-working on this a bit more.
Simplified this a bunch (I think). When the app is in the background and sync is complete, cores will be un-replicated. There is no longer an auto-stop timeout, but it should be easy to add in the future. There are now two booleans:
After these operations and when the state is updated, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work on this, the logic looks correct to me, but there is one place in particular where it seems very fragile around management of the syncEnabledState
- it should work as-is, but only because of nuances of Map iteration order and how we read it. I think we should fix this before merging.
Summary of other comments:
- We need to define the
state.initial.syncing
now, because currently it defaults totrue
always. - There are a few naming things that might help future maintainers understand things.
- Finally I think it's not great to have duplicate logic for deriving syncEnabledState in both the
#getState
and#updateState
methods.
src/sync/sync-api.js
Outdated
state.data.syncing = | ||
this.#wantsToSyncData && | ||
(!this.#isBackgrounded || | ||
!isSynced(namespaceSyncState, 'full', this.#peerSyncControllers)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably makes sense to rename state['data' | 'initial'].syncing
to state['data' | 'initial'].syncEnabled
to be clear about what this means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is essentially calculating the value of syncEnabledState
based on the same logic that is in #updateState()
, and we need to remember to keep those two pieces of logic in sync. Hoisting syncEnabledState to somewhere that can be read here would avoid having this duplication.
For completeness we should also set state.initial.syncing
here, because currently it just defaults to always be true
, but now it can be false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored in 8fe34aa.
src/sync/sync-api.js
Outdated
@@ -50,7 +56,8 @@ export class SyncApi extends TypedEmitter { | |||
#pscByPeerId = new Map() | |||
/** @type {Set<string>} */ | |||
#peerIds = new Set() | |||
#isSyncing = false | |||
#wantsToSyncData = false | |||
#isBackgrounded = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this naming (isBackgrounded
) is confusing, because it doesn't describe what this does, it describes a state that only really makes sense on mobile, and it's behaviour is determined by platform specifics of how we want sync to behave when in the background. Understanding what it does requires reading the code, or understanding that we want sync to gracefully stop when complete when the app is in the background. I'm thinking ahead to when we integrate this into Desktop and Mapeo Cloud server. This is why in my hacking around yesterday I was moving towards the concept of starting and stopping both sync types (initial & data), and being more explicit about auto-starting initial sync on initialization (e.g. starting initial sync in the constructor).
I'm not great at names, but perhaps we can call this either wantsToStopAllSync
or the inverse wantsToSyncInitial
or wantsToSyncPresync
. Then perhaps the methods can be kStopAllSyncGracefully
and kResumeSync
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed in 4a641e7.
src/sync/sync-api.js
Outdated
for (const peerSyncController of this.#peerSyncControllers.values()) { | ||
isStopped = peerSyncController.syncEnabledState === 'none' | ||
break | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it starts to get quite confusing storing this state on the peer sync controllers, since a future maintainer could without understanding set a peer controller sync enabled state somewhere, so that this state is different for some PSCs, which would break this. There is also kind of vague behaviour around when PSC instances are created with syncEnabledState === 'none', before it is set from this class, because this class reads that state as the source of truth while also setting that state. It's kind of circular...
I suggest that the syncEnabled state is hoisted, either to this class or to the SyncState class, which is already passed to the PSC instances, so they could read syncState.syncEnabledState from there. That would also avoid the not-quite-determined behaviour when PSC instances are created, because they will read the shared value from SyncState, rather than having their own state until it starts being controlled by this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I'm wanting to avoid here is implicit context knowledge in the codebase, e.g. "you must set the syncEnabledState of all PSCs at the same time, but you can't entirely rely on any one PSC's syncEnabledState to be accurate, so we should re-set syncEnabledState each time we update".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in ba539cf. (I should've left a PR comment saying that I didn't like the way I addressed this.)
Co-authored-by: Gregor MacLennan <[email protected]>
This test-only change drops Brittle from our basic manager tests and replaces them with `node:test` and `node:assert`. Two notable things about this change: 1. There was a test that was commented out because of a Brittle bug. This uncomments it and sets the `skip: true` option. 2. Brittle has an ([undocumented][0]) `t.snapshot()` method which we can't use. I replaced it with a custom snapshot assertion. The rest was just mechanical changes. [0]: holepunchto/brittle#45
Co-authored-by: Evan Hahn <[email protected]>
When you leave a project, we now delete all indexed data outside of the `auth` namespace. Fixes [#427]. [#427]: #427 Co-Authored-By: Andrew Chou <[email protected]>
Thanks for all the review here. Hopefully this is the last round! |
src/sync/sync-api.js
Outdated
return state | ||
} | ||
|
||
#updateState = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add namespace sync state as an optional parameter, to save recalculating it on state events. It's expensive to calculate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in db4ff6c.
MapeoManager#onBackgrounded()
should be called when the app goes into the background. It will do its best to gracefully shut down sync.Closes #576.