@@ -371,7 +371,6 @@ SyncSession::SyncSession(Private, SyncClient& client, std::shared_ptr<DB> db, co
371371 , m_migration_store{sync::MigrationStore::create (m_db)}
372372 , m_client(client)
373373 , m_sync_manager(sync_manager)
374- , m_previous_schema_version(_impl::sync_schema_migration::has_pending_migration(*m_db->start_read ()))
375374{
376375 REALM_ASSERT (m_config.sync_config );
377376 // we don't want the following configs enabled during a client reset
@@ -633,12 +632,12 @@ void SyncSession::handle_fresh_realm_downloaded(DBRef db, Status status,
633632 revive_if_needed ();
634633}
635634
636- util::Future<void > SyncSession::Internal:: pause_async (SyncSession& session )
635+ util::Future<void > SyncSession::pause_async ()
637636{
638637 {
639- util::CheckedUniqueLock lock (session. m_state_mutex );
638+ util::CheckedUniqueLock lock (m_state_mutex);
640639 // Nothing to wait for if the session is already paused or inactive.
641- if (session. m_state == SyncSession::State::Paused || session. m_state == SyncSession::State::Inactive) {
640+ if (m_state == SyncSession::State::Paused || m_state == SyncSession::State::Inactive) {
642641 return util::Future<void >::make_ready ();
643642 }
644643 }
@@ -647,8 +646,8 @@ util::Future<void> SyncSession::Internal::pause_async(SyncSession& session)
647646 // must have been destroyed upon return. This allows the caller to follow up with a call to
648647 // sync::Client::notify_session_terminated() in order to be notified when the Realm file is closed. This works
649648 // so long as this SyncSession object remains in the `paused` state after the invocation of shutdown().
650- session. pause ();
651- return session. m_client .notify_session_terminated ();
649+ pause ();
650+ return m_client.notify_session_terminated ();
652651}
653652
654653void SyncSession::OnlyForTesting::handle_error (SyncSession& session, sync::SessionErrorInfo&& error)
@@ -658,7 +657,7 @@ void SyncSession::OnlyForTesting::handle_error(SyncSession& session, sync::Sessi
658657
659658util::Future<void > SyncSession::OnlyForTesting::pause_async (SyncSession& session)
660659{
661- return SyncSession::Internal:: pause_async (session );
660+ return session. pause_async ();
662661}
663662
664663// This method should only be called from within the error handler callback registered upon the underlying
@@ -1401,10 +1400,10 @@ void SyncSession::update_subscription_store(bool flx_sync_requested, std::option
14011400 // waiters
14021401 auto subscription_store = std::move (m_flx_subscription_store);
14031402 lock.unlock ();
1404- subscription_store->terminate ();
14051403 auto tr = m_db->start_write ();
1404+ subscription_store->reset (*tr);
14061405 history.set_write_validator_factory (nullptr );
1407- tr->rollback ();
1406+ tr->commit ();
14081407 }
14091408 return ;
14101409 }
@@ -1670,3 +1669,59 @@ util::Future<std::string> SyncSession::send_test_command(std::string body)
16701669
16711670 return m_session->send_test_command (std::move (body));
16721671}
1672+
1673+ void SyncSession::migrate_schema (util::UniqueFunction<void (Status)>&& callback)
1674+ {
1675+ util::CheckedUniqueLock lock (m_state_mutex);
1676+ // If the schema migration is already in progress, just wait to complete.
1677+ if (m_schema_migration_in_progress) {
1678+ add_completion_callback (std::move (callback), ProgressDirection::download);
1679+ return ;
1680+ }
1681+ m_schema_migration_in_progress = true ;
1682+
1683+ // Perform the migration:
1684+ // 1. Pause the sync session
1685+ // 2. Once the sync client releases the realm file:
1686+ // a. Delete all tables (private and public)
1687+ // b. Reset the subscription store
1688+ // d. Empty the sync history and adjust cursors
1689+ // e. Reset file ident (the server flags the old ident as in the case of a client reset)
1690+ // 3. Resume the session (the client asks for a new file ident)
1691+ // See `sync_schema_migration::perform_schema_migration` for more details.
1692+
1693+ CompletionCallbacks callbacks;
1694+ std::swap (m_completion_callbacks, callbacks);
1695+ auto guard = util::make_scope_exit ([&]() noexcept {
1696+ util::CheckedUniqueLock lock (m_state_mutex);
1697+ if (m_completion_callbacks.empty ())
1698+ std::swap (callbacks, m_completion_callbacks);
1699+ else
1700+ m_completion_callbacks.merge (std::move (callbacks));
1701+ });
1702+ m_state_mutex.unlock (lock);
1703+
1704+ auto future = pause_async ();
1705+ std::move (future).get_async (
1706+ [callback = std::move (callback), weak_session = weak_from_this ()](Status status) mutable {
1707+ if (!status.is_ok ())
1708+ return callback (status);
1709+
1710+ auto session = weak_session.lock ();
1711+ if (!session) {
1712+ status = Status (ErrorCodes::InvalidSession, " Sync session was destroyed during schema migration" );
1713+ return callback (status);
1714+ }
1715+ sync_schema_migration::perform_schema_migration (*session->m_db );
1716+ {
1717+ util::CheckedUniqueLock lock (session->m_state_mutex );
1718+ session->m_previous_schema_version .reset ();
1719+ session->m_schema_migration_in_progress = false ;
1720+ session->m_subscription_store_base .reset ();
1721+ session->m_flx_subscription_store .reset ();
1722+ }
1723+ session->update_subscription_store (true , {});
1724+ session->wait_for_download_completion (std::move (callback));
1725+ session->resume ();
1726+ });
1727+ }
0 commit comments