Skip to content

Commit

Permalink
Rework sync user handling and metadata storage
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tgoyne committed Jan 31, 2024
1 parent f7edd47 commit 2f32488
Show file tree
Hide file tree
Showing 44 changed files with 2,444 additions and 2,880 deletions.
32 changes: 16 additions & 16 deletions src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2789,6 +2789,12 @@ typedef enum realm_auth_provider {
RLM_AUTH_PROVIDER_API_KEY,
} realm_auth_provider_e;

typedef enum realm_sync_client_metadata_mode {
RLM_SYNC_CLIENT_METADATA_MODE_PLAINTEXT,
RLM_SYNC_CLIENT_METADATA_MODE_ENCRYPTED,
RLM_SYNC_CLIENT_METADATA_MODE_DISABLED,
} realm_sync_client_metadata_mode_e;

typedef struct realm_app_user_apikey {
realm_object_id_t id;
const char* key;
Expand Down Expand Up @@ -2897,6 +2903,12 @@ RLM_API void realm_app_config_set_framework_name(realm_app_config_t* config,
RLM_API void realm_app_config_set_framework_version(realm_app_config_t* config,
const char* framework_version) RLM_API_NOEXCEPT;
RLM_API void realm_app_config_set_bundle_id(realm_app_config_t* config, const char* bundle_id) RLM_API_NOEXCEPT;
RLM_API void realm_app_config_set_base_file_path(realm_app_config_t*, const char*) RLM_API_NOEXCEPT;
RLM_API void realm_app_config_set_metadata_mode(realm_app_config_t*,
realm_sync_client_metadata_mode_e) RLM_API_NOEXCEPT;
RLM_API void realm_app_config_set_metadata_encryption_key(realm_app_config_t*, const uint8_t[64]) RLM_API_NOEXCEPT;

RLM_API realm_sync_client_config_t* realm_app_config_get_sync_client_config(realm_app_config_t*) RLM_API_NOEXCEPT;

/**
* Get an existing @a realm_app_credentials_t and return it's json representation
Expand All @@ -2911,14 +2923,14 @@ RLM_API const char* realm_app_credentials_serialize_as_json(realm_app_credential
*
* @return A non-null pointer if no error occurred.
*/
RLM_API realm_app_t* realm_app_create(const realm_app_config_t*, const realm_sync_client_config_t*);
RLM_API realm_app_t* realm_app_create(const realm_app_config_t*);

/**
* Create cached realm_app_t* instance given a valid realm configuration and sync client configuration.
*
* @return A non-null pointer if no error occurred.
*/
RLM_API realm_app_t* realm_app_create_cached(const realm_app_config_t*, const realm_sync_client_config_t*);
RLM_API realm_app_t* realm_app_create_cached(const realm_app_config_t*);

/**
* Get a cached realm_app_t* instance given an app id. out_app may be null if the app with this id hasn't been
Expand Down Expand Up @@ -3048,11 +3060,10 @@ RLM_API bool realm_app_link_user(realm_app_t* app, realm_user_t* user, realm_app
* Switches the active user with the specified one. The user must exist in the list of all users who have logged into
* this application.
* @param app ptr to realm_app
* @param user ptr to current user
* @param new_user ptr to the new user to switch
* @param user ptr to user to set as current.
* @return True if no error has been recorded, False otherwise
*/
RLM_API bool realm_app_switch_user(realm_app_t* app, realm_user_t* user, realm_user_t** new_user);
RLM_API bool realm_app_switch_user(realm_app_t* app, realm_user_t* user);

/**
* Logs out and removes the provided user.
Expand Down Expand Up @@ -3365,12 +3376,6 @@ RLM_API realm_app_t* realm_user_get_app(const realm_user_t*) RLM_API_NOEXCEPT;


/* Sync */
typedef enum realm_sync_client_metadata_mode {
RLM_SYNC_CLIENT_METADATA_MODE_PLAINTEXT,
RLM_SYNC_CLIENT_METADATA_MODE_ENCRYPTED,
RLM_SYNC_CLIENT_METADATA_MODE_DISABLED,
} realm_sync_client_metadata_mode_e;

typedef enum realm_sync_client_reconnect_mode {
RLM_SYNC_CLIENT_RECONNECT_MODE_NORMAL,
RLM_SYNC_CLIENT_RECONNECT_MODE_TESTING,
Expand Down Expand Up @@ -3522,11 +3527,6 @@ typedef void (*realm_async_open_task_completion_func_t)(realm_userdata_t userdat
typedef void (*realm_async_open_task_init_subscription_func_t)(realm_t* realm, realm_userdata_t userdata);

RLM_API realm_sync_client_config_t* realm_sync_client_config_new(void) RLM_API_NOEXCEPT;
RLM_API void realm_sync_client_config_set_base_file_path(realm_sync_client_config_t*, const char*) RLM_API_NOEXCEPT;
RLM_API void realm_sync_client_config_set_metadata_mode(realm_sync_client_config_t*,
realm_sync_client_metadata_mode_e) RLM_API_NOEXCEPT;
RLM_API void realm_sync_client_config_set_metadata_encryption_key(realm_sync_client_config_t*,
const uint8_t[64]) RLM_API_NOEXCEPT;
RLM_API void realm_sync_client_config_set_reconnect_mode(realm_sync_client_config_t*,
realm_sync_client_reconnect_mode_e) RLM_API_NOEXCEPT;
RLM_API void realm_sync_client_config_set_multiplex_sessions(realm_sync_client_config_t*, bool) RLM_API_NOEXCEPT;
Expand Down
4 changes: 4 additions & 0 deletions src/realm/mixed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ class Mixed {
: Mixed(StringData(s))
{
}
Mixed(std::string_view s) noexcept
: Mixed(StringData(s))
{
}

DataType get_type() const noexcept
{
Expand Down
6 changes: 6 additions & 0 deletions src/realm/obj.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,12 @@ inline Obj& Obj::set(ColKey col_key, std::string str, bool is_default)
return set(col_key, StringData(str), is_default);
}

template <>
inline Obj& Obj::set(ColKey col_key, std::string_view str, bool is_default)
{
return set(col_key, StringData(str), is_default);
}

template <>
inline Obj& Obj::set(ColKey col_key, realm::null, bool is_default)
{
Expand Down
37 changes: 21 additions & 16 deletions src/realm/object-store/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,41 +92,46 @@ set(HEADERS
if(REALM_ENABLE_SYNC)
list(APPEND HEADERS
sync/app.hpp
sync/app_utils.hpp
sync/app_config.hpp
sync/app_credentials.hpp
sync/generic_network_transport.hpp
sync/async_open_task.hpp
sync/sync_manager.hpp
sync/sync_session.hpp
sync/sync_user.hpp
sync/app_service_client.hpp
sync/app_user.hpp
sync/app_utils.hpp
sync/async_open_task.hpp
sync/auth_request_client.hpp
sync/generic_network_transport.hpp
sync/mongo_client.hpp
sync/mongo_collection.hpp
sync/mongo_database.hpp
sync/push_client.hpp
sync/subscribable.hpp
sync/sync_manager.hpp
sync/sync_session.hpp
sync/sync_user.hpp
sync/user_provider.hpp

sync/impl/app_metadata.hpp
sync/impl/network_reachability.hpp
sync/impl/sync_client.hpp
sync/impl/sync_file.hpp
sync/impl/sync_metadata.hpp
sync/impl/network_reachability.hpp)
sync/impl/sync_file.hpp)

list(APPEND SOURCES
sync/app.cpp
sync/app_utils.cpp
sync/app_credentials.cpp
sync/generic_network_transport.cpp
sync/app_user.cpp
sync/app_utils.cpp
sync/async_open_task.cpp
sync/sync_manager.cpp
sync/sync_session.cpp
sync/sync_user.cpp
sync/generic_network_transport.cpp
sync/impl/app_metadata.cpp
sync/impl/sync_file.cpp
sync/mongo_client.cpp
sync/mongo_collection.cpp
sync/mongo_database.cpp
sync/push_client.cpp
sync/impl/sync_file.cpp
sync/impl/sync_metadata.cpp)
sync/sync_manager.cpp
sync/sync_session.cpp
sync/sync_user.cpp)

if(APPLE)
list(APPEND HEADERS
sync/impl/apple/network_reachability_observer.hpp
Expand Down
2 changes: 2 additions & 0 deletions src/realm/object-store/audit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ struct AuditConfig {
// in the server-side schema for AuditEvent. This is not validated and will
// result in a sync error if violated.
std::vector<std::pair<std::string, std::string>> metadata;
// Root directory to store audit Realms
std::string_view base_file_path;
};

class AuditInterface {
Expand Down
47 changes: 19 additions & 28 deletions src/realm/object-store/audit.mm
Original file line number Diff line number Diff line change
Expand Up @@ -622,20 +622,15 @@ bool write_event(Timestamp timestamp, StringData activity, StringData event_type

// Get a pool for the given sync user. Pools are cached internally to avoid
// creating duplicate ones.
static std::shared_ptr<AuditRealmPool> get_pool(std::shared_ptr<SyncUser> user,
std::string const& partition_prefix,
const std::shared_ptr<util::Logger>& logger,
ErrorHandler error_handler);
static std::shared_ptr<AuditRealmPool> get_pool(std::shared_ptr<SyncUser> user, const AuditConfig& config,
const std::shared_ptr<util::Logger>& logger);

// Write to a pooled Realm. The Transaction should not be retained outside
// of the callback.
void write(util::FunctionRef<void(Transaction&)> func) REQUIRES(!m_mutex);

explicit AuditRealmPool(Private, std::shared_ptr<SyncUser> user, std::string const& partition_prefix,
ErrorHandler error_handler, const std::shared_ptr<util::Logger>& logger,
std::string_view app_id);
AuditRealmPool(const AuditRealmPool&) = delete;
AuditRealmPool& operator=(const AuditRealmPool&) = delete;
explicit AuditRealmPool(Private, std::shared_ptr<SyncUser> user, const AuditConfig& config,
const std::shared_ptr<util::Logger>& logger);

// Block the calling thread until all pooled Realms have been fully uploaded,
// including ones which do not currently have sync sessions. For testing
Expand Down Expand Up @@ -663,13 +658,12 @@ explicit AuditRealmPool(Private, std::shared_ptr<SyncUser> user, std::string con
std::string prefixed_partition(std::string const& partition);
};

std::shared_ptr<AuditRealmPool> AuditRealmPool::get_pool(std::shared_ptr<SyncUser> user,
std::string const& partition_prefix,
const std::shared_ptr<util::Logger>& logger,
ErrorHandler error_handler) NO_THREAD_SAFETY_ANALYSIS
std::shared_ptr<AuditRealmPool>
AuditRealmPool::get_pool(std::shared_ptr<SyncUser> user, const AuditConfig& config,
const std::shared_ptr<util::Logger>& logger) NO_THREAD_SAFETY_ANALYSIS
{
struct CachedPool {
std::string user_identity;
std::string user_id;
std::string partition_prefix;
std::string app_id;
std::weak_ptr<AuditRealmPool> pool;
Expand All @@ -683,9 +677,9 @@ explicit AuditRealmPool(Private, std::shared_ptr<SyncUser> user, std::string con
}),
s_pools.end());

auto app_id = user->sync_manager()->app().lock()->config().app_id;
auto app_id = user->app_id();
auto it = std::find_if(s_pools.begin(), s_pools.end(), [&](auto& pool) {
return pool.user_identity == user->identity() && pool.partition_prefix == partition_prefix &&
return pool.user_id == user->user_id() && pool.partition_prefix == config.partition_value_prefix &&
pool.app_id == app_id;
});
if (it != s_pools.end()) {
Expand All @@ -694,28 +688,26 @@ explicit AuditRealmPool(Private, std::shared_ptr<SyncUser> user, std::string con
}
}

auto pool = std::make_shared<AuditRealmPool>(Private(), user, partition_prefix, error_handler, logger, app_id);
auto pool = std::make_shared<AuditRealmPool>(Private(), user, config, logger);
pool->scan_for_realms_to_upload();
s_pools.push_back({user->identity(), partition_prefix, app_id, pool});
s_pools.push_back({user->user_id(), config.partition_value_prefix, app_id, pool});
return pool;
}

AuditRealmPool::AuditRealmPool(Private, std::shared_ptr<SyncUser> user, std::string const& partition_prefix,
ErrorHandler error_handler, const std::shared_ptr<util::Logger>& logger,
std::string_view app_id)
AuditRealmPool::AuditRealmPool(Private, std::shared_ptr<SyncUser> user, const AuditConfig& config,
const std::shared_ptr<util::Logger>& logger)
: m_user(user)
, m_partition_prefix(partition_prefix)
, m_error_handler(error_handler)
, m_partition_prefix(config.partition_value_prefix)
, m_error_handler(config.sync_error_handler)
, m_path_root([&] {
auto base_file_path = m_user->sync_manager()->config().base_file_path;
#ifdef _WIN32 // Move to File?
const char separator[] = "\\";
#else
const char separator[] = "/";
#endif
// "$root/realm-audit/$appId/$userId/$partitonPrefix/"
return util::format("%2%1realm-audit%1%3%1%4%1%5%1", separator, base_file_path, app_id, m_user->identity(),
partition_prefix);
return util::format("%2%1realm-audit%1%3%1%4%1%5%1", separator, config.base_file_path, m_user->app_id(),
m_user->user_id(), config.partition_value_prefix);
}())
, m_logger(logger)
{
Expand Down Expand Up @@ -1016,8 +1008,7 @@ throw InvalidArgument("Auditing a flexible sync realm requires setting the audit
if (!m_serializer)
m_serializer = std::make_shared<AuditObjectSerializer>();

m_realm_pool = AuditRealmPool::get_pool(audit_user, audit_config.partition_value_prefix, m_logger,
audit_config.sync_error_handler);
m_realm_pool = AuditRealmPool::get_pool(audit_user, audit_config, m_logger);
}

void AuditContext::update_metadata(std::vector<std::pair<std::string, std::string>> new_metadata)
Expand Down
Loading

0 comments on commit 2f32488

Please sign in to comment.