-
Notifications
You must be signed in to change notification settings - Fork 168
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
Rework sync user handling and metadata storage #7300
Conversation
5778fd4
to
07f0ac9
Compare
6e8a96c
to
519849b
Compare
d09e596
to
e31fc3b
Compare
c579d7b
to
faeb506
Compare
faeb506
to
7bb0c3c
Compare
e7b82a0
to
a381d9b
Compare
For reference realm/realm-swift@04af045 is the changes required to update Swift for the API changes here. |
94c7d43
to
9489195
Compare
std::string_view can already contain a null pointer and there's no reason to have a pointer to a string view.
We already have a more robust implementation of Uri splitting that should be used instead of a second implementation. Putting the redirect count on Request is more complicated than passing it directly, and testing that there are not unexpected additional requests made via checking the request count directly caught a bug which validating the redirect count did not. A few places used std::optional<std::string> where none and empty were treated identically, which means that it should be just an unwrapped string. The wrapper to heap-copy Requests did not do anything useful.
9489195
to
11a70a7
Compare
This introduces the beginning of a split between sync and app services. Object store Sync types (almost) don't depend on the `app` namespace, and can be used independently of it. The SyncUser type now implements only the functionality required internally for sync, and is backed by a UserProvider interface which allows it to request things like access token refreshes. All user management has been removed from SyncManager, and it now only owns the sync client and active sync sessions. SyncSession is mostly unchanged. `app::User` is the new equivalent of the old SyncUser type. The user management which used to be in SyncManager is now in App, which implements the UserProvider interface. Metadata storage for sync and App has been completely redesigned. The metadata store is no longer optional, and instead has an in-memory implementation that should work identically to the persistent store other than not being persistent. The interface has been reworked to enable atomic updates to the metadata store rather than relying on the filesystem mutex in SyncManager, which will be required for multiprocess sync. This required pushing significantly more logic into the metadata storage, which fortunately turned out to also simplify things in the process. The ownership relationship between `App` and `User` has been inverted, with `App` now holding a weak cache of users and `User` strongly retaining the `App`. This ensures that a `SyncConfig` now retains everything it depends on even when app caching is not used. Co-authored-by: James Stone <[email protected]>
We sometimes need to retry requests after doing things like fetching a new access token, so the response handlers need access to the request. This requires either copying the request or heap allocating it, and as the request body can be quite large heap allocating it is better. Previously we created them on the stack and then moved them to the heap, but as every request goes through the path where they're moved to the heap they should just start there.
11a70a7
to
84a9d03
Compare
// ------------------------------------------------------------------------ | ||
// SyncUser implementation | ||
|
||
std::string user_id() const noexcept override; |
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.
Why not simply id
... it's already namespaced by User
?
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.
Most of the places where user_id is used isn't in User.
This makes a bunch of changes required by both the multiprocess sync and app services split projects because they sort of collided and had to change the same types in conflicting ways.
For the app services split, the core idea is that SyncUser is now an abstract virtual class which has just the interface required by the sync components. SyncManager is no longer involved in user management, but is now responsible for session management. When using sync without the core app services implementation, the entire
app
namespace will be disabled. The SDK will implement SyncUser to expose the SDK's users to core/sync, and instantiate a SyncManager directly.The user management functionaly that used to be split between SyncManager and App is now entirely on App. App vends
app::User
instances, which implement the newSyncUser
interface and are roughly a drop-in replacement for the oldSyncUser
type. App continues to instantiate and own a SyncManager. Unlike before, App's cache of users is now a weak one, and insteadapp::User
instances retain the parentApp
, which makes it so that aSyncConfig
remains valid without something else keeping the App alive.To support sharing users between processes, the sync metadata store has been entirely rewritten and renamed to the app metadata store to reflect that it's now part of App rather than SyncManager.
Unlike the old SyncUser, app::User is never the source of truth for the state of a user, and instead just holds a cached copy of the last-read user data. Instead there is a unidirectional data flow where all updates are made by writing to the metadata store, which then publishes updates to all live app::User instances. This simplifies the implementation and will make it so that writes to the metadata realm can update users in other processes via normal Realm change notifications. This is not yet fully implemented in this PR.
The metadata store is no longer optional, and instead there are two implementations of it: the persisted store which is backed by a Realm file, and a basic in-memory store suitable for running most tests. The in-memory store should be indistinguishable from the persisted store in any scenario that doesn't involve multiple App instances, unlike the old no metadata mode. The persisted store does not yet have the actual multi-process functionality, but it does make atomic updates to the metadata realm, unlike the old implementation which would split some updates over multiple write transactions. This may fix some bugs resulting from crashing at exactly the incorrect time. The new persisted store is also more consistent about how logged-out users are handled (they are now always in
App::all_users()
, rather than only until the next launch of the process) and fixes some problems around cleaning up removed users.