From e8b52d0a31712d315903d1b3541fa08e21f156d4 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 17 Feb 2023 17:26:39 -0500 Subject: [PATCH 01/17] refactor bind and consider user_id/sync server mode part of server endpoint --- src/realm/sync/client.cpp | 100 +++--- src/realm/sync/client.hpp | 29 +- src/realm/sync/noinst/client_impl_base.cpp | 17 +- src/realm/sync/noinst/client_impl_base.hpp | 41 ++- src/realm/sync/noinst/server/server.cpp | 4 + test/benchmark-sync/bench_transform.cpp | 24 +- test/sync_fixtures.hpp | 58 ++-- test/test_client_reset.cpp | 100 +++--- test/test_sync.cpp | 350 ++++++++++++--------- 9 files changed, 394 insertions(+), 329 deletions(-) diff --git a/src/realm/sync/client.cpp b/src/realm/sync/client.cpp index 7949eb5a523..3f3df70308f 100644 --- a/src/realm/sync/client.cpp +++ b/src/realm/sync/client.cpp @@ -189,8 +189,6 @@ class SessionWrapper final : public util::AtomicRefCountBase, public SyncTransac void set_connection_state_change_listener(util::UniqueFunction); void initiate(); - void initiate(ProtocolEnvelope, std::string server_address, port_type server_port, std::string virt_path, - std::string signed_access_token); void force_close(); @@ -219,6 +217,8 @@ class SessionWrapper final : public util::AtomicRefCountBase, public SyncTransac void handle_pending_client_reset_acknowledgement(); + std::string get_appservices_connection_id(); + private: ClientImpl& m_client; DBRef m_db; @@ -227,6 +227,8 @@ class SessionWrapper final : public util::AtomicRefCountBase, public SyncTransac const ProtocolEnvelope m_protocol_envelope; const std::string m_server_address; const port_type m_server_port; + const std::string m_user_id; + const SyncServerMode m_sync_mode; const std::string m_authorization_header_name; const std::map m_custom_http_headers; const bool m_verify_servers_ssl_certificate; @@ -614,12 +616,13 @@ void ClientImpl::actualize_and_finalize_session_wrappers() } -ClientImpl::Connection& -ClientImpl::get_connection(ServerEndpoint endpoint, const std::string& authorization_header_name, - const std::map& custom_http_headers, - bool verify_servers_ssl_certificate, Optional ssl_trust_certificate_path, - std::function ssl_verify_callback, - Optional proxy_config, SyncServerMode sync_mode, bool& was_created) +ClientImpl::Connection& ClientImpl::get_connection(ServerEndpoint endpoint, + const std::string& authorization_header_name, + const std::map& custom_http_headers, + bool verify_servers_ssl_certificate, + Optional ssl_trust_certificate_path, + std::function ssl_verify_callback, + Optional proxy_config, bool& was_created) { ServerSlot& server_slot = m_server_slots[endpoint]; // Throws @@ -636,8 +639,7 @@ ClientImpl::get_connection(ServerEndpoint endpoint, const std::string& authoriza std::unique_ptr conn_2 = std::make_unique( *this, ident, std::move(endpoint), authorization_header_name, custom_http_headers, verify_servers_ssl_certificate, std::move(ssl_trust_certificate_path), std::move(ssl_verify_callback), - std::move(proxy_config), server_slot.reconnect_info, - sync_mode); // Throws + std::move(proxy_config), server_slot.reconnect_info); // Throws ClientImpl::Connection& conn = *conn_2; if (!m_one_connection_per_session) { server_slot.connection = std::move(conn_2); @@ -1074,6 +1076,8 @@ SessionWrapper::SessionWrapper(ClientImpl& client, DBRef db, std::shared_ptrclaim_sync_agent(); - - SyncServerMode sync_mode = m_flx_subscription_store ? SyncServerMode::FLX : SyncServerMode::PBS; + auto sync_mode = endpoint.server_mode; bool was_created = false; ClientImpl::Connection& conn = m_client.get_connection( std::move(endpoint), m_authorization_header_name, m_custom_http_headers, m_verify_servers_ssl_certificate, - m_ssl_trust_certificate_path, m_ssl_verify_callback, m_proxy_config, sync_mode, + m_ssl_trust_certificate_path, m_ssl_verify_callback, m_proxy_config, was_created); // Throws try { // FIXME: This only makes sense when each session uses a separate connection. @@ -1592,7 +1581,7 @@ inline void SessionWrapper::report_sync_transact(VersionID old_version, VersionI void SessionWrapper::do_initiate(ProtocolEnvelope protocol, std::string server_address, port_type server_port) { REALM_ASSERT(!m_initiated); - ServerEndpoint server_endpoint{protocol, std::move(server_address), server_port}; + ServerEndpoint server_endpoint{protocol, std::move(server_address), server_port, m_user_id, m_sync_mode}; m_client.register_unactualized_session_wrapper(this, std::move(server_endpoint)); // Throws m_initiated = true; } @@ -1776,6 +1765,28 @@ void SessionWrapper::handle_pending_client_reset_acknowledgement() }); } +std::string SessionWrapper::get_appservices_connection_id() +{ + auto pf = util::make_promise_future(); + REALM_ASSERT(m_initiated); + + util::bind_ptr self(this); + get_client().post([self, promise = std::move(pf.promise)](Status status) mutable { + if (!status.is_ok()) { + promise.set_error(status); + return; + } + + if (!self->m_sess) { + promise.set_error({ErrorCodes::RuntimeError, "session already finalized"}); + } + + promise.emplace_value(self->m_sess->get_connection().get_active_appservices_connection_id()); + }); + + return pf.future.get(); +} + // ################ ClientImpl::Connection ################ ClientImpl::Connection::Connection(ClientImpl& client, connection_ident_type ident, ServerEndpoint endpoint, @@ -1784,20 +1795,15 @@ ClientImpl::Connection::Connection(ClientImpl& client, connection_ident_type ide bool verify_servers_ssl_certificate, Optional ssl_trust_certificate_path, std::function ssl_verify_callback, - Optional proxy_config, ReconnectInfo reconnect_info, - SyncServerMode sync_mode) + Optional proxy_config, ReconnectInfo reconnect_info) : logger_ptr{std::make_shared(make_logger_prefix(ident), client.logger_ptr)} // Throws , logger{*logger_ptr} , m_client{client} - , m_protocol_envelope{std::get<0>(endpoint)} - , m_address{std::get<1>(endpoint)} - , m_port{std::get<2>(endpoint)} , m_verify_servers_ssl_certificate{verify_servers_ssl_certificate} // DEPRECATED , m_ssl_trust_certificate_path{std::move(ssl_trust_certificate_path)} // DEPRECATED , m_ssl_verify_callback{std::move(ssl_verify_callback)} // DEPRECATED , m_proxy_config{std::move(proxy_config)} // DEPRECATED , m_reconnect_info{reconnect_info} - , m_sync_mode(sync_mode) , m_ident{ident} , m_server_endpoint{std::move(endpoint)} , m_authorization_header_name{authorization_header_name} // DEPRECATED @@ -1972,27 +1978,6 @@ void Session::bind() } -void Session::bind(std::string server_url, std::string signed_access_token) -{ - ClientImpl& client = m_impl->get_client(); - ProtocolEnvelope protocol = {}; - std::string address; - port_type port = {}; - std::string path; - if (!client.decompose_server_url(server_url, protocol, address, port, path)) // Throws - throw BadServerUrl(); - bind(std::move(address), std::move(path), std::move(signed_access_token), port, protocol); // Throws -} - - -void Session::bind(std::string server_address, std::string realm_identifier, std::string signed_access_token, - port_type server_port, ProtocolEnvelope protocol) -{ - m_impl->initiate(protocol, std::move(server_address), server_port, std::move(realm_identifier), - std::move(signed_access_token)); // Throws -} - - void Session::nonsync_transact_notify(version_type new_version) { m_impl->nonsync_transact_notify(new_version); // Throws @@ -2048,6 +2033,11 @@ util::Future Session::send_test_command(std::string body) return m_impl->send_test_command(std::move(body)); } +std::string Session::get_appservices_connection_id() +{ + return m_impl->get_appservices_connection_id(); +} + const std::error_category& client_error_category() noexcept { return g_error_category; diff --git a/src/realm/sync/client.hpp b/src/realm/sync/client.hpp index 2cf532e8956..4679fb2c751 100644 --- a/src/realm/sync/client.hpp +++ b/src/realm/sync/client.hpp @@ -202,6 +202,11 @@ class Session { /// to file system paths, and thus, these restrictions do not apply. std::string realm_identifier = ""; + /// The user id of the logged in user for this sync session. This will be used + /// along with the server_address/server_port/protocol_envelope to determine + /// which connection to the server this session will use. + std::string user_id; + /// The protocol used for communicating with the server. See /// ProtocolEnvelope. ProtocolEnvelope protocol_envelope = ProtocolEnvelope::realm; @@ -553,23 +558,7 @@ class Session { /// /// The two other forms of bind() are convenience functions. void bind(); - /// \brief parses parameters and replaces the parameters in the Session::Config object - /// before the session is bound. - /// \param server_url For example "realm://sync.realm.io/test". See - /// server_address, server_path, and server_port in Session::Config for - /// information about the individual components of the URL. See - /// ProtocolEnvelope for the list of available URL schemes and the - /// associated default ports. - /// - /// \throw BadServerUrl if the specified server URL is malformed. - void bind(std::string server_url, std::string signed_user_token); - /// void bind(std::string server_address, std::string server_path, - /// std::string signed_user_token, port_type server_port = 0, - /// ProtocolEnvelope protocol = ProtocolEnvelope::realm); - /// replaces the corresponding parameters from the Session::Config object - /// before the session is bound. - void bind(std::string server_address, std::string server_path, std::string signed_user_token, - port_type server_port = 0, ProtocolEnvelope protocol = ProtocolEnvelope::realm); + /// @} /// \brief Refresh the access token associated with this session. @@ -739,6 +728,12 @@ class Session { util::Future send_test_command(std::string command_body); + /// Returns the app services connection id if the session is connected, otherwise + /// returns an empty string. This function blocks until the value is set from + /// the event loop thread. If an error occurs, this will throw an ExceptionForStatus + /// with the error. + std::string get_appservices_connection_id(); + private: SessionWrapper* m_impl = nullptr; diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index 9acae144c09..83510206898 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -841,11 +841,11 @@ void Connection::initiate_reconnect() m_websocket = m_client.m_socket_provider->connect(std::make_unique(this), WebSocketEndpoint{ - m_address, - m_port, + m_server_endpoint.address, + m_server_endpoint.port, get_http_request_path(), std::move(sec_websocket_protocol), - is_ssl(m_protocol_envelope), + is_ssl(m_server_endpoint.envelope), /// DEPRECATED - The following will be removed in a future release {m_custom_http_headers.begin(), m_custom_http_headers.end()}, m_verify_servers_ssl_certificate, @@ -1299,7 +1299,7 @@ void Connection::disconnect(const SessionErrorInfo& info) bool Connection::is_flx_sync_connection() const noexcept { - return m_sync_mode != SyncServerMode::PBS; + return m_server_endpoint.server_mode != SyncServerMode::PBS; } void Connection::receive_pong(milliseconds_type timestamp) @@ -1547,6 +1547,15 @@ void Connection::enlist_to_send(Session* sess) } +std::string Connection::get_active_appservices_connection_id() +{ + if (!m_websocket) { + return {}; + } + + return std::string{m_websocket->get_appservices_request_id()}; +} + void Session::cancel_resumption_delay() { REALM_ASSERT(m_state == Active); diff --git a/src/realm/sync/noinst/client_impl_base.hpp b/src/realm/sync/noinst/client_impl_base.hpp index 0d17f033b4d..4f5c20eec93 100644 --- a/src/realm/sync/noinst/client_impl_base.hpp +++ b/src/realm/sync/noinst/client_impl_base.hpp @@ -35,7 +35,32 @@ namespace sync { // // `protocol` is included for convenience, even though it is not strictly part // of an endpoint. -using ServerEndpoint = std::tuple; + +struct ServerEndpoint { + ProtocolEnvelope envelope; + std::string address; + network::Endpoint::port_type port; + std::string user_id; + SyncServerMode server_mode = SyncServerMode::PBS; + +private: + auto to_tuple() const + { + return std::make_tuple(server_mode, envelope, std::ref(address), port, std::ref(user_id)); + } + +public: + friend inline bool operator==(const ServerEndpoint& lhs, const ServerEndpoint& rhs) + { + return lhs.to_tuple() == rhs.to_tuple(); + } + + + friend inline bool operator<(const ServerEndpoint& lhs, const ServerEndpoint& rhs) + { + return lhs.to_tuple() < rhs.to_tuple(); + } +}; class SessionWrapper; @@ -269,8 +294,7 @@ class ClientImpl { bool verify_servers_ssl_certificate, util::Optional ssl_trust_certificate_path, std::function, - util::Optional, SyncServerMode, - bool& was_created); + util::Optional, bool& was_created); // Destroys the specified connection. void remove_connection(ClientImpl::Connection&) noexcept; @@ -415,10 +439,12 @@ class ClientImpl::Connection { void resume_active_sessions(); + std::string get_active_appservices_connection_id(); + Connection(ClientImpl&, connection_ident_type, ServerEndpoint, const std::string& authorization_header_name, const std::map& custom_http_headers, bool verify_servers_ssl_certificate, util::Optional ssl_trust_certificate_path, std::function, - util::Optional, ReconnectInfo, SyncServerMode); + util::Optional, ReconnectInfo); ~Connection(); @@ -517,9 +543,6 @@ class ClientImpl::Connection { ClientImpl& m_client; util::bind_ptr m_websocket_sentinel; std::unique_ptr m_websocket; - const ProtocolEnvelope m_protocol_envelope; - const std::string m_address; - const port_type m_port; /// DEPRECATED - These will be removed in a future release const bool m_verify_servers_ssl_certificate; @@ -529,8 +552,6 @@ class ClientImpl::Connection { ReconnectInfo m_reconnect_info; int m_negotiated_protocol_version = 0; - SyncServerMode m_sync_mode = SyncServerMode::PBS; - bool m_is_flx_sync_connection = false; ConnectionState m_state = ConnectionState::disconnected; @@ -1197,7 +1218,7 @@ inline ConnectionState ClientImpl::Connection::get_state() const noexcept inline SyncServerMode ClientImpl::Connection::get_sync_server_mode() const noexcept { - return m_sync_mode; + return m_server_endpoint.server_mode; } inline auto ClientImpl::Connection::get_reconnect_info() const noexcept -> ReconnectInfo diff --git a/src/realm/sync/noinst/server/server.cpp b/src/realm/sync/noinst/server/server.cpp index e8512dfe46c..4dea3d04fd3 100644 --- a/src/realm/sync/noinst/server/server.cpp +++ b/src/realm/sync/noinst/server/server.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -1578,6 +1579,7 @@ class HTTPConnection { private: ServerImpl& m_server; const int_fast64_t m_id; + const ObjectId m_appservices_request_id = ObjectId::gen(); std::unique_ptr m_socket; std::unique_ptr m_ssl_stream; std::unique_ptr m_read_ahead_buffer; @@ -1920,6 +1922,8 @@ class HTTPConnection { void add_common_http_response_headers(HTTPResponse& response) { response.headers["Server"] = "RealmSync/" REALM_VERSION_STRING; // Throws + // This isn't a real X-Appservices-Request-Id, but it should + response.headers["X-Appservices-Request-Id"] = m_appservices_request_id.to_string(); } void read_error(std::error_code ec) diff --git a/test/benchmark-sync/bench_transform.cpp b/test/benchmark-sync/bench_transform.cpp index 9e3ff7b649b..84674866e94 100644 --- a/test/benchmark-sync/bench_transform.cpp +++ b/test/benchmark-sync/bench_transform.cpp @@ -85,10 +85,10 @@ void transform_transactions(TestContext& test_context) return SyncClientHookAction::NoAction; }; - Session session_1 = fixture.make_session(0, db_1, std::move(session_config)); - fixture.bind_session(session_1, 0, "/test"); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test", std::move(session_config)); + session_1.bind(); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); // Start server and upload changes of second client. fixture.start_server(0); @@ -166,10 +166,10 @@ void transform_instructions(TestContext& test_context) return SyncClientHookAction::NoAction; }; - Session session_1 = fixture.make_session(0, db_1, std::move(session_config)); - fixture.bind_session(session_1, 0, "/test"); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test", std::move(session_config)); + session_1.bind(); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); // Start server and upload changes of second client. fixture.start_server(0); @@ -245,10 +245,10 @@ void connected_objects(TestContext& test_context) return SyncClientHookAction::NoAction; }; - Session session_1 = fixture.make_session(0, db_1, std::move(session_config)); - fixture.bind_session(session_1, 0, "/test"); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test", std::move(session_config)); + session_1.bind(); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); // Start server and upload changes of second client. fixture.start_server(0); diff --git a/test/sync_fixtures.hpp b/test/sync_fixtures.hpp index 4a255e87320..8191018da7a 100644 --- a/test/sync_fixtures.hpp +++ b/test/sync_fixtures.hpp @@ -691,11 +691,15 @@ class MultiClientServerFixture { return *m_servers[server_index]; } - Session make_session(int client_index, DBRef db, Session::Config config = {}) + Session make_session(int client_index, int server_index, DBRef db, std::string realm_identifier, + Session::Config config = {}) { // *ClientServerFixture uses the service identifier "/realm-sync" to distinguish Sync // connections, while the MongoDB/Stitch-based Sync server does not. config.service_identifier = "/realm-sync"; + config.realm_identifier = std::move(realm_identifier); + config.server_port = m_server_ports[server_index]; + config.server_address = "localhost"; Session session{*m_clients[client_index], std::move(db), nullptr, std::move(config)}; if (m_connection_state_change_listeners[client_index]) { @@ -719,16 +723,6 @@ class MultiClientServerFixture { return session; } - void bind_session(Session& session, int server_index, std::string server_path, - std::string signed_user_token = g_signed_test_user_token, - ProtocolEnvelope protocol = ProtocolEnvelope::realm) - { - std::string server_address = "localhost"; - port_type server_port = m_server_ports[server_index]; - session.bind(std::move(server_address), std::move(server_path), std::move(signed_user_token), server_port, - protocol); - } - Session make_bound_session(int client_index, DBRef db, int server_index, std::string server_path, Session::Config config = {}) { @@ -739,9 +733,10 @@ class MultiClientServerFixture { Session make_bound_session(int client_index, DBRef db, int server_index, std::string server_path, std::string signed_user_token, Session::Config config = {}) { - Session session = make_session(client_index, std::move(db), std::move(config)); - bind_session(session, server_index, std::move(server_path), std::move(signed_user_token), - config.protocol_envelope); + config.signed_user_token = std::move(signed_user_token); + Session session = + make_session(client_index, server_index, std::move(db), std::move(server_path), std::move(config)); + session.bind(); return session; } @@ -896,22 +891,16 @@ class ClientServerFixture : public MultiClientServerFixture { return MultiClientServerFixture::get_server(0); } - Session make_session(DBRef db, Session::Config&& config = {}) + Session make_session(DBRef db, std::string realm_identifier, Session::Config&& config = {}) { - return MultiClientServerFixture::make_session(0, std::move(db), std::move(config)); + return MultiClientServerFixture::make_session(0, 0, std::move(db), std::move(realm_identifier), + std::move(config)); } - Session make_session(std::string const& path, Session::Config&& config = {}) + Session make_session(std::string const& path, std::string realm_identifier, Session::Config&& config = {}) { auto db = DB::create(make_client_replication(), path); - return MultiClientServerFixture::make_session(0, std::move(db), std::move(config)); - } - - void bind_session(Session& session, std::string server_path, - std::string signed_user_token = g_signed_test_user_token, - ProtocolEnvelope protocol = ProtocolEnvelope::realm) - { - MultiClientServerFixture::bind_session(session, 0, std::move(server_path), std::move(signed_user_token), - protocol); + return MultiClientServerFixture::make_session(0, 0, std::move(db), std::move(realm_identifier), + std::move(config)); } Session make_bound_session(DBRef db, std::string server_path = "/test", Session::Config&& config = {}) @@ -999,25 +988,26 @@ class RealmFixture { inline RealmFixture::RealmFixture(ClientServerFixture& client_server_fixture, const std::string& real_path, const std::string& virt_path, Config config) - : m_self_ref{std::make_shared(this)} // Throws - , m_db{DB::create(make_client_replication(), real_path)} // Throws - , m_session{client_server_fixture.make_session(m_db, std::move(config))} // Throws + : m_self_ref{std::make_shared(this)} // Throws + , m_db{DB::create(make_client_replication(), real_path)} // Throws + , m_session{client_server_fixture.make_session(m_db, virt_path, std::move(config))} // Throws { if (config.error_handler) setup_error_handler(std::move(config.error_handler)); - client_server_fixture.bind_session(m_session, virt_path); + m_session.bind(); } inline RealmFixture::RealmFixture(MultiClientServerFixture& client_server_fixture, int client_index, int server_index, const std::string& real_path, const std::string& virt_path, Config config) - : m_self_ref{std::make_shared(this)} // Throws - , m_db{DB::create(make_client_replication(), real_path)} // Throws - , m_session{client_server_fixture.make_session(client_index, m_db, std::move(config))} // Throws + : m_self_ref{std::make_shared(this)} // Throws + , m_db{DB::create(make_client_replication(), real_path)} // Throws + , m_session{client_server_fixture.make_session(client_index, server_index, m_db, virt_path, std::move(config))} +// Throws { if (config.error_handler) setup_error_handler(std::move(config.error_handler)); - client_server_fixture.bind_session(m_session, server_index, virt_path); + m_session.bind(); } inline RealmFixture::~RealmFixture() noexcept diff --git a/test/test_client_reset.cpp b/test/test_client_reset.cpp index dfcdcf035f1..90d80bdb1fa 100644 --- a/test/test_client_reset.cpp +++ b/test/test_client_reset.cpp @@ -120,8 +120,8 @@ TEST(ClientReset_NoLocalChanges) session.nonsync_transact_notify(wt.commit()); session.wait_for_upload_complete_or_client_stopped(); - Session session_2 = fixture.make_session(path_2); - fixture.bind_session(session_2, server_path); + Session session_2 = fixture.make_session(path_2, server_path); + session_2.bind(); session_2.wait_for_download_complete_or_client_stopped(); } @@ -156,17 +156,17 @@ TEST(ClientReset_NoLocalChanges) bowl.add_stone(); }; - Session session = fixture.make_session(path_2); + Session session = fixture.make_session(path_2, server_path); session.set_connection_state_change_listener(listener); - fixture.bind_session(session, server_path); + session.bind(); bowl.get_stone(); } // get a fresh copy from the server to reset against SHARED_GROUP_TEST_PATH(path_fresh); { - Session session_fresh = fixture.make_session(path_fresh); - fixture.bind_session(session_fresh, server_path); + Session session_fresh = fixture.make_session(path_fresh, server_path); + session_fresh.bind(); session_fresh.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh = DB::create(make_client_replication(), path_fresh); @@ -197,9 +197,9 @@ TEST(ClientReset_NoLocalChanges) client_reset_config.fresh_copy = std::move(sg_fresh); session_config.client_reset_config = std::move(client_reset_config); } - Session session = fixture.make_session(sg, std::move(session_config)); + Session session = fixture.make_session(sg, server_path, std::move(session_config)); session.set_sync_transact_callback(std::move(sync_transact_callback)); - fixture.bind_session(session, server_path); + session.bind(); session.wait_for_download_complete_or_client_stopped(); CHECK(sync_transact_callback_called); } @@ -229,8 +229,8 @@ TEST(ClientReset_InitialLocalChanges) ClientServerFixture fixture(dir, test_context); fixture.start(); - Session session_1 = fixture.make_session(path_1); - fixture.bind_session(session_1, server_path); + Session session_1 = fixture.make_session(path_1, server_path); + session_1.bind(); // First we make a changeset and upload it { @@ -256,8 +256,8 @@ TEST(ClientReset_InitialLocalChanges) // get a fresh copy from the server to reset against SHARED_GROUP_TEST_PATH(path_fresh); { - Session session_fresh = fixture.make_session(path_fresh); - fixture.bind_session(session_fresh, server_path); + Session session_fresh = fixture.make_session(path_fresh, server_path); + session_fresh.bind(); session_fresh.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh = DB::create(make_client_replication(), path_fresh); @@ -270,8 +270,8 @@ TEST(ClientReset_InitialLocalChanges) client_reset_config.fresh_copy = std::move(sg_fresh); session_config_2.client_reset_config = std::move(client_reset_config); } - Session session_2 = fixture.make_session(path_2, std::move(session_config_2)); - fixture.bind_session(session_2, server_path); + Session session_2 = fixture.make_session(path_2, server_path, std::move(session_config_2)); + session_2.bind(); session_2.wait_for_upload_complete_or_client_stopped(); session_2.wait_for_download_complete_or_client_stopped(); @@ -347,8 +347,8 @@ TEST_TYPES(ClientReset_LocalChangesWhenOffline, std::true_type, std::false_type) { // Download a new Realm. The state is empty. Session::Config session_config_1; - Session session_1 = fixture.make_session(sg, std::move(session_config_1)); - fixture.bind_session(session_1, server_path); + Session session_1 = fixture.make_session(sg, server_path, std::move(session_config_1)); + session_1.bind(); session_1.wait_for_download_complete_or_client_stopped(); WriteTransaction wt{sg}; @@ -360,8 +360,8 @@ TEST_TYPES(ClientReset_LocalChangesWhenOffline, std::true_type, std::false_type) } DBRef sg_2 = DB::create(make_client_replication(), path_2); - Session session_2 = fixture.make_session(sg_2); - fixture.bind_session(session_2, server_path); + Session session_2 = fixture.make_session(sg_2, server_path); + session_2.bind(); session_2.wait_for_upload_complete_or_client_stopped(); session_2.wait_for_download_complete_or_client_stopped(); @@ -385,8 +385,8 @@ TEST_TYPES(ClientReset_LocalChangesWhenOffline, std::true_type, std::false_type) // get a fresh copy from the server to reset against SHARED_GROUP_TEST_PATH(path_fresh1); { - Session session4 = fixture.make_session(path_fresh1); - fixture.bind_session(session4, server_path); + Session session4 = fixture.make_session(path_fresh1, server_path); + session4.bind(); session4.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh1 = DB::create(make_client_replication(), path_fresh1); @@ -395,8 +395,8 @@ TEST_TYPES(ClientReset_LocalChangesWhenOffline, std::true_type, std::false_type) session_config_3.client_reset_config = Session::Config::ClientReset{}; session_config_3.client_reset_config->mode = recover ? ClientResyncMode::Recover : ClientResyncMode::DiscardLocal; session_config_3.client_reset_config->fresh_copy = std::move(sg_fresh1); - Session session_3 = fixture.make_session(sg, std::move(session_config_3)); - fixture.bind_session(session_3, server_path); + Session session_3 = fixture.make_session(sg, server_path, std::move(session_config_3)); + session_3.bind(); session_3.wait_for_upload_complete_or_client_stopped(); session_3.wait_for_download_complete_or_client_stopped(); @@ -486,10 +486,10 @@ TEST(ClientReset_ThreeClients) wt.commit(); } - Session session_1 = fixture.make_session(path_1); - fixture.bind_session(session_1, server_path); - Session session_2 = fixture.make_session(path_2); - fixture.bind_session(session_2, server_path); + Session session_1 = fixture.make_session(path_1, server_path); + session_1.bind(); + Session session_2 = fixture.make_session(path_2, server_path); + session_2.bind(); session_1.wait_for_upload_complete_or_client_stopped(); session_2.wait_for_upload_complete_or_client_stopped(); @@ -605,12 +605,12 @@ TEST(ClientReset_ThreeClients) bowl.add_stone(); }; - Session session_1 = fixture.make_session(path_1); + Session session_1 = fixture.make_session(path_1, server_path); session_1.set_connection_state_change_listener(listener); - fixture.bind_session(session_1, server_path); - Session session_2 = fixture.make_session(path_2); + session_1.bind(); + Session session_2 = fixture.make_session(path_2, server_path); session_2.set_connection_state_change_listener(listener); - fixture.bind_session(session_2, server_path); + session_2.bind(); bowl.get_stone(); bowl.get_stone(); } @@ -619,15 +619,15 @@ TEST(ClientReset_ThreeClients) SHARED_GROUP_TEST_PATH(path_fresh1); SHARED_GROUP_TEST_PATH(path_fresh2); { - Session session4 = fixture.make_session(path_fresh1); - fixture.bind_session(session4, server_path); + Session session4 = fixture.make_session(path_fresh1, server_path); + session4.bind(); session4.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh1 = DB::create(make_client_replication(), path_fresh1); { - Session session4 = fixture.make_session(path_fresh2); - fixture.bind_session(session4, server_path); + Session session4 = fixture.make_session(path_fresh2, server_path); + session4.bind(); session4.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh2 = DB::create(make_client_replication(), path_fresh2); @@ -648,10 +648,10 @@ TEST(ClientReset_ThreeClients) client_reset_config.fresh_copy = std::move(sg_fresh2); session_config_2.client_reset_config = std::move(client_reset_config); } - Session session_1 = fixture.make_session(path_1, std::move(session_config_1)); - fixture.bind_session(session_1, server_path); - Session session_2 = fixture.make_session(path_2, std::move(session_config_2)); - fixture.bind_session(session_2, server_path); + Session session_1 = fixture.make_session(path_1, server_path, std::move(session_config_1)); + session_1.bind(); + Session session_2 = fixture.make_session(path_2, server_path, std::move(session_config_2)); + session_2.bind(); session_1.wait_for_download_complete_or_client_stopped(); session_2.wait_for_download_complete_or_client_stopped(); @@ -676,10 +676,10 @@ TEST(ClientReset_ThreeClients) } // Upload and download complete the clients. - Session session_1 = fixture.make_session(path_1); - fixture.bind_session(session_1, server_path); - Session session_2 = fixture.make_session(path_2); - fixture.bind_session(session_2, server_path); + Session session_1 = fixture.make_session(path_1, server_path); + session_1.bind(); + Session session_2 = fixture.make_session(path_2, server_path); + session_2.bind(); session_1.wait_for_upload_complete_or_client_stopped(); session_2.wait_for_upload_complete_or_client_stopped(); @@ -691,8 +691,8 @@ TEST(ClientReset_ThreeClients) // A third client downloads the state { Session::Config session_config; - Session session = fixture.make_session(path_3, std::move(session_config)); - fixture.bind_session(session, server_path); + Session session = fixture.make_session(path_3, server_path, std::move(session_config)); + session.bind(); session.wait_for_download_complete_or_client_stopped(); } } @@ -750,8 +750,8 @@ TEST(ClientReset_DoNotRecoverSchema) // get a fresh copy from the server to reset against SHARED_GROUP_TEST_PATH(path_fresh1); { - Session session_fresh = fixture.make_session(path_fresh1); - fixture.bind_session(session_fresh, server_path_2); + Session session_fresh = fixture.make_session(path_fresh1, server_path_2); + session_fresh.bind(); session_fresh.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh1 = DB::create(make_client_replication(), path_fresh1); @@ -767,7 +767,7 @@ TEST(ClientReset_DoNotRecoverSchema) client_reset_config.fresh_copy = std::move(sg_fresh1); session_config.client_reset_config = std::move(client_reset_config); } - Session session = fixture.make_session(path_1, std::move(session_config)); + Session session = fixture.make_session(path_1, server_path_2, std::move(session_config)); BowlOfStonesSemaphore bowl; session.set_connection_state_change_listener( [&](ConnectionState state, util::Optional error_info) { @@ -778,7 +778,7 @@ TEST(ClientReset_DoNotRecoverSchema) CHECK_EQUAL(ec, sync::Client::Error::auto_client_reset_failure); bowl.add_stone(); }); - fixture.bind_session(session, server_path_2); + session.bind(); bowl.get_stone(); } @@ -841,8 +841,8 @@ TEST(ClientReset_PinnedVersion) // get a fresh copy from the server to reset against SHARED_GROUP_TEST_PATH(path_fresh); { - Session session_fresh = fixture.make_session(path_fresh); - fixture.bind_session(session_fresh, server_path_1); + Session session_fresh = fixture.make_session(path_fresh, server_path_1); + session_fresh.bind(); session_fresh.wait_for_download_complete_or_client_stopped(); } DBRef sg_fresh = DB::create(make_client_replication(), path_fresh); diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 0547dc842bb..8fd42f434df 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -152,17 +152,17 @@ TEST(Sync_BadVirtualPath) fixture.stop(); }; - Session session_1 = fixture.make_session(db_1); + Session session_1 = fixture.make_session(db_1, "/test.realm"); session_1.set_connection_state_change_listener(listener); - fixture.bind_session(session_1, "/test.realm"); + session_1.bind(); - Session session_2 = fixture.make_session(db_2); + Session session_2 = fixture.make_session(db_2, "/../test"); session_2.set_connection_state_change_listener(listener); - fixture.bind_session(session_2, "/../test"); + session_2.bind(); - Session session_3 = fixture.make_session(db_3); + Session session_3 = fixture.make_session(db_3, "test%abc "); session_3.set_connection_state_change_listener(listener); - fixture.bind_session(session_3, "/test%abc "); + session_3.bind(); session_1.wait_for_download_complete_or_client_stopped(); session_2.wait_for_download_complete_or_client_stopped(); @@ -587,9 +587,11 @@ TEST(Sync_TokenWithoutExpirationAllowed) fixture.start(); - Session session = fixture.make_session(db); + Session::Config sess_config; + sess_config.signed_user_token = g_signed_test_user_token_expiration_unspecified; + Session session = fixture.make_session(db, "/test", std::move(sess_config)); session.set_connection_state_change_listener(listener); - fixture.bind_session(session, "/test", g_signed_test_user_token_expiration_unspecified); + session.bind(); write_transaction_notifying_session(db, session, [](WriteTransaction& wt) { wt.get_group().add_table_with_primary_key("class_foo", type_Int, "id"); }); @@ -614,8 +616,10 @@ TEST(Sync_TokenWithNullExpirationAllowed) fixture.set_client_side_error_handler(error_handler); fixture.start(); - Session session = fixture.make_session(db); - fixture.bind_session(session, "/test", g_signed_test_user_token_expiration_null); + Session::Config config; + config.signed_user_token = g_signed_test_user_token_expiration_null; + Session session = fixture.make_session(db, "/test", std::move(config)); + session.bind(); { write_transaction_notifying_session(db, session, [](WriteTransaction& wt) { wt.get_group().add_table_with_primary_key("class_foo", type_Int, "id"); @@ -675,9 +679,9 @@ TEST(Sync_Replication) Session session_1 = fixture.make_bound_session(db_1); - Session session_2 = fixture.make_session(db_2); + Session session_2 = fixture.make_session(db_2, "/test"); session_2.set_sync_transact_callback(std::move(sync_transact_callback)); - fixture.bind_session(session_2, "/test"); + session_2.bind(); // Create schema write_transaction_notifying_session(db_1, session_1, [](WriteTransaction& wt) { @@ -727,11 +731,11 @@ TEST(Sync_Merge) MultiClientServerFixture fixture(2, 1, dir, test_context); fixture.start(); - Session session_1 = fixture.make_session(0, db_1); - fixture.bind_session(session_1, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); + session_1.bind(); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); // Create schema on both clients. auto create_schema = [](Session& sess, DBRef db) { @@ -810,8 +814,8 @@ void test_schema_mismatch(unit_test::TestContext& test_context, util::FunctionRe fixture.allow_server_errors(0, 1); fixture.start(); - Session session_1 = fixture.make_session(0, db_1); - Session session_2 = fixture.make_session(1, db_2); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); if (!expected_error_2) expected_error_2 = expected_error_1; @@ -819,8 +823,8 @@ void test_schema_mismatch(unit_test::TestContext& test_context, util::FunctionRe session_1.set_connection_state_change_listener(ExpectChangesetError{test_context, fixture, expected_error_1}); session_2.set_connection_state_change_listener(ExpectChangesetError{test_context, fixture, expected_error_2}); - fixture.bind_session(session_1, 0, "/test"); - fixture.bind_session(session_2, 0, "/test"); + session_1.bind(); + session_2.bind(); write_transaction_notifying_session(db_1, session_1, fn_1); write_transaction_notifying_session(db_2, session_2, fn_2); @@ -1078,31 +1082,31 @@ TEST(Sync_AbandonUnboundSessions) int n = 32; for (int i = 0; i < n; ++i) { - fixture.make_session(db_1); - fixture.make_session(db_2); - fixture.make_session(db_3); + fixture.make_session(db_1, "/test"); + fixture.make_session(db_2, "/test"); + fixture.make_session(db_3, "/test"); } for (int i = 0; i < n; ++i) { - fixture.make_session(db_1); - Session session = fixture.make_session(db_2); - fixture.make_session(db_3); - fixture.bind_session(session, "/test"); + fixture.make_session(db_1, "/test"); + Session session = fixture.make_session(db_2, "/test"); + fixture.make_session(db_3, "/test"); + session.bind(); } for (int i = 0; i < n; ++i) { - fixture.make_session(db_1); - Session session = fixture.make_session(db_2); - fixture.make_session(db_3); - fixture.bind_session(session, "/test"); + fixture.make_session(db_1, "/test"); + Session session = fixture.make_session(db_2, "/test"); + fixture.make_session(db_3, "/test"); + session.bind(); session.wait_for_upload_complete_or_client_stopped(); } for (int i = 0; i < n; ++i) { - fixture.make_session(db_1); - Session session = fixture.make_session(db_2); - fixture.make_session(db_3); - fixture.bind_session(session, "/test"); + fixture.make_session(db_1, "/test"); + Session session = fixture.make_session(db_2, "/test"); + fixture.make_session(db_3, "/test"); + session.bind(); session.wait_for_download_complete_or_client_stopped(); } } @@ -1414,11 +1418,11 @@ TEST(Sync_Randomized) client_shared_groups[i] = DB::create(make_client_replication(), test_path); } - std::unique_ptr sessions[num_clients]; + std::vector> sessions(num_clients); for (size_t i = 0; i < num_clients; ++i) { auto db = client_shared_groups[i]; - sessions[i].reset(new Session(fixture.make_session(int(i), db))); - fixture.bind_session(*sessions[i], 0, "/test"); + sessions[i] = std::make_unique(fixture.make_session(int(i), 0, db, "/test")); + sessions[i]->bind(); } auto run_client_test_program = [&](size_t i) { @@ -2144,8 +2148,8 @@ TEST(Sync_MultipleServers) std::string server_path = "/" + std::to_string(realm_index); for (int i = 0; i < num_sessions_per_file; ++i) { int client_index = 0; - Session session = fixture.make_session(client_index, db); - fixture.bind_session(session, server_index, server_path); + Session session = fixture.make_session(client_index, server_index, db, server_path); + session.bind(); for (int j = 0; j < num_transacts_per_session; ++j) { WriteTransaction wt(db); TableRef table = wt.get_table("class_table"); @@ -2173,8 +2177,8 @@ TEST(Sync_MultipleServers) std::string path = get_file_path(server_index, realm_index, file_index); DBRef db = DB::create(make_client_replication(), path); std::string server_path = "/" + std::to_string(realm_index); - Session session = fixture.make_session(client_index, db); - fixture.bind_session(session, server_index, server_path); + Session session = fixture.make_session(client_index, server_index, db, server_path); + session.bind(); session.wait_for_download_complete_or_client_stopped(); } catch (...) { @@ -2706,8 +2710,8 @@ TEST(Sync_SSL_Certificate_1) session_config.verify_servers_ssl_certificate = true; session_config.ssl_trust_certificate_path = ca_dir + "crt.pem"; - Session session = fixture.make_session(db, std::move(session_config)); - fixture.bind_session(session, "/test", g_signed_test_user_token, ProtocolEnvelope::realms); + Session session = fixture.make_session(db, "/test", std::move(session_config)); + session.bind(); fixture.start(); session.wait_for_download_complete_or_client_stopped(); @@ -2801,8 +2805,8 @@ TEST(Sync_SSL_Certificate_DER) session_config.verify_servers_ssl_certificate = true; session_config.ssl_trust_certificate_path = ca_dir + "localhost-chain.crt.cer"; - Session session = fixture.make_session(db, std::move(session_config)); - fixture.bind_session(session, "/test", g_signed_test_user_token, ProtocolEnvelope::realms); + Session session = fixture.make_session(db, "/test", std::move(session_config)); + session.bind(); fixture.start(); session.wait_for_download_complete_or_client_stopped(); @@ -3038,7 +3042,7 @@ TEST(Sync_UploadDownloadProgress_1) ClientServerFixture fixture(server_dir, test_context); fixture.start(); - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); auto progress_handler = [&](uint_fast64_t downloaded, uint_fast64_t downloadable, uint_fast64_t uploaded, uint_fast64_t uploadable, uint_fast64_t progress, uint_fast64_t snapshot) { @@ -3060,7 +3064,7 @@ TEST(Sync_UploadDownloadProgress_1) std::unique_lock lock(mutex); session.set_progress_handler(progress_handler); - fixture.bind_session(session, "/test"); + session.bind(); cond_var.wait(lock, [&] { return cond_var_signaled; }); @@ -3127,7 +3131,13 @@ TEST(Sync_UploadDownloadProgress_1) config.reconnect_mode = ReconnectMode::testing; Client client(config); - Session session(client, db, nullptr); + Session::Config sess_config; + sess_config.server_address = "no server"; + sess_config.server_port = 8000; + sess_config.realm_identifier = "/test"; + sess_config.signed_user_token = g_signed_test_user_token; + + Session session(client, db, nullptr, std::move(sess_config)); int number_of_handler_calls = 0; @@ -3150,9 +3160,7 @@ TEST(Sync_UploadDownloadProgress_1) std::unique_lock lock(mutex); session.set_progress_handler(progress_handler); - std::string server_address = "no server"; - Session::port_type server_port = 8000; - session.bind(server_address, "/test", g_signed_test_user_token, server_port, ProtocolEnvelope::realm); + session.bind(); cond_var.wait(lock, [&] { return cond_var_signaled; }); @@ -3178,8 +3186,8 @@ TEST(Sync_UploadDownloadProgress_2) ClientServerFixture fixture(server_dir, test_context); fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); uint_fast64_t downloaded_bytes_1 = 123; // Not zero uint_fast64_t downloadable_bytes_1 = 123; @@ -3221,8 +3229,8 @@ TEST(Sync_UploadDownloadProgress_2) session_2.set_progress_handler(progress_handler_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + session_1.bind(); + session_2.bind(); session_1.wait_for_upload_complete_or_client_stopped(); session_2.wait_for_upload_complete_or_client_stopped(); @@ -3397,6 +3405,10 @@ TEST(Sync_UploadDownloadProgress_3) // when connecting to the C++ server, use URL prefix: Session::Config config; config.service_identifier = "/realm-sync"; + config.server_address = server_address; + config.signed_user_token = g_signed_test_user_token; + config.server_port = server_port; + config.realm_identifier = "/test"; Session session(client, db, nullptr, std::move(config)); @@ -3448,7 +3460,7 @@ TEST(Sync_UploadDownloadProgress_3) session.set_progress_handler(progress_handler); - session.bind(server_address, "/test", g_signed_test_user_token, server_port, ProtocolEnvelope::realm); + session.bind(); session.wait_for_upload_complete_or_client_stopped(); session.wait_for_download_complete_or_client_stopped(); @@ -3528,7 +3540,7 @@ TEST(Sync_UploadDownloadProgress_4) ClientServerFixture fixture(server_dir, test_context, std::move(config)); fixture.start(); - Session session_1 = fixture.make_session(db_1); + Session session_1 = fixture.make_session(db_1, "/test"); int entry_1 = 0; @@ -3554,13 +3566,14 @@ TEST(Sync_UploadDownloadProgress_4) session_1.set_progress_handler(progress_handler_1); - fixture.bind_session(session_1, "/test"); + session_1.bind(); + session_1.wait_for_upload_complete_or_client_stopped(); session_1.wait_for_download_complete_or_client_stopped(); CHECK_NOT_EQUAL(entry_1, 0); - Session session_2 = fixture.make_session(db_2); + Session session_2 = fixture.make_session(db_2, "/test"); int entry_2 = 0; @@ -3594,7 +3607,7 @@ TEST(Sync_UploadDownloadProgress_4) session_2.set_progress_handler(progress_handler_2); - fixture.bind_session(session_2, "/test"); + session_2.bind(); session_2.wait_for_upload_complete_or_client_stopped(); session_2.wait_for_download_complete_or_client_stopped(); @@ -3614,7 +3627,7 @@ TEST(Sync_UploadDownloadProgress_5) ClientServerFixture fixture(server_dir, test_context); fixture.start(); - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); auto progress_handler = [&, promise = util::CopyablePromiseHolder(std::move(progress_handled_promise))]( uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes, @@ -3633,7 +3646,7 @@ TEST(Sync_UploadDownloadProgress_5) session.set_progress_handler(progress_handler); - fixture.bind_session(session, "/test"); + session.bind(); progress_handled.get(); // The check is that we reach this point. @@ -3751,9 +3764,9 @@ TEST(Sync_CancelReconnectDelay) if (CHECK_EQUAL(info.error_code, ProtocolError::connection_closed)) bowl.add_stone(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); session.set_error_handler(std::move(handler)); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_download_complete_or_client_stopped(); fixture.close_server_side_connections(); bowl.get_stone(); @@ -3773,9 +3786,9 @@ TEST(Sync_CancelReconnectDelay) if (CHECK_EQUAL(info.error_code, ProtocolError::connection_closed)) bowl.add_stone(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); session.set_error_handler(std::move(handler)); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_download_complete_or_client_stopped(); fixture.close_server_side_connections(); bowl.get_stone(); @@ -3796,9 +3809,9 @@ TEST(Sync_CancelReconnectDelay) if (CHECK_EQUAL(info.error_code, ProtocolError::connection_closed)) bowl.add_stone(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); session.set_error_handler(std::move(handler)); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_download_complete_or_client_stopped(); fixture.close_server_side_connections(); bowl.get_stone(); @@ -3832,9 +3845,9 @@ TEST(Sync_CancelReconnectDelay) if (CHECK_EQUAL(info.error_code, ProtocolError::illegal_realm_path)) bowl.add_stone(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/.."); session.set_error_handler(std::move(handler)); - fixture.bind_session(session, "/.."); // Illegal virtual path + session.bind(); bowl.get_stone(); session.cancel_reconnect_delay(); @@ -3855,9 +3868,9 @@ TEST(Sync_CancelReconnectDelay) if (CHECK_EQUAL(info.error_code, ProtocolError::illegal_realm_path)) bowl.add_stone(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/.."); session.set_error_handler(std::move(handler)); - fixture.bind_session(session, "/.."); // Illegal virtual path + session.bind(); bowl.get_stone(); fixture.cancel_reconnect_delay(); @@ -3969,24 +3982,24 @@ TEST_IF(Sync_MergeLargeBinary, !(REALM_ARCHITECTURE_X86_32)) fixture.start(); { - Session session_1 = fixture.make_session(0, db_1); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); session_1.set_progress_handler(progress_handler_1); - fixture.bind_session(session_1, 0, "/test"); + session_1.bind(); session_1.wait_for_upload_complete_or_client_stopped(); } { - Session session_2 = fixture.make_session(1, db_2); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); session_2.set_progress_handler(progress_handler_2); - fixture.bind_session(session_2, 0, "/test"); + session_2.bind(); session_2.wait_for_download_complete_or_client_stopped(); session_2.wait_for_upload_complete_or_client_stopped(); } { - Session session_1 = fixture.make_session(0, db_1); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); session_1.set_progress_handler(progress_handler_1); - fixture.bind_session(session_1, 0, "/test"); + session_1.bind(); session_1.wait_for_download_complete_or_client_stopped(); } } @@ -4123,24 +4136,24 @@ TEST(Sync_MergeLargeBinaryReducedMemory) fixture.start(); { - Session session_1 = fixture.make_session(0, db_1); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); session_1.set_progress_handler(progress_handler_1); - fixture.bind_session(session_1, 0, "/test"); + session_1.bind(); session_1.wait_for_upload_complete_or_client_stopped(); } { - Session session_2 = fixture.make_session(1, db_2); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); session_2.set_progress_handler(progress_handler_2); - fixture.bind_session(session_2, 0, "/test"); + session_2.bind(); session_2.wait_for_download_complete_or_client_stopped(); session_2.wait_for_upload_complete_or_client_stopped(); } { - Session session_1 = fixture.make_session(0, db_1); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); session_1.set_progress_handler(progress_handler_1); - fixture.bind_session(session_1, 0, "/test"); + session_1.bind(); session_1.wait_for_download_complete_or_client_stopped(); } } @@ -4235,10 +4248,10 @@ TEST(Sync_MergeLargeChangesets) TEST_DIR(dir); MultiClientServerFixture fixture(2, 1, dir, test_context); - Session session_1 = fixture.make_session(0, db_1); - fixture.bind_session(session_1, 0, "/test"); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); + session_1.bind(); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); fixture.start(); @@ -4311,10 +4324,10 @@ TEST(Sync_MergeMultipleChangesets) // Start server and upload changes of first client. - Session session_1 = fixture.make_session(0, db_1); - fixture.bind_session(session_1, 0, "/test"); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); + session_1.bind(); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); fixture.start_server(0); fixture.start_client(0); @@ -4486,12 +4499,12 @@ TEST(Sync_Quadratic_Merge) MultiClientServerFixture fixture{num_clients, num_servers, server_dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(0, db_1); - fixture.bind_session(session_1, 0, "/test"); + Session session_1 = fixture.make_session(0, 0, db_1, "/test"); + session_1.bind(); session_1.wait_for_upload_complete_or_client_stopped(); - Session session_2 = fixture.make_session(1, db_2); - fixture.bind_session(session_2, 0, "/test"); + Session session_2 = fixture.make_session(1, 0, db_2, "/test"); + session_2.bind(); session_2.wait_for_upload_complete_or_client_stopped(); session_1.wait_for_download_complete_or_client_stopped(); @@ -4507,7 +4520,7 @@ TEST(Sync_BatchedUploadMessages) ClientServerFixture fixture(server_dir, test_context); fixture.start(); - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); { WriteTransaction wt{db}; @@ -4541,7 +4554,7 @@ TEST(Sync_BatchedUploadMessages) }; session.set_progress_handler(progress_handler); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_upload_complete_or_client_stopped(); } @@ -4557,8 +4570,8 @@ TEST(Sync_UploadLogCompactionEnabled) ClientServerFixture fixture(server_dir, test_context, std::move(config)); fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); // Create a changeset with lots of overwrites of the // same fields. @@ -4575,7 +4588,7 @@ TEST(Sync_UploadLogCompactionEnabled) wt.commit(); } - fixture.bind_session(session_1, "/test"); + session_1.bind(); session_1.wait_for_upload_complete_or_client_stopped(); auto progress_handler = [&](uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes, @@ -4591,7 +4604,7 @@ TEST(Sync_UploadLogCompactionEnabled) session_2.set_progress_handler(progress_handler); - fixture.bind_session(session_2, "/test"); + session_2.bind(); session_2.wait_for_download_complete_or_client_stopped(); @@ -4648,9 +4661,9 @@ TEST(Sync_UploadLogCompactionDisabled) CHECK_NOT_EQUAL(0, downloadable_bytes); }; - Session session_2 = fixture.make_session(db_2); + Session session_2 = fixture.make_session(db_2, "/test"); session_2.set_progress_handler(progress_handler); - fixture.bind_session(session_2, "/test"); + session_2.bind(); session_2.wait_for_download_complete_or_client_stopped(); { @@ -4831,14 +4844,14 @@ TEST(Sync_ConnectionStateChange) bowl_2.add_stone(); }; - Session session_1 = fixture.make_session(db_1); + Session session_1 = fixture.make_session(db_1, "/test"); session_1.set_connection_state_change_listener(listener_1); - fixture.bind_session(session_1, "/test"); + session_1.bind(); session_1.wait_for_download_complete_or_client_stopped(); - Session session_2 = fixture.make_session(db_2); + Session session_2 = fixture.make_session(db_2, "/test"); session_2.set_connection_state_change_listener(listener_2); - fixture.bind_session(session_2, "/test"); + session_2.bind(); session_2.wait_for_download_complete_or_client_stopped(); fixture.close_server_side_connections(); @@ -4864,9 +4877,9 @@ TEST(Sync_ClientErrorHandler) bowl.add_stone(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); session.set_error_handler(std::move(handler)); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_download_complete_or_client_stopped(); fixture.close_server_side_connections(); @@ -4897,8 +4910,8 @@ TEST(Sync_VerifyServerHistoryAfterLargeUpload) wt->commit(); - Session session = fixture.make_session(db); - fixture.bind_session(session, "/test"); + Session session = fixture.make_session(db, "/test"); + session.bind(); session.wait_for_upload_complete_or_client_stopped(); } @@ -5072,8 +5085,8 @@ TEST(Sync_AuthorizationHeaderName) custom_http_headers["Header-Name-1"] = "Header-Value-1"; custom_http_headers["Header-Name-2"] = "Header-Value-2"; session_config.custom_http_headers = std::move(custom_http_headers); - Session session = fixture.make_session(db, std::move(session_config)); - fixture.bind_session(session, "/test"); + Session session = fixture.make_session(db, "/test", std::move(session_config)); + session.bind(); session.wait_for_download_complete_or_client_stopped(); } @@ -5119,9 +5132,9 @@ TEST(Sync_BadChangeset) fixture.stop(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); session.set_connection_state_change_listener(listener); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_upload_complete_or_client_stopped(); session.wait_for_download_complete_or_client_stopped(); @@ -5163,9 +5176,9 @@ TEST(Sync_GoodChangeset_AccentCharacterInFieldName) fixture.stop(); }; - Session session = fixture.make_session(db); + Session session = fixture.make_session(db, "/test"); session.set_connection_state_change_listener(listener); - fixture.bind_session(session, "/test"); + session.bind(); session.wait_for_upload_complete_or_client_stopped(); } @@ -5708,10 +5721,10 @@ NONCONCURRENT_TEST_TYPES(Sync_PrimaryKeyTypes, Int, String, ObjectId, UUID, util fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); TEST_TYPE obj_1_id; TEST_TYPE obj_2_id; @@ -5782,10 +5795,10 @@ TEST(Sync_Mixed) fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); { WriteTransaction tr{db_1}; @@ -5861,10 +5874,10 @@ TEST(Sync_TypedLinks) fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); write_transaction_notifying_session(db_1, session_1, [](WriteTransaction& tr) { auto& g = tr.get_group(); @@ -5922,10 +5935,10 @@ TEST(Sync_Dictionary) fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); Timestamp now{std::chrono::system_clock::now()}; @@ -6024,10 +6037,10 @@ TEST(Sync_Dictionary_Links) fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); // Test that we can transmit links. @@ -6128,10 +6141,10 @@ TEST(Sync_Set) fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); ColKey col_ints, col_strings, col_mixeds; { @@ -6422,10 +6435,10 @@ TEST(Sync_UpgradeToClientHistory) fixtures::ClientServerFixture fixture{dir, test_context}; fixture.start(); - Session session_1 = fixture.make_session(db_1); - Session session_2 = fixture.make_session(db_2); - fixture.bind_session(session_1, "/test"); - fixture.bind_session(session_2, "/test"); + Session session_1 = fixture.make_session(db_1, "/test"); + Session session_2 = fixture.make_session(db_2, "/test"); + session_1.bind(); + session_2.bind(); write_transaction_notifying_session(db_1, session_1, [](WriteTransaction& tr) { auto foos = tr.get_group().get_table("class_Foo"); @@ -6605,4 +6618,47 @@ TEST(Sync_InvalidChangesetFromServer) StringData(e.what()).contains("Failed to parse received changeset: Invalid interned string")); } +TEST(Sync_DifferentUsersMultiplexing) +{ + ClientServerFixture::Config fixture_config; + fixture_config.one_connection_per_session = false; + + TEST_DIR(server_dir); + ClientServerFixture fixture(server_dir, test_context, std::move(fixture_config)); + + struct SessionBundle { + test_util::DBTestPathGuard path_guard; + DBRef db; + Session sess; + + SessionBundle(unit_test::TestContext& ctx, ClientServerFixture& fixture, std::string name, + std::string signed_token, std::string user_id) + : path_guard(realm::test_util::get_test_path(ctx.get_test_name(), "." + name + ".realm")) + , db(DB::create(make_client_replication(), path_guard)) + { + Session::Config config; + config.signed_user_token = signed_token; + config.user_id = user_id; + sess = fixture.make_bound_session(db, "/test", std::move(config)); + sess.wait_for_download_complete_or_client_stopped(); + } + }; + + fixture.start(); + + SessionBundle user_1_sess_1(test_context, fixture, "user_1_db_1", g_user_0_token, "user_0"); + SessionBundle user_2_sess_1(test_context, fixture, "user_2_db_1", g_user_1_token, "user_1"); + SessionBundle user_1_sess_2(test_context, fixture, "user_1_db_2", g_user_0_token, "user_0"); + SessionBundle user_2_sess_2(test_context, fixture, "user_2_db_2", g_user_1_token, "user_1"); + + CHECK_EQUAL(user_1_sess_1.sess.get_appservices_connection_id(), + user_1_sess_2.sess.get_appservices_connection_id()); + CHECK_EQUAL(user_2_sess_1.sess.get_appservices_connection_id(), + user_2_sess_2.sess.get_appservices_connection_id()); + CHECK_NOT_EQUAL(user_1_sess_1.sess.get_appservices_connection_id(), + user_2_sess_1.sess.get_appservices_connection_id()); + CHECK_NOT_EQUAL(user_1_sess_2.sess.get_appservices_connection_id(), + user_2_sess_2.sess.get_appservices_connection_id()); +} + } // unnamed namespace From f11a81126f6d407aa7fc1acffe286ba185b7bba2 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Mon, 6 Mar 2023 17:03:12 -0500 Subject: [PATCH 02/17] wip --- src/realm/sync/noinst/client_impl_base.cpp | 47 ++++++++++------------ src/realm/sync/noinst/client_impl_base.hpp | 12 +++--- test/object-store/sync/flx_sync.cpp | 24 +++++------ test/object-store/sync/sync_test_utils.hpp | 2 +- 4 files changed, 40 insertions(+), 45 deletions(-) diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index fde2ca1542a..072696b6709 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -132,7 +132,7 @@ ClientImpl::ClientImpl(ClientConfig config) , logger{*logger_ptr} , m_reconnect_mode{config.reconnect_mode} , m_connect_timeout{config.connect_timeout} - , m_connection_linger_time{config.one_connection_per_session ? 0 : config.connection_linger_time} + , m_connection_linger_time{config.connection_linger_time} , m_ping_keepalive_period{config.ping_keepalive_period} , m_pong_keepalive_timeout{config.pong_keepalive_timeout} , m_fast_reconnect_limit{config.fast_reconnect_limit} @@ -144,7 +144,7 @@ ClientImpl::ClientImpl(ClientConfig config) , m_roundtrip_time_handler{std::move(config.roundtrip_time_handler)} , m_socket_provider{std::move(config.socket_provider)} , m_client_protocol{} // Throws - , m_one_connection_per_session{config.one_connection_per_session} + , m_one_connection_per_session{false} , m_random{} { // FIXME: Would be better if seeding was up to the application. @@ -1695,8 +1695,8 @@ void Session::on_integration_failure(const IntegrationException& error) // Since the deactivation process has not been initiated, the UNBIND // message cannot have been sent unless an ERROR message was received. - REALM_ASSERT(m_error_message_received || !m_unbind_message_sent); - if (m_ident_message_sent && !m_error_message_received) { + REALM_ASSERT(m_suspended || m_error_message_received || !m_unbind_message_sent); + if (m_ident_message_sent && !m_error_message_received && !m_suspended) { ensure_enlisted_to_send(); // Throws } } @@ -1723,8 +1723,8 @@ void Session::on_changesets_integrated(version_type client_version, const SyncPr // Since the deactivation process has not been initiated, the UNBIND // message cannot have been sent unless an ERROR message was received. - REALM_ASSERT(m_error_message_received || !m_unbind_message_sent); - if (m_ident_message_sent && !m_error_message_received) { + REALM_ASSERT(m_suspended || m_error_message_received || !m_unbind_message_sent); + if (m_ident_message_sent && !m_error_message_received && !m_suspended) { ensure_enlisted_to_send(); // Throws } } @@ -1871,7 +1871,7 @@ void Session::send_message() REALM_ASSERT(m_state == Active || m_state == Deactivating); REALM_ASSERT(m_enlisted_to_send); m_enlisted_to_send = false; - if (m_state == Deactivating || m_error_message_received) { + if (m_state == Deactivating || m_error_message_received || m_suspended) { // Deactivation has been initiated. If the UNBIND message has not been // sent yet, there is no point in sending it. Instead, we can let the // deactivation process complete. @@ -2200,7 +2200,7 @@ void Session::send_mark_message() void Session::send_unbind_message() { - REALM_ASSERT(m_state == Deactivating || m_error_message_received); + REALM_ASSERT(m_state == Deactivating || m_error_message_received || m_suspended); REALM_ASSERT(m_bind_message_sent); REALM_ASSERT(!m_unbind_message_sent); @@ -2269,18 +2269,6 @@ void Session::send_test_command_message() } -void Session::close_connection() -{ - REALM_ASSERT(m_state == Active); - REALM_ASSERT(m_ident_message_sent); - REALM_ASSERT(!m_unbind_message_sent); - REALM_ASSERT(m_client_error); - - m_conn.close_due_to_protocol_error(m_client_error->code(), m_client_error->what()); // Throws - m_client_error = util::none; -} - - std::error_code Session::receive_ident_message(SaltedFileIdent client_file_ident) { logger.debug("Received: IDENT(client_file_ident=%1, client_file_ident_salt=%2)", client_file_ident.ident, @@ -2381,8 +2369,11 @@ std::error_code Session::receive_ident_message(SaltedFileIdent client_file_ident did_client_reset = client_reset_if_needed(); } catch (const std::exception& e) { - logger.error("A fatal error occured during client reset: '%1'", e.what()); - return make_error_code(sync::ClientError::auto_client_reset_failure); + auto err_msg = util::format("A fatal error occured during client reset: '%1'", e.what()); + logger.error(err_msg.c_str()); + SessionErrorInfo err_info(make_error_code(ClientError::auto_client_reset_failure), err_msg, false); + suspend(err_info); + return {}; } if (!did_client_reset) { repl.get_history().set_client_file_ident(client_file_ident, m_fix_up_object_ids); // Throws @@ -2608,11 +2599,17 @@ std::error_code Session::receive_error_message(const ProtocolErrorInfo& info) return {}; } + m_error_message_received = true; + suspend(SessionErrorInfo{info, make_error_code(error_code)}); + return {}; +} + +void Session::suspend(const SessionErrorInfo& info) +{ REALM_ASSERT(!m_suspended); REALM_ASSERT(m_state == Active || m_state == Deactivating); logger.debug("Suspended"); // Throws - m_error_message_received = true; m_suspended = true; // Detect completion of the unbinding process @@ -2633,7 +2630,7 @@ std::error_code Session::receive_error_message(const ProtocolErrorInfo& info) // still in the Active state if (m_state == Active) { m_conn.one_less_active_unsuspended_session(); // Throws - on_suspended(SessionErrorInfo{info, make_error_code(error_code)}); // Throws + on_suspended(info); // Throws } if (info.try_again) { @@ -2643,8 +2640,6 @@ std::error_code Session::receive_error_message(const ProtocolErrorInfo& info) // Ready to send the UNBIND message, if it has not been sent already if (!m_unbind_message_sent) ensure_enlisted_to_send(); // Throws - - return std::error_code{}; // Success; } std::error_code Session::receive_test_command_response(request_ident_type ident, std::string_view body) diff --git a/src/realm/sync/noinst/client_impl_base.hpp b/src/realm/sync/noinst/client_impl_base.hpp index 4f5c20eec93..561e72a0967 100644 --- a/src/realm/sync/noinst/client_impl_base.hpp +++ b/src/realm/sync/noinst/client_impl_base.hpp @@ -1128,8 +1128,8 @@ class ClientImpl::Session { void initiate_deactivation(); void complete_deactivation(); void connection_established(bool fast_reconnect); + void suspend(const SessionErrorInfo& session_error); void connection_lost(); - void close_connection(); void send_message(); void message_sent(); void send_bind_message(); @@ -1357,8 +1357,8 @@ inline void ClientImpl::Session::recognize_sync_version(version_type version) if (REALM_LIKELY(resume_upload)) { // Since the deactivation process has not been initiated, the UNBIND // message cannot have been sent unless an ERROR message was received. - REALM_ASSERT(m_error_message_received || !m_unbind_message_sent); - if (m_ident_message_sent && !m_error_message_received) + REALM_ASSERT(m_suspended || !m_unbind_message_sent); + if (m_ident_message_sent && !m_suspended) ensure_enlisted_to_send(); // Throws } } @@ -1379,8 +1379,8 @@ inline void ClientImpl::Session::request_download_completion_notification() // Since the deactivation process has not been initiated, the UNBIND message // cannot have been sent unless an ERROR message was received. - REALM_ASSERT(m_error_message_received || !m_unbind_message_sent); - if (m_ident_message_sent && !m_error_message_received) + REALM_ASSERT(m_suspended || !m_unbind_message_sent); + if (m_ident_message_sent && !m_suspended) ensure_enlisted_to_send(); // Throws } @@ -1419,7 +1419,7 @@ inline bool ClientImpl::Session::have_client_file_ident() const noexcept inline bool ClientImpl::Session::unbind_process_complete() const noexcept { - return (m_unbind_message_sent_2 && (m_error_message_received || m_unbound_message_received)); + return (m_unbind_message_sent_2 && (m_suspended || m_error_message_received || m_unbound_message_received)); } inline void ClientImpl::Session::connection_established(bool fast_reconnect) diff --git a/test/object-store/sync/flx_sync.cpp b/test/object-store/sync/flx_sync.cpp index 806e9647a75..091a3d54eed 100644 --- a/test/object-store/sync/flx_sync.cpp +++ b/test/object-store/sync/flx_sync.cpp @@ -594,8 +594,8 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { .get(); REQUIRE(actual == sync::SubscriptionSet::State::Complete); }) - ->on_post_reset([&, err_future = std::move(error_future)](SharedRealm local_realm) { - auto sync_error = std::move(err_future).get(); + ->on_post_reset([&, err_future = std::move(error_future)](SharedRealm local_realm) mutable { + auto sync_error = wait_for_future(std::move(err_future)).get(); REQUIRE(before_reset_count == 1); REQUIRE(after_reset_count == 0); REQUIRE(sync_error.get_system_error() == @@ -641,8 +641,8 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { }; reset_utils::TestClientReset::Callback verify_post_reset_state = [&, err_future = std::move(error_future)]( - SharedRealm local_realm) { - auto sync_error = std::move(err_future).get(); + SharedRealm local_realm) mutable { + auto sync_error = wait_for_future(std::move(err_future)).get(); REQUIRE(before_reset_count == 1); REQUIRE(after_reset_count == 0); REQUIRE(sync_error.get_system_error() == @@ -670,7 +670,7 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { auto&& [error_future2, err_handler2] = make_error_handler(); config_copy.sync_config->error_handler = err_handler2; auto realm_post_reset = Realm::get_shared_realm(config_copy); - auto sync_error = std::move(error_future2).get(); + auto sync_error = wait_for_future(std::move(error_future2)).get(); REQUIRE(before_reset_count == 2); REQUIRE(after_reset_count == 0); REQUIRE(sync_error.get_system_error() == @@ -693,7 +693,7 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { }; config_copy.sync_config->notify_after_client_reset = reset_handler; auto realm_post_reset = Realm::get_shared_realm(config_copy); - ClientResyncMode mode = client_reset_future.get(); + ClientResyncMode mode = wait_for_future(std::move(client_reset_future)).get(); REQUIRE(mode == ClientResyncMode::DiscardLocal); realm_post_reset->refresh(); auto table = realm_post_reset->read_group().get_table("class_TopLevel"); @@ -715,11 +715,11 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { ->make_remote_changes([&](SharedRealm remote_realm) { add_subscription_for_new_object(remote_realm, str_field_value, remote_added_int); }) - ->on_post_reset([&, client_reset_future = std::move(reset_future)](SharedRealm local_realm) { - ClientResyncMode mode = client_reset_future.get(); + ->on_post_reset([&, client_reset_future = std::move(reset_future)](SharedRealm local_realm) mutable { + ClientResyncMode mode = wait_for_future(std::move(client_reset_future)).get(); REQUIRE(mode == ClientResyncMode::DiscardLocal); auto subs = local_realm->get_latest_subscription_set(); - subs.get_state_change_notification(sync::SubscriptionSet::State::Complete).get(); + wait_for_future(subs.get_state_change_notification(sync::SubscriptionSet::State::Complete)).get(); local_realm->refresh(); auto table = local_realm->read_group().get_table("class_TopLevel"); auto queryable_str_field = table->get_column_key("queryable_str_field"); @@ -736,7 +736,7 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { add_subscription_for_new_object(local_realm, str_field_value, local_added_int); auto latest_subs = local_realm->get_latest_subscription_set(); REQUIRE(latest_subs.version() > subs.version()); - latest_subs.get_state_change_notification(sync::SubscriptionSet::State::Complete).get(); + wait_for_future(latest_subs.get_state_change_notification(sync::SubscriptionSet::State::Complete)).get(); local_realm->refresh(); count_of_foo = count_queries_with_str(latest_subs, util::format("\"%1\"", str_field_value)); REQUIRE(count_of_foo == 1); @@ -807,8 +807,8 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { baas_sync_config.queryable_field_names->erase(it); app_session.admin_api.enable_sync(app_session.server_app_id, baas_sync_service.id, baas_sync_config); }) - ->on_post_reset([&, err_future = std::move(error_future)](SharedRealm) { - auto sync_error = std::move(err_future).get(); + ->on_post_reset([&, err_future = std::move(error_future)](SharedRealm) mutable { + auto sync_error = wait_for_future(std::move(err_future)).get(); // There is a race here depending on if the server produces a query error or responds to // the ident message first. We consider either error to be a sufficient outcome. if (sync_error.get_system_error() == diff --git a/test/object-store/sync/sync_test_utils.hpp b/test/object-store/sync/sync_test_utils.hpp index 7fe0cf1c2fe..887dd402a61 100644 --- a/test/object-store/sync/sync_test_utils.hpp +++ b/test/object-store/sync/sync_test_utils.hpp @@ -65,7 +65,7 @@ struct TimedFutureState : public util::AtomicRefCountBase { }; template -util::Future wait_for_future(util::Future&& input, std::chrono::milliseconds max_ms) +util::Future wait_for_future(util::Future&& input, std::chrono::milliseconds max_ms = std::chrono::seconds(60)) { auto pf = util::make_promise_future(); auto shared_state = util::make_bind>(std::move(pf.promise)); From b144140d387bbbdce3ddf380a809ece555481444 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 15:14:18 -0400 Subject: [PATCH 03/17] not crashing --- src/realm/object-store/sync/sync_session.cpp | 8 ++++++++ src/realm/sync/client.cpp | 18 ++++++++---------- src/realm/sync/config.hpp | 1 + src/realm/sync/network/default_socket.cpp | 14 ++++++++++++-- src/realm/sync/network/websocket.cpp | 2 +- src/realm/sync/noinst/client_impl_base.cpp | 3 ++- test/object-store/sync/flx_sync.cpp | 4 ++-- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/realm/object-store/sync/sync_session.cpp b/src/realm/object-store/sync/sync_session.cpp index c4817f8e16e..daf39882173 100644 --- a/src/realm/object-store/sync/sync_session.cpp +++ b/src/realm/object-store/sync/sync_session.cpp @@ -718,6 +718,13 @@ void SyncSession::handle_error(sync::SessionErrorInfo error) } } + // If the websocket was closed cleanly or if the socket disappeared, don't notify the user as an error + // since the sync client will retry. + if (error_code == sync::websocket::WebSocketError::websocket_ok || + error_code == sync::websocket::WebSocketError::websocket_abnormal_closure) { + return; + } + // Surface a simplified websocket error to the user. auto simplified_error = sync::websocket::get_simplified_websocket_error( static_cast(error_code.value())); @@ -839,6 +846,7 @@ void SyncSession::create_sync_session() sync::Session::Config session_config; session_config.signed_user_token = sync_config.user->access_token(); + session_config.user_id = sync_config.user->identity(); session_config.realm_identifier = sync_config.partition_value; session_config.verify_servers_ssl_certificate = sync_config.client_validate_ssl; session_config.ssl_trust_certificate_path = sync_config.ssl_trust_certificate_path; diff --git a/src/realm/sync/client.cpp b/src/realm/sync/client.cpp index 14e592cd042..fe0c2f5fe2b 100644 --- a/src/realm/sync/client.cpp +++ b/src/realm/sync/client.cpp @@ -326,8 +326,6 @@ class SessionWrapper final : public util::AtomicRefCountBase, public SyncTransac std::int_fast64_t m_staged_upload_mark = 0, m_staged_download_mark = 0; std::int_fast64_t m_reached_upload_mark = 0, m_reached_download_mark = 0; - void do_initiate(ProtocolEnvelope, std::string server_address, port_type server_port); - void on_sync_progress(); void on_upload_completion(); void on_download_completion(); @@ -976,6 +974,10 @@ SyncClientHookAction SessionImpl::call_debug_hook(const SyncClientHookData& data REALM_ASSERT(!err_processing_err); return SyncClientHookAction::EarlyReturn; } + case realm::SyncClientHookAction::TriggerReconnect: { + get_connection().voluntary_disconnect(); + return SyncClientHookAction::EarlyReturn; + } default: return action; } @@ -1244,7 +1246,10 @@ SessionWrapper::set_connection_state_change_listener(util::UniqueFunctionget_appservices_request_id(); !coid.empty()) { - logger.info("Connected to app services with request id: \"%1\"", coid); + logger.info("Connected to app services with request id: \"%1\" for user \"%2\"", coid, + m_server_endpoint.user_id); } milliseconds_type now = monotonic_clock_now(); diff --git a/test/object-store/sync/flx_sync.cpp b/test/object-store/sync/flx_sync.cpp index 0e8ce18223c..8991c3c824c 100644 --- a/test/object-store/sync/flx_sync.cpp +++ b/test/object-store/sync/flx_sync.cpp @@ -2493,7 +2493,7 @@ TEST_CASE("flx: bootstraps contain all changes", "[sync][flx][app]") { REQUIRE(table->find_primary_key(bar_obj_id)); REQUIRE_FALSE(table->find_primary_key(bizz_obj_id)); - return SyncClientHookAction::SuspendWithRetryableError; + return SyncClientHookAction::TriggerReconnect; }; auto problem_realm = Realm::get_shared_realm(triggered_config); @@ -2738,7 +2738,7 @@ TEST_CASE("flx: compensating write errors get re-sent across sessions", "[sync][ REQUIRE_FALSE(data.error_info->compensating_writes.empty()); promise.get_promise().emplace_value(); - return SyncClientHookAction::SuspendWithRetryableError; + return SyncClientHookAction::TriggerReconnect; }; auto realm = Realm::get_shared_realm(config); From a17958572bb37f9ce0231255b5c27a3626893261 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 16:22:04 -0400 Subject: [PATCH 04/17] make tests that assume only one connection force reconnects --- .../object-store/sync/impl/sync_client.hpp | 5 ++ src/realm/object-store/sync/sync_manager.cpp | 5 ++ src/realm/object-store/sync/sync_manager.hpp | 6 +++ src/realm/sync/client.cpp | 49 +++++++++++++++++++ src/realm/sync/client.hpp | 3 ++ src/realm/sync/noinst/client_impl_base.hpp | 4 +- test/object-store/sync/app.cpp | 2 + 7 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/realm/object-store/sync/impl/sync_client.hpp b/src/realm/object-store/sync/impl/sync_client.hpp index ef0fc056d62..5459c50afae 100644 --- a/src/realm/object-store/sync/impl/sync_client.hpp +++ b/src/realm/object-store/sync/impl/sync_client.hpp @@ -99,6 +99,11 @@ struct SyncClient { m_client.shutdown(); } + void voluntary_disconnect_all_connections() + { + m_client.voluntary_disconnect_all_connections(); + } + std::unique_ptr make_session(std::shared_ptr db, std::shared_ptr flx_sub_store, sync::Session::Config config) diff --git a/src/realm/object-store/sync/sync_manager.cpp b/src/realm/object-store/sync/sync_manager.cpp index 9b195e83d1e..311f74fa7cc 100644 --- a/src/realm/object-store/sync/sync_manager.cpp +++ b/src/realm/object-store/sync/sync_manager.cpp @@ -765,3 +765,8 @@ void SyncManager::close_all_sessions() get_sync_client().wait_for_session_terminations(); } + +void SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(SyncManager& mgr) +{ + mgr.get_sync_client().voluntary_disconnect_all_connections(); +} diff --git a/src/realm/object-store/sync/sync_manager.hpp b/src/realm/object-store/sync/sync_manager.hpp index 87a219f2a24..25638363616 100644 --- a/src/realm/object-store/sync/sync_manager.hpp +++ b/src/realm/object-store/sync/sync_manager.hpp @@ -243,6 +243,12 @@ class SyncManager : public std::enable_shared_from_this { SyncManager(const SyncManager&) = delete; SyncManager& operator=(const SyncManager&) = delete; + struct OnlyForTesting { + friend class TestHelper; + + static void voluntary_disconnect_all_connections(SyncManager&); + }; + protected: friend class SyncUser; friend class SyncSesson; diff --git a/src/realm/sync/client.cpp b/src/realm/sync/client.cpp index fe0c2f5fe2b..75f643596f6 100644 --- a/src/realm/sync/client.cpp +++ b/src/realm/sync/client.cpp @@ -443,6 +443,51 @@ void ClientImpl::cancel_reconnect_delay() } +void ClientImpl::voluntary_disconnect_all_connections() +{ + auto done_pf = util::make_promise_future(); + post([this, promise = std::move(done_pf.promise)](Status status) mutable { + if (status == ErrorCodes::OperationAborted) { + return; + } + + REALM_ASSERT(status.is_ok()); + + try { + for (auto& p : m_server_slots) { + ServerSlot& slot = p.second; + if (m_one_connection_per_session) { + REALM_ASSERT(!slot.connection); + for (const auto& p : slot.alt_connections) { + ClientImpl::Connection& conn = *p.second; + if (conn.get_state() == ConnectionState::disconnected) { + continue; + } + conn.voluntary_disconnect(); + } + } + else { + REALM_ASSERT(slot.alt_connections.empty()); + if (!slot.connection) { + continue; + } + ClientImpl::Connection& conn = *slot.connection; + if (conn.get_state() == ConnectionState::disconnected) { + continue; + } + conn.voluntary_disconnect(); + } + } + } + catch (...) { + promise.set_error(exception_to_status()); + } + promise.emplace_value(); + }); + done_pf.future.get(); +} + + bool ClientImpl::wait_for_session_terminations_or_client_stopped() { // Thread safety required @@ -1925,6 +1970,10 @@ void Client::cancel_reconnect_delay() m_impl->cancel_reconnect_delay(); } +void Client::voluntary_disconnect_all_connections() +{ + m_impl->voluntary_disconnect_all_connections(); +} bool Client::wait_for_session_terminations_or_client_stopped() { diff --git a/src/realm/sync/client.hpp b/src/realm/sync/client.hpp index 3a3b9fadcd1..bd2a7acbed0 100644 --- a/src/realm/sync/client.hpp +++ b/src/realm/sync/client.hpp @@ -61,6 +61,9 @@ class Client { /// Thread-safe. void cancel_reconnect_delay(); + /// Forces all open connections to disconnect/reconnect. To be used in testing. + void voluntary_disconnect_all_connections(); + /// \brief Wait for session termination to complete. /// /// Wait for termination of all sessions whose termination was initiated diff --git a/src/realm/sync/noinst/client_impl_base.hpp b/src/realm/sync/noinst/client_impl_base.hpp index 561e72a0967..e63394a51f4 100644 --- a/src/realm/sync/noinst/client_impl_base.hpp +++ b/src/realm/sync/noinst/client_impl_base.hpp @@ -185,6 +185,7 @@ class ClientImpl { void cancel_reconnect_delay(); bool wait_for_session_terminations_or_client_stopped(); + void voluntary_disconnect_all_connections(); private: using connection_ident_type = std::int_fast64_t; @@ -439,6 +440,8 @@ class ClientImpl::Connection { void resume_active_sessions(); + void voluntary_disconnect(); + std::string get_active_appservices_connection_id(); Connection(ClientImpl&, connection_ident_type, ServerEndpoint, const std::string& authorization_header_name, @@ -506,7 +509,6 @@ class ClientImpl::Connection { void close_due_to_protocol_error(std::error_code, std::optional msg = std::nullopt); void close_due_to_client_side_error(std::error_code, std::optional msg, bool is_fatal); void close_due_to_server_side_error(ProtocolError, const ProtocolErrorInfo& info); - void voluntary_disconnect(); void involuntary_disconnect(const SessionErrorInfo& info); void disconnect(const SessionErrorInfo& info); void change_state_to_disconnected() noexcept; diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 6e672d9ac0b..7820663a234 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -2527,6 +2527,7 @@ TEST_CASE("app: sync integration", "[sync][app]") { } }; + SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(*sync_manager); sync_session->resume(); REQUIRE(!wait_for_download(*r)); @@ -2584,6 +2585,7 @@ TEST_CASE("app: sync integration", "[sync][app]") { } }; + SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(*sync_manager); sync_session->resume(); REQUIRE(wait_for_download(*r)); REQUIRE(!user1->is_logged_in()); From bafcb43ecfe69f0b54bf5722248397b024d639b1 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 16:31:57 -0400 Subject: [PATCH 05/17] clang format --- src/realm/sync/noinst/client_impl_base.cpp | 2 +- test/object-store/sync/flx_sync.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index cd002dff244..ed0339587cc 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -2631,7 +2631,7 @@ void Session::suspend(const SessionErrorInfo& info) // still in the Active state if (m_state == Active) { m_conn.one_less_active_unsuspended_session(); // Throws - on_suspended(info); // Throws + on_suspended(info); // Throws } if (info.try_again) { diff --git a/test/object-store/sync/flx_sync.cpp b/test/object-store/sync/flx_sync.cpp index 8991c3c824c..b53fe27e70d 100644 --- a/test/object-store/sync/flx_sync.cpp +++ b/test/object-store/sync/flx_sync.cpp @@ -708,7 +708,8 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { add_subscription_for_new_object(local_realm, str_field_value, local_added_int); auto latest_subs = local_realm->get_latest_subscription_set(); REQUIRE(latest_subs.version() > subs.version()); - wait_for_future(latest_subs.get_state_change_notification(sync::SubscriptionSet::State::Complete)).get(); + wait_for_future(latest_subs.get_state_change_notification(sync::SubscriptionSet::State::Complete)) + .get(); local_realm->refresh(); count_of_foo = count_queries_with_str(latest_subs, util::format("\"%1\"", str_field_value)); REQUIRE(count_of_foo == 1); From fcd4d2677aaf87f00b8b15ba3d1f2f44b30825a2 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 16:50:28 -0400 Subject: [PATCH 06/17] update test error code --- test/test_sync.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 2b3c26b2a6f..1ce63ae4ed6 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -4513,8 +4513,7 @@ TEST(Sync_ServerDiscardDeadConnections) BowlOfStonesSemaphore bowl; auto error_handler = [&](std::error_code ec, bool, const std::string&) { - bool valid_error = ec == sync::websocket::WebSocketError::websocket_read_error; - CHECK(valid_error); + CHECK_EQUAL(ec, websocket::make_error_code(sync::websocket::WebSocketError::websocket_abnormal_closure)); bowl.add_stone(); }; fixture.set_client_side_error_handler(std::move(error_handler)); From 9e54db2dbf2425e472c64694cefb3da3fda5bf85 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 17:05:25 -0400 Subject: [PATCH 07/17] undo error handling bits --- src/realm/object-store/sync/sync_session.cpp | 4 ++-- src/realm/sync/network/default_socket.cpp | 10 ---------- test/test_sync.cpp | 3 ++- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/realm/object-store/sync/sync_session.cpp b/src/realm/object-store/sync/sync_session.cpp index daf39882173..1b92d472d7c 100644 --- a/src/realm/object-store/sync/sync_session.cpp +++ b/src/realm/object-store/sync/sync_session.cpp @@ -720,8 +720,8 @@ void SyncSession::handle_error(sync::SessionErrorInfo error) // If the websocket was closed cleanly or if the socket disappeared, don't notify the user as an error // since the sync client will retry. - if (error_code == sync::websocket::WebSocketError::websocket_ok || - error_code == sync::websocket::WebSocketError::websocket_abnormal_closure) { + if (error_code == sync::websocket::WebSocketError::websocket_read_error || + error_code == sync::websocket::WebSocketError::websocket_write_error) { return; } diff --git a/src/realm/sync/network/default_socket.cpp b/src/realm/sync/network/default_socket.cpp index b27babcce00..47a67c22053 100644 --- a/src/realm/sync/network/default_socket.cpp +++ b/src/realm/sync/network/default_socket.cpp @@ -77,11 +77,6 @@ class DefaultWebSocketImpl final : public DefaultWebSocket, public Config { void websocket_read_error_handler(std::error_code ec) override { constexpr bool was_clean = false; - if (ec == util::make_error_code(util::MiscExtErrors::end_of_input)) { - websocket_error_and_close_handler( - was_clean, {make_error_code(WebSocketError::websocket_abnormal_closure), ec.message()}); - return; - } m_logger.error("Reading failed: %1", ec.message()); // Throws websocket_error_and_close_handler( was_clean, Status{make_error_code(WebSocketError::websocket_read_error), ec.message()}); @@ -89,11 +84,6 @@ class DefaultWebSocketImpl final : public DefaultWebSocket, public Config { void websocket_write_error_handler(std::error_code ec) override { constexpr bool was_clean = false; - if (ec == util::make_error_code(util::MiscExtErrors::end_of_input)) { - websocket_error_and_close_handler( - was_clean, {make_error_code(WebSocketError::websocket_abnormal_closure), ec.message()}); - return; - } m_logger.error("Writing failed: %1", ec.message()); // Throws websocket_error_and_close_handler( was_clean, Status{make_error_code(WebSocketError::websocket_write_error), ec.message()}); diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 1ce63ae4ed6..d0e3bdac068 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -4513,7 +4513,8 @@ TEST(Sync_ServerDiscardDeadConnections) BowlOfStonesSemaphore bowl; auto error_handler = [&](std::error_code ec, bool, const std::string&) { - CHECK_EQUAL(ec, websocket::make_error_code(sync::websocket::WebSocketError::websocket_abnormal_closure)); + bool valid_error = (ec == sync::websocket::WebSocketError::websocket_read_error); + CHECK(valid_error); bowl.add_stone(); }; fixture.set_client_side_error_handler(std::move(error_handler)); From cc0a75c82ed46727411df26992b903e37f9c165f Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 21:04:36 -0400 Subject: [PATCH 08/17] fix asan failure --- test/object-store/sync/flx_sync.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/object-store/sync/flx_sync.cpp b/test/object-store/sync/flx_sync.cpp index b53fe27e70d..f217ab57b4d 100644 --- a/test/object-store/sync/flx_sync.cpp +++ b/test/object-store/sync/flx_sync.cpp @@ -1246,7 +1246,7 @@ TEST_CASE("flx: interrupted bootstrap restarts/recovers on reconnect", "[sync][f session->close(); promise->emplace_value(); - return SyncClientHookAction::NoAction; + return SyncClientHookAction::TriggerReconnect; }; auto realm = Realm::get_shared_realm(config); @@ -1259,7 +1259,6 @@ TEST_CASE("flx: interrupted bootstrap restarts/recovers on reconnect", "[sync][f interrupted.get(); realm->sync_session()->shutdown_and_wait(); - realm->close(); } _impl::RealmCoordinator::assert_no_open_realms(); From 86a03d347407bc9af6ee136b74cd6ac2aeefcf2d Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 21:49:49 -0400 Subject: [PATCH 09/17] more asan failures --- test/object-store/sync/flx_sync.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/object-store/sync/flx_sync.cpp b/test/object-store/sync/flx_sync.cpp index f217ab57b4d..755825ba206 100644 --- a/test/object-store/sync/flx_sync.cpp +++ b/test/object-store/sync/flx_sync.cpp @@ -1845,7 +1845,7 @@ TEST_CASE("flx: bootstrap batching prevents orphan documents", "[sync][flx][app] if (data.query_version == 1 && data.batch_state == sync::DownloadBatchState::MoreToCome) { session->force_close(); promise->emplace_value(); - return SyncClientHookAction::EarlyReturn; + return SyncClientHookAction::TriggerReconnect; } return SyncClientHookAction::NoAction; }; @@ -1924,7 +1924,7 @@ TEST_CASE("flx: bootstrap batching prevents orphan documents", "[sync][flx][app] if (data.query_version == 1 && data.batch_state == sync::DownloadBatchState::LastInBatch) { session->force_close(); promise->emplace_value(); - return SyncClientHookAction::EarlyReturn; + return SyncClientHookAction::TriggerReconnect; } return SyncClientHookAction::NoAction; }; From 65f0bd835aed07cad4631f0e7a2a417ad18f49da Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 23 Mar 2023 23:42:00 -0400 Subject: [PATCH 10/17] more asan failures --- src/realm/sync/noinst/client_impl_base.cpp | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index ed0339587cc..1c1ca239505 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -2392,6 +2392,12 @@ void Session::receive_download_message(const SyncProgress& progress, std::uint_f DownloadBatchState batch_state, int64_t query_version, const ReceivedChangesets& received_changesets) { + // Ignore the message if the deactivation process has been initiated, + // because in that case, the associated Realm must not be accessed any + // longer. + if (m_state != Active) + return; + if (is_steady_state_download_message(batch_state, query_version)) { batch_state = DownloadBatchState::SteadyState; } @@ -2412,12 +2418,6 @@ void Session::receive_download_message(const SyncProgress& progress, std::uint_f return; } - // Ignore the message if the deactivation process has been initiated, - // because in that case, the associated Realm must not be accessed any - // longer. - if (m_state != Active) - return; - bool legal_at_this_time = (m_ident_message_sent && !m_error_message_received && !m_unbound_message_received); if (REALM_UNLIKELY(!legal_at_this_time)) { logger.error("Illegal message at this time"); @@ -2568,14 +2568,15 @@ std::error_code Session::receive_query_error_message(int error_code, std::string // deactivated upon return. std::error_code Session::receive_error_message(const ProtocolErrorInfo& info) { + // Ignore the message if the deactivation process has been initiated, + // because in that case, the associated Realm must not be accessed any + // longer. + if (m_state != Active) + return {}; // Success + logger.info("Received: ERROR \"%1\" (error_code=%2, try_again=%3, error_action=%4)", info.message, info.raw_error_code, info.try_again, info.server_requests_action); // Throws - auto debug_action = call_debug_hook(SyncClientHookEvent::ErrorMessageReceived, info); - if (debug_action == SyncClientHookAction::EarlyReturn) { - return {}; - } - bool legal_at_this_time = (m_bind_message_sent && !m_error_message_received && !m_unbound_message_received); if (REALM_UNLIKELY(!legal_at_this_time)) { logger.error("Illegal message at this time"); @@ -2593,6 +2594,10 @@ std::error_code Session::receive_error_message(const ProtocolErrorInfo& info) return ClientError::bad_error_code; } + auto debug_action = call_debug_hook(SyncClientHookEvent::ErrorMessageReceived, info); + if (debug_action == SyncClientHookAction::EarlyReturn) { + return {}; + } // For compensating write errors, we need to defer raising them to the SDK until after the server version // containing the compensating write has appeared in a download message. if (error_code == ProtocolError::compensating_write) { From e833eeef9bf632398998b6269cc6c5a2a2158740 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 24 Mar 2023 12:17:56 -0400 Subject: [PATCH 11/17] add evergreen variant --- CMakeLists.txt | 1 + evergreen/config.yml | 19 +++++++++++++++++++ src/realm/object-store/CMakeLists.txt | 4 ++++ src/realm/object-store/sync/sync_manager.hpp | 4 ++++ src/realm/sync/CMakeLists.txt | 4 ++++ src/realm/sync/client_base.hpp | 4 ++++ src/realm/sync/noinst/client_impl_base.cpp | 4 ++-- 7 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 202622630fa..ad098d29a32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -211,6 +211,7 @@ option(REALM_ENABLE_MEMDEBUG "Add additional memory checks" OFF) option(REALM_VALGRIND "Tell the test suite we are running with valgrind" OFF) option(REALM_METRICS "Enable various metric tracking" ON) option(REALM_INCLUDE_CERTS "Include a list of trust certificates in the build for SSL certificate verification" ${REALM_INCLUDE_CERTS_DEFAULT}) +option(REALM_ENABLE_SYNC_MULTPLEXING "Enables sync session multi-plexing by default" ON) set(REALM_MAX_BPNODE_SIZE "1000" CACHE STRING "Max B+ tree node size.") # Find dependencies diff --git a/evergreen/config.yml b/evergreen/config.yml index 12d013abbb2..c2887b4301e 100644 --- a/evergreen/config.yml +++ b/evergreen/config.yml @@ -97,6 +97,10 @@ functions: set_cmake_var realm_vars REALM_TEST_DURATION STRING "${long_running_test_duration}" fi + if [ -n "${enable_sync_multiplexing|}" ]; then + set_cmake_var realm_vars REALM_ENABLE_SYNC_MULTIPLEXING BOOL On + fi + set_cmake_var realm_vars REALM_BUILD_COMMANDLINE_TOOLS BOOL On set_cmake_var realm_vars REALM_ENABLE_ENCRYPTION BOOL On @@ -803,6 +807,21 @@ buildvariants: distros: - ubuntu2004-large +- name: ubuntu2004-session-multiplexing + display_name: "Ubuntu 20.04 x86_64 (Sync Multiplexing Enabled)" + run_on: ubuntu2004-large + expansions: + clang_url: "https://s3.amazonaws.com/static.realm.io/evergreen-assets/clang%2Bllvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz" + cmake_url: "https://s3.amazonaws.com/static.realm.io/evergreen-assets/cmake-3.20.3-linux-x86_64.tar.gz" + cmake_bindir: "./cmake_binaries/bin" + fetch_missing_dependencies: On + run_tests_against_baas: On + c_compiler: "./clang_binaries/bin/clang" + cxx_compiler: "./clang_binaries/bin/clang++" + enable_sync_multiplexing: On + tasks: + - name: compile_test + - name: ubuntu2004-encryption-tsan display_name: "Ubuntu 20.04 x86_64 (Clang 11 Encryption Enabled w/TSAN)" run_on: ubuntu2004-small diff --git a/src/realm/object-store/CMakeLists.txt b/src/realm/object-store/CMakeLists.txt index 14f3d0c6f5d..cdaa19932ca 100644 --- a/src/realm/object-store/CMakeLists.txt +++ b/src/realm/object-store/CMakeLists.txt @@ -166,6 +166,10 @@ else() target_sources(ObjectStore PRIVATE impl/generic/external_commit_helper.cpp) endif() +if(REALM_ENABLE_SYNC AND REALM_ENABLE_SYNC_MULTPLEXING) + target_compile_definitions(ObjectStore PUBLIC REALM_ENABLE_SYNC_MULTPLEXING=1) +endif() + if(REALM_ENABLE_SYNC OR REALM_ENABLE_SERVER) # needed to disable assertions in external/json target_compile_definitions(ObjectStore PUBLIC diff --git a/src/realm/object-store/sync/sync_manager.hpp b/src/realm/object-store/sync/sync_manager.hpp index 25638363616..ed3b0e829f5 100644 --- a/src/realm/object-store/sync/sync_manager.hpp +++ b/src/realm/object-store/sync/sync_manager.hpp @@ -76,7 +76,11 @@ struct SyncClientConfig { util::Optional> custom_encryption_key; ReconnectMode reconnect_mode = ReconnectMode::normal; // For internal sync-client testing only! +#if REALM_ENABLE_SYNC_MULTIPLEXING + bool multiplex_sessions = true; +#else bool multiplex_sessions = false; +#endif // The SyncSocket instance used by the Sync Client for event synchronization // and creating WebSockets. If not provided the default implementation will be used. diff --git a/src/realm/sync/CMakeLists.txt b/src/realm/sync/CMakeLists.txt index a4c43eb55d2..2d37ea796ce 100644 --- a/src/realm/sync/CMakeLists.txt +++ b/src/realm/sync/CMakeLists.txt @@ -88,6 +88,10 @@ set_target_properties(Sync PROPERTIES OUTPUT_NAME "realm-sync" ) +if(REALM_ENABLE_SYNC_MULTIPLEXING) + target_compile_definitions(Sync PUBLIC REALM_ENABLE_SYNC_MULTIPLEXING=1) +endif() + target_link_libraries(Sync PUBLIC Storage) if(APPLE AND NOT REALM_FORCE_OPENSSL) diff --git a/src/realm/sync/client_base.hpp b/src/realm/sync/client_base.hpp index 342ab4edbbf..f1340d8d058 100644 --- a/src/realm/sync/client_base.hpp +++ b/src/realm/sync/client_base.hpp @@ -160,7 +160,11 @@ struct ClientConfig { /// /// FIXME: This setting needs to be true for now, due to limitations in /// the load balancer. +#if REALM_ENABLE_SYNC_MULTIPLEXING + bool one_connection_per_session = false; +#else bool one_connection_per_session = true; +#endif /// Do not access the local file system. Sessions will act as if /// initiated on behalf of an empty (or nonexisting) local Realm diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index 1c1ca239505..24cccb25db5 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -132,7 +132,7 @@ ClientImpl::ClientImpl(ClientConfig config) , logger{*logger_ptr} , m_reconnect_mode{config.reconnect_mode} , m_connect_timeout{config.connect_timeout} - , m_connection_linger_time{config.connection_linger_time} + , m_connection_linger_time{config.one_connection_per_session ? 0 : config.connection_linger_time} , m_ping_keepalive_period{config.ping_keepalive_period} , m_pong_keepalive_timeout{config.pong_keepalive_timeout} , m_fast_reconnect_limit{config.fast_reconnect_limit} @@ -144,7 +144,7 @@ ClientImpl::ClientImpl(ClientConfig config) , m_roundtrip_time_handler{std::move(config.roundtrip_time_handler)} , m_socket_provider{std::move(config.socket_provider)} , m_client_protocol{} // Throws - , m_one_connection_per_session{false} + , m_one_connection_per_session{config.one_connection_per_session} , m_random{} { // FIXME: Would be better if seeding was up to the application. From 82f53e83f3bed8389cbc897d76c4b5d52d9267a1 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 24 Mar 2023 13:06:21 -0400 Subject: [PATCH 12/17] allow unbound responses for suspended sessions --- src/realm/sync/noinst/client_impl_base.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index 24cccb25db5..9e8e0437c43 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -2540,8 +2540,9 @@ std::error_code Session::receive_unbound_message() // The fact that the UNBIND message has been sent, but an ERROR message has // not been received, implies that the deactivation process must have been - // initiated, so this session must be in the Deactivating state. - REALM_ASSERT(m_state == Deactivating); + // initiated, so this session must be in the Deactivating state or the session + // has been suspended because of a client side error. + REALM_ASSERT(m_state == Deactivating || m_suspended); m_unbound_message_received = true; From 47bdad3b87d1795f991c084fd2f0093b92c3f9f3 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 29 Mar 2023 16:23:02 -0400 Subject: [PATCH 13/17] ensure we only become de-activated when the SessionWrapper is finalized --- src/realm/sync/noinst/client_impl_base.cpp | 12 ++++++------ src/realm/sync/noinst/client_impl_base.hpp | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index 9e8e0437c43..01a3b77f5b0 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -1856,6 +1856,7 @@ void Session::initiate_deactivation() void Session::complete_deactivation() { + REALM_ASSERT(m_state == Deactivating); m_state = Deactivated; logger.debug("Deactivation completed"); // Throws @@ -2547,7 +2548,7 @@ std::error_code Session::receive_unbound_message() m_unbound_message_received = true; // Detect completion of the unbinding process - if (m_unbind_message_sent_2) { + if (m_unbind_message_send_complete && m_state == Deactivating) { // The deactivation process completes when the unbinding process // completes. complete_deactivation(); // Throws @@ -2620,11 +2621,10 @@ void Session::suspend(const SessionErrorInfo& info) m_suspended = true; // Detect completion of the unbinding process - if (m_unbind_message_sent_2) { - // The fact that the UNBIND message has been sent, but an ERROR message - // has not been received, implies that the deactivation process must - // have been initiated, so this session must be in the Deactivating - // state. + if (m_unbind_message_send_complete && m_error_message_received) { + // The fact that the UNBIND message has been sent, but we are not being suspended because + // we received an ERROR message implies that the deactivation process must + // have been initiated, so this session must be in the Deactivating state. REALM_ASSERT(m_state == Deactivating); // The deactivation process completes when the unbinding process diff --git a/src/realm/sync/noinst/client_impl_base.hpp b/src/realm/sync/noinst/client_impl_base.hpp index e63394a51f4..9a695a834fc 100644 --- a/src/realm/sync/noinst/client_impl_base.hpp +++ b/src/realm/sync/noinst/client_impl_base.hpp @@ -986,12 +986,12 @@ class ClientImpl::Session { // These are reset when the session is activated, and again whenever the // connection is lost or the rebinding process is initiated. bool m_enlisted_to_send; - bool m_bind_message_sent; // Sending of BIND message has been initiated - bool m_ident_message_sent; // Sending of IDENT message has been initiated - bool m_unbind_message_sent; // Sending of UNBIND message has been initiated - bool m_unbind_message_sent_2; // Sending of UNBIND message has been completed - bool m_error_message_received; // Session specific ERROR message received - bool m_unbound_message_received; // UNBOUND message received + bool m_bind_message_sent; // Sending of BIND message has been initiated + bool m_ident_message_sent; // Sending of IDENT message has been initiated + bool m_unbind_message_sent; // Sending of UNBIND message has been initiated + bool m_unbind_message_send_complete; // Sending of UNBIND message has been completed + bool m_error_message_received; // Session specific ERROR message received + bool m_unbound_message_received; // UNBOUND message received bool m_error_to_send; // True when there is a new FLX sync query we need to send to the server. @@ -1421,7 +1421,7 @@ inline bool ClientImpl::Session::have_client_file_ident() const noexcept inline bool ClientImpl::Session::unbind_process_complete() const noexcept { - return (m_unbind_message_sent_2 && (m_suspended || m_error_message_received || m_unbound_message_received)); + return (m_unbind_message_send_complete && (m_error_message_received || m_unbound_message_received)); } inline void ClientImpl::Session::connection_established(bool fast_reconnect) @@ -1471,14 +1471,14 @@ inline void ClientImpl::Session::message_sent() REALM_ASSERT(m_state == Active || m_state == Deactivating); // No message will be sent after the UNBIND message - REALM_ASSERT(!m_unbind_message_sent_2); + REALM_ASSERT(!m_unbind_message_send_complete); if (m_unbind_message_sent) { REALM_ASSERT(!m_enlisted_to_send); // If the sending of the UNBIND message has been initiated, this must be // the time when the sending of that message completes. - m_unbind_message_sent_2 = true; + m_unbind_message_send_complete = true; // Detect the completion of the unbinding process if (m_error_message_received || m_unbound_message_received) { @@ -1521,7 +1521,7 @@ inline void ClientImpl::Session::reset_protocol_state() noexcept m_error_to_send = false; m_ident_message_sent = false; m_unbind_message_sent = false; - m_unbind_message_sent_2 = false; + m_unbind_message_send_complete = false; m_error_message_received = false; m_unbound_message_received = false; m_client_error = util::none; From 7f55d0a3122efc999cdcd4dae701b75367993398 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 29 Mar 2023 16:40:06 -0400 Subject: [PATCH 14/17] fixups --- src/realm/sync/network/default_socket.cpp | 4 ++-- src/realm/sync/noinst/client_impl_base.cpp | 9 +-------- src/realm/sync/noinst/server/server.cpp | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/realm/sync/network/default_socket.cpp b/src/realm/sync/network/default_socket.cpp index f4473fa08dc..c4a2024165d 100644 --- a/src/realm/sync/network/default_socket.cpp +++ b/src/realm/sync/network/default_socket.cpp @@ -76,15 +76,15 @@ class DefaultWebSocketImpl final : public DefaultWebSocket, public Config { } void websocket_read_error_handler(std::error_code ec) override { - constexpr bool was_clean = false; m_logger.error("Reading failed: %1", ec.message()); // Throws + constexpr bool was_clean = false; websocket_error_and_close_handler( was_clean, Status{make_error_code(WebSocketError::websocket_read_error), ec.message()}); } void websocket_write_error_handler(std::error_code ec) override { - constexpr bool was_clean = false; m_logger.error("Writing failed: %1", ec.message()); // Throws + constexpr bool was_clean = false; websocket_error_and_close_handler( was_clean, Status{make_error_code(WebSocketError::websocket_write_error), ec.message()}); } diff --git a/src/realm/sync/noinst/client_impl_base.cpp b/src/realm/sync/noinst/client_impl_base.cpp index 01a3b77f5b0..84ebeaff55a 100644 --- a/src/realm/sync/noinst/client_impl_base.cpp +++ b/src/realm/sync/noinst/client_impl_base.cpp @@ -925,8 +925,7 @@ void Connection::handle_connection_established() // TODO(RCORE-1380) get this information in-band rather than from the websocket. if (auto coid = m_websocket->get_appservices_request_id(); !coid.empty()) { - logger.info("Connected to app services with request id: \"%1\" for user \"%2\"", coid, - m_server_endpoint.user_id); + logger.info("Connected to app services with request id: \"%1\"", coid); } milliseconds_type now = monotonic_clock_now(); @@ -2570,12 +2569,6 @@ std::error_code Session::receive_query_error_message(int error_code, std::string // deactivated upon return. std::error_code Session::receive_error_message(const ProtocolErrorInfo& info) { - // Ignore the message if the deactivation process has been initiated, - // because in that case, the associated Realm must not be accessed any - // longer. - if (m_state != Active) - return {}; // Success - logger.info("Received: ERROR \"%1\" (error_code=%2, try_again=%3, error_action=%4)", info.message, info.raw_error_code, info.try_again, info.server_requests_action); // Throws diff --git a/src/realm/sync/noinst/server/server.cpp b/src/realm/sync/noinst/server/server.cpp index 929fcfb3097..834255d9d7b 100644 --- a/src/realm/sync/noinst/server/server.cpp +++ b/src/realm/sync/noinst/server/server.cpp @@ -1922,7 +1922,7 @@ class HTTPConnection { void add_common_http_response_headers(HTTPResponse& response) { response.headers["Server"] = "RealmSync/" REALM_VERSION_STRING; // Throws - // This isn't a real X-Appservices-Request-Id, but it should + // This isn't a real X-Appservices-Request-Id, but it should be enough to test with response.headers["X-Appservices-Request-Id"] = m_appservices_request_id.to_string(); } From f2436eec194d87e543712d334ef1f846dc8c1470 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 6 Apr 2023 16:02:02 -0400 Subject: [PATCH 15/17] fixes from review --- src/realm/sync/client.cpp | 2 ++ src/realm/sync/client_base.hpp | 6 +----- src/realm/sync/noinst/client_impl_base.hpp | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/realm/sync/client.cpp b/src/realm/sync/client.cpp index 75f643596f6..795c2d7098e 100644 --- a/src/realm/sync/client.cpp +++ b/src/realm/sync/client.cpp @@ -481,6 +481,7 @@ void ClientImpl::voluntary_disconnect_all_connections() } catch (...) { promise.set_error(exception_to_status()); + return; } promise.emplace_value(); }); @@ -1822,6 +1823,7 @@ std::string SessionWrapper::get_appservices_connection_id() if (!self->m_sess) { promise.set_error({ErrorCodes::RuntimeError, "session already finalized"}); + return; } promise.emplace_value(self->m_sess->get_connection().get_active_appservices_connection_id()); diff --git a/src/realm/sync/client_base.hpp b/src/realm/sync/client_base.hpp index f1340d8d058..c0890f63078 100644 --- a/src/realm/sync/client_base.hpp +++ b/src/realm/sync/client_base.hpp @@ -155,11 +155,7 @@ struct ClientConfig { /// For testing purposes only. ReconnectMode reconnect_mode = ReconnectMode::normal; - /// Create a separate connection for each session. For testing purposes - /// only. - /// - /// FIXME: This setting needs to be true for now, due to limitations in - /// the load balancer. + /// Create a separate connection for each session. #if REALM_ENABLE_SYNC_MULTIPLEXING bool one_connection_per_session = false; #else diff --git a/src/realm/sync/noinst/client_impl_base.hpp b/src/realm/sync/noinst/client_impl_base.hpp index 9a695a834fc..b1fd30a95ca 100644 --- a/src/realm/sync/noinst/client_impl_base.hpp +++ b/src/realm/sync/noinst/client_impl_base.hpp @@ -1358,7 +1358,8 @@ inline void ClientImpl::Session::recognize_sync_version(version_type version) bool resume_upload = do_recognize_sync_version(version); if (REALM_LIKELY(resume_upload)) { // Since the deactivation process has not been initiated, the UNBIND - // message cannot have been sent unless an ERROR message was received. + // message cannot have been sent unless the session was suspended due to + // an error. REALM_ASSERT(m_suspended || !m_unbind_message_sent); if (m_ident_message_sent && !m_suspended) ensure_enlisted_to_send(); // Throws From 28d65ed3eb07dd501df5684134afa9a4300414c5 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 21 Apr 2023 12:45:53 -0400 Subject: [PATCH 16/17] fix build config typo --- CMakeLists.txt | 2 +- src/realm/object-store/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 743b5b8791a..476957cde11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,7 +246,7 @@ option(REALM_ENABLE_MEMDEBUG "Add additional memory checks" OFF) option(REALM_VALGRIND "Tell the test suite we are running with valgrind" OFF) option(REALM_METRICS "Enable various metric tracking" ON) option(REALM_INCLUDE_CERTS "Include a list of trust certificates in the build for SSL certificate verification" ${REALM_INCLUDE_CERTS_DEFAULT}) -option(REALM_ENABLE_SYNC_MULTPLEXING "Enables sync session multi-plexing by default" ON) +option(REALM_ENABLE_SYNC_MULTIPLEXING "Enables sync session multi-plexing by default") set(REALM_MAX_BPNODE_SIZE "1000" CACHE STRING "Max B+ tree node size.") # Find dependencies diff --git a/src/realm/object-store/CMakeLists.txt b/src/realm/object-store/CMakeLists.txt index cdaa19932ca..32b42ee65ea 100644 --- a/src/realm/object-store/CMakeLists.txt +++ b/src/realm/object-store/CMakeLists.txt @@ -166,8 +166,8 @@ else() target_sources(ObjectStore PRIVATE impl/generic/external_commit_helper.cpp) endif() -if(REALM_ENABLE_SYNC AND REALM_ENABLE_SYNC_MULTPLEXING) - target_compile_definitions(ObjectStore PUBLIC REALM_ENABLE_SYNC_MULTPLEXING=1) +if(REALM_ENABLE_SYNC AND REALM_ENABLE_SYNC_MULTIPLEXING) + target_compile_definitions(ObjectStore PUBLIC REALM_ENABLE_SYNC_MULTIPLEXING=1) endif() if(REALM_ENABLE_SYNC OR REALM_ENABLE_SERVER) From c9def4c2e0f6ca4c9e8602d1c887927529209a84 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 21 Apr 2023 15:18:00 -0400 Subject: [PATCH 17/17] changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 857d5fad2af..f9845d9ec90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,9 @@ * None. ### Fixed -* ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) -* None. +* If session multiplexing was enabled in the sync client and multiple realms for multiple users were being synchronized, a connection authenticated for the wrong user could have been used, reuslting in a UserMismatch error from the server. ([PR #6320](https://github.com/realm/realm-core/pull/6320), since v10.0.0). +* If session multiplexing was enabled and an automatic client reset failed, it could cause all sessions to fail with a fatal ProtocolError rather than just the session that failed to client reset. This would mean that no other sync session would be able to be opened for up to an hour without restarting the app. ([PR #6320](https://github.com/realm/realm-core/pull/6320), since v11.5.0) +* If a DOWNLOAD message was received after a sync session was de-activated but before the UNBOUND message was received by the client, a use-after-free error may have occurred when the sync session tried to process the download messaage. So far this has only been reproducible if session multiplexing was enabled. ([PR #6320](https://github.com/realm/realm-core/pull/6320), since v12.9.0) ### Breaking changes * None.