def prepare(self, reactor, clock, hs): db_config = hs.config.database.get_single_database() self.master_store = self.hs.get_datastore() self.storage = hs.get_storage() database = hs.get_datastores().databases[0] self.slaved_store = self.STORE_TYPE( database, make_conn(db_config, database.engine), self.hs) self.event_id = 0 server_factory = ReplicationStreamProtocolFactory(self.hs) self.streamer = hs.get_replication_streamer() # We now do some gut wrenching so that we have a client that is based # off of the slave store rather than the main store. self.replication_handler = ReplicationCommandHandler(self.hs) self.replication_handler._instance_name = "worker" self.replication_handler._replication_data_handler = ReplicationDataHandler( self.slaved_store) client_factory = DirectTcpReplicationClientFactory( self.hs, "client_name", self.replication_handler) client_factory.handler = self.replication_handler server = server_factory.buildProtocol(None) client = client_factory.buildProtocol(None) client.makeConnection(FakeTransport(server, reactor)) self.server_to_client_transport = FakeTransport(client, reactor) server.makeConnection(self.server_to_client_transport)
def prepare(self, reactor, clock, hs): # build a replication server server_factory = ReplicationStreamProtocolFactory(hs) self.streamer = hs.get_replication_streamer() self.server = server_factory.buildProtocol(None) # Make a new HomeServer object for the worker self.reactor.lookups["testserv"] = "1.2.3.4" self.worker_hs = self.setup_test_homeserver( http_client=None, homeserver_to_use=GenericWorkerServer, config=self._get_worker_hs_config(), reactor=self.reactor, ) # Since we use sqlite in memory databases we need to make sure the # databases objects are the same. self.worker_hs.get_datastore().db_pool = hs.get_datastore().db_pool self.test_handler = self._build_replication_data_handler() self.worker_hs.replication_data_handler = self.test_handler repl_handler = ReplicationCommandHandler(self.worker_hs) self.client = ClientReplicationStreamProtocol( self.worker_hs, "client", "test", clock, repl_handler, ) self._client_transport = None self._server_transport = None
def make_worker_hs(self, worker_app: str, extra_config: dict = {}, **kwargs) -> HomeServer: """Make a new worker HS instance, correctly connecting replcation stream to the master HS. Args: worker_app: Type of worker, e.g. `synapse.app.federation_sender`. extra_config: Any extra config to use for this instances. **kwargs: Options that get passed to `self.setup_test_homeserver`, useful to e.g. pass some mocks for things like `http_client` Returns: The new worker HomeServer instance. """ config = self._get_worker_hs_config() config["worker_app"] = worker_app config.update(extra_config) worker_hs = self.setup_test_homeserver( homeserverToUse=GenericWorkerServer, config=config, reactor=self.reactor, **kwargs) store = worker_hs.get_datastore() store.db_pool._db_pool = self.database_pool._db_pool repl_handler = ReplicationCommandHandler(worker_hs) client = ClientReplicationStreamProtocol( worker_hs, "client", "test", self.clock, repl_handler, ) server = self.server_factory.buildProtocol(None) client_transport = FakeTransport(server, self.reactor) client.makeConnection(client_transport) server_transport = FakeTransport(client, self.reactor) server.makeConnection(server_transport) # Set up a resource for the worker resource = ReplicationRestResource(self.hs) for servlet in self.servlets: servlet(worker_hs, resource) self._worker_hs_to_resource[worker_hs] = resource return worker_hs
def prepare(self, reactor, clock, hs): # build a replication server server_factory = ReplicationStreamProtocolFactory(hs) self.streamer = hs.get_replication_streamer() self.server: ServerReplicationStreamProtocol = server_factory.buildProtocol( IPv4Address("TCP", "127.0.0.1", 0)) # Make a new HomeServer object for the worker self.reactor.lookups["testserv"] = "1.2.3.4" self.worker_hs = self.setup_test_homeserver( federation_http_client=None, homeserver_to_use=GenericWorkerServer, config=self._get_worker_hs_config(), reactor=self.reactor, ) # Since we use sqlite in memory databases we need to make sure the # databases objects are the same. self.worker_hs.get_datastores().main.db_pool = hs.get_datastores( ).main.db_pool # Normally we'd pass in the handler to `setup_test_homeserver`, which would # eventually hit "Install @cache_in_self attributes" in tests/utils.py. # Unfortunately our handler wants a reference to the homeserver. That leaves # us with a chicken-and-egg problem. # We can workaround this: create the homeserver first, create the handler # and bodge it in after the fact. The bodging requires us to know the # dirty details of how `cache_in_self` works. We politely ask mypy to # ignore our dirty dealings. self.test_handler = self._build_replication_data_handler() self.worker_hs._replication_data_handler = self.test_handler # type: ignore[attr-defined] repl_handler = ReplicationCommandHandler(self.worker_hs) self.client = ClientReplicationStreamProtocol( self.worker_hs, "client", "test", clock, repl_handler, ) self._client_transport = None self._server_transport = None
def get_tcp_replication(self) -> ReplicationCommandHandler: return ReplicationCommandHandler(self)
def make_worker_hs(self, worker_app: str, extra_config: dict = {}, **kwargs) -> HomeServer: """Make a new worker HS instance, correctly connecting replcation stream to the master HS. Args: worker_app: Type of worker, e.g. `synapse.app.federation_sender`. extra_config: Any extra config to use for this instances. **kwargs: Options that get passed to `self.setup_test_homeserver`, useful to e.g. pass some mocks for things like `http_client` Returns: The new worker HomeServer instance. """ config = self._get_worker_hs_config() config["worker_app"] = worker_app config.update(extra_config) worker_hs = self.setup_test_homeserver( homeserver_to_use=GenericWorkerServer, config=config, reactor=self.reactor, **kwargs, ) # If the instance is in the `instance_map` config then workers may try # and send HTTP requests to it, so we register it with # `_handle_http_replication_attempt` like we do with the master HS. instance_name = worker_hs.get_instance_name() instance_loc = worker_hs.config.worker.instance_map.get(instance_name) if instance_loc: # Ensure the host is one that has a fake DNS entry. if instance_loc.host not in self.reactor.lookups: raise Exception( "Host does not have an IP for instance_map[%r].host = %r" % ( instance_name, instance_loc.host, )) self.reactor.add_tcp_client_callback( self.reactor.lookups[instance_loc.host], instance_loc.port, lambda: self._handle_http_replication_attempt( worker_hs, instance_loc.port), ) store = worker_hs.get_datastore() store.db_pool._db_pool = self.database_pool._db_pool # Set up TCP replication between master and the new worker if we don't # have Redis support enabled. if not worker_hs.config.redis_enabled: repl_handler = ReplicationCommandHandler(worker_hs) client = ClientReplicationStreamProtocol( worker_hs, "client", "test", self.clock, repl_handler, ) server = self.server_factory.buildProtocol(None) client_transport = FakeTransport(server, self.reactor) client.makeConnection(client_transport) server_transport = FakeTransport(client, self.reactor) server.makeConnection(server_transport) # Set up a resource for the worker resource = ReplicationRestResource(worker_hs) for servlet in self.servlets: servlet(worker_hs, resource) self._hs_to_site[worker_hs] = SynapseSite( logger_name="synapse.access.http.fake", site_tag="{}-{}".format(worker_hs.config.server.server_name, worker_hs.get_instance_name()), config=worker_hs.config.server.listeners[0], resource=resource, server_version_string="1", ) if worker_hs.config.redis.redis_enabled: worker_hs.get_tcp_replication().start_replication(worker_hs) return worker_hs
def build_tcp_replication(self): return ReplicationCommandHandler(self)
def get_replication_command_handler(self) -> ReplicationCommandHandler: return ReplicationCommandHandler(self)