You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

324 lines
12 KiB

  1. # Copyright 2016 OpenMarket Ltd
  2. # Copyright 2020 The Matrix.org Foundation C.I.C.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import logging
  16. import sys
  17. from typing import Dict, List
  18. from twisted.web.resource import Resource
  19. import synapse
  20. import synapse.events
  21. from synapse.api.urls import (
  22. CLIENT_API_PREFIX,
  23. FEDERATION_PREFIX,
  24. LEGACY_MEDIA_PREFIX,
  25. MEDIA_R0_PREFIX,
  26. MEDIA_V3_PREFIX,
  27. SERVER_KEY_PREFIX,
  28. )
  29. from synapse.app import _base
  30. from synapse.app._base import (
  31. handle_startup_exception,
  32. max_request_body_size,
  33. redirect_stdio_to_logs,
  34. register_start,
  35. )
  36. from synapse.config._base import ConfigError
  37. from synapse.config.homeserver import HomeServerConfig
  38. from synapse.config.logger import setup_logging
  39. from synapse.config.server import ListenerConfig
  40. from synapse.federation.transport.server import TransportLayerServer
  41. from synapse.http.server import JsonResource, OptionsResource
  42. from synapse.logging.context import LoggingContext
  43. from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
  44. from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
  45. from synapse.rest import ClientRestResource
  46. from synapse.rest.admin import register_servlets_for_media_repo
  47. from synapse.rest.health import HealthResource
  48. from synapse.rest.key.v2 import KeyResource
  49. from synapse.rest.synapse.client import build_synapse_client_resource_tree
  50. from synapse.rest.well_known import well_known_resource
  51. from synapse.server import HomeServer
  52. from synapse.storage.databases.main.account_data import AccountDataWorkerStore
  53. from synapse.storage.databases.main.appservice import (
  54. ApplicationServiceTransactionWorkerStore,
  55. ApplicationServiceWorkerStore,
  56. )
  57. from synapse.storage.databases.main.censor_events import CensorEventsStore
  58. from synapse.storage.databases.main.client_ips import ClientIpWorkerStore
  59. from synapse.storage.databases.main.deviceinbox import DeviceInboxWorkerStore
  60. from synapse.storage.databases.main.devices import DeviceWorkerStore
  61. from synapse.storage.databases.main.directory import DirectoryWorkerStore
  62. from synapse.storage.databases.main.e2e_room_keys import EndToEndRoomKeyStore
  63. from synapse.storage.databases.main.event_federation import EventFederationWorkerStore
  64. from synapse.storage.databases.main.event_push_actions import (
  65. EventPushActionsWorkerStore,
  66. )
  67. from synapse.storage.databases.main.events_worker import EventsWorkerStore
  68. from synapse.storage.databases.main.filtering import FilteringWorkerStore
  69. from synapse.storage.databases.main.keys import KeyStore
  70. from synapse.storage.databases.main.lock import LockStore
  71. from synapse.storage.databases.main.media_repository import MediaRepositoryStore
  72. from synapse.storage.databases.main.metrics import ServerMetricsStore
  73. from synapse.storage.databases.main.monthly_active_users import (
  74. MonthlyActiveUsersWorkerStore,
  75. )
  76. from synapse.storage.databases.main.presence import PresenceStore
  77. from synapse.storage.databases.main.profile import ProfileWorkerStore
  78. from synapse.storage.databases.main.push_rule import PushRulesWorkerStore
  79. from synapse.storage.databases.main.pusher import PusherWorkerStore
  80. from synapse.storage.databases.main.receipts import ReceiptsWorkerStore
  81. from synapse.storage.databases.main.registration import RegistrationWorkerStore
  82. from synapse.storage.databases.main.relations import RelationsWorkerStore
  83. from synapse.storage.databases.main.room import RoomWorkerStore
  84. from synapse.storage.databases.main.room_batch import RoomBatchStore
  85. from synapse.storage.databases.main.roommember import RoomMemberWorkerStore
  86. from synapse.storage.databases.main.search import SearchStore
  87. from synapse.storage.databases.main.session import SessionStore
  88. from synapse.storage.databases.main.signatures import SignatureWorkerStore
  89. from synapse.storage.databases.main.state import StateGroupWorkerStore
  90. from synapse.storage.databases.main.stats import StatsStore
  91. from synapse.storage.databases.main.stream import StreamWorkerStore
  92. from synapse.storage.databases.main.tags import TagsWorkerStore
  93. from synapse.storage.databases.main.transactions import TransactionWorkerStore
  94. from synapse.storage.databases.main.ui_auth import UIAuthWorkerStore
  95. from synapse.storage.databases.main.user_directory import UserDirectoryStore
  96. from synapse.storage.databases.main.user_erasure_store import UserErasureWorkerStore
  97. from synapse.util import SYNAPSE_VERSION
  98. from synapse.util.httpresourcetree import create_resource_tree
  99. logger = logging.getLogger("synapse.app.generic_worker")
  100. class GenericWorkerSlavedStore(
  101. # FIXME(#3714): We need to add UserDirectoryStore as we write directly
  102. # rather than going via the correct worker.
  103. UserDirectoryStore,
  104. StatsStore,
  105. UIAuthWorkerStore,
  106. EndToEndRoomKeyStore,
  107. PresenceStore,
  108. DeviceInboxWorkerStore,
  109. DeviceWorkerStore,
  110. TagsWorkerStore,
  111. AccountDataWorkerStore,
  112. CensorEventsStore,
  113. ClientIpWorkerStore,
  114. # KeyStore isn't really safe to use from a worker, but for now we do so and hope that
  115. # the races it creates aren't too bad.
  116. KeyStore,
  117. RoomWorkerStore,
  118. RoomBatchStore,
  119. DirectoryWorkerStore,
  120. PushRulesWorkerStore,
  121. ApplicationServiceTransactionWorkerStore,
  122. ApplicationServiceWorkerStore,
  123. ProfileWorkerStore,
  124. FilteringWorkerStore,
  125. MonthlyActiveUsersWorkerStore,
  126. MediaRepositoryStore,
  127. ServerMetricsStore,
  128. PusherWorkerStore,
  129. RoomMemberWorkerStore,
  130. RelationsWorkerStore,
  131. EventFederationWorkerStore,
  132. EventPushActionsWorkerStore,
  133. StateGroupWorkerStore,
  134. SignatureWorkerStore,
  135. UserErasureWorkerStore,
  136. ReceiptsWorkerStore,
  137. StreamWorkerStore,
  138. EventsWorkerStore,
  139. RegistrationWorkerStore,
  140. SearchStore,
  141. TransactionWorkerStore,
  142. LockStore,
  143. SessionStore,
  144. ):
  145. # Properties that multiple storage classes define. Tell mypy what the
  146. # expected type is.
  147. server_name: str
  148. config: HomeServerConfig
  149. class GenericWorkerServer(HomeServer):
  150. DATASTORE_CLASS = GenericWorkerSlavedStore # type: ignore
  151. def _listen_http(self, listener_config: ListenerConfig) -> None:
  152. assert listener_config.http_options is not None
  153. # We always include a health resource.
  154. resources: Dict[str, Resource] = {"/health": HealthResource()}
  155. for res in listener_config.http_options.resources:
  156. for name in res.names:
  157. if name == "metrics":
  158. resources[METRICS_PREFIX] = MetricsResource(RegistryProxy)
  159. elif name == "client":
  160. resource: Resource = ClientRestResource(self)
  161. resources[CLIENT_API_PREFIX] = resource
  162. resources.update(build_synapse_client_resource_tree(self))
  163. resources["/.well-known"] = well_known_resource(self)
  164. elif name == "federation":
  165. resources[FEDERATION_PREFIX] = TransportLayerServer(self)
  166. elif name == "media":
  167. if self.config.media.can_load_media_repo:
  168. media_repo = self.get_media_repository_resource()
  169. # We need to serve the admin servlets for media on the
  170. # worker.
  171. admin_resource = JsonResource(self, canonical_json=False)
  172. register_servlets_for_media_repo(self, admin_resource)
  173. resources.update(
  174. {
  175. MEDIA_R0_PREFIX: media_repo,
  176. MEDIA_V3_PREFIX: media_repo,
  177. LEGACY_MEDIA_PREFIX: media_repo,
  178. "/_synapse/admin": admin_resource,
  179. }
  180. )
  181. else:
  182. logger.warning(
  183. "A 'media' listener is configured but the media"
  184. " repository is disabled. Ignoring."
  185. )
  186. elif name == "health":
  187. # Skip loading, health resource is always included
  188. continue
  189. if name == "openid" and "federation" not in res.names:
  190. # Only load the openid resource separately if federation resource
  191. # is not specified since federation resource includes openid
  192. # resource.
  193. resources[FEDERATION_PREFIX] = TransportLayerServer(
  194. self, servlet_groups=["openid"]
  195. )
  196. if name in ["keys", "federation"]:
  197. resources[SERVER_KEY_PREFIX] = KeyResource(self)
  198. if name == "replication":
  199. resources[REPLICATION_PREFIX] = ReplicationRestResource(self)
  200. # Attach additional resources registered by modules.
  201. resources.update(self._module_web_resources)
  202. self._module_web_resources_consumed = True
  203. root_resource = create_resource_tree(resources, OptionsResource())
  204. _base.listen_http(
  205. listener_config,
  206. root_resource,
  207. self.version_string,
  208. max_request_body_size(self.config),
  209. self.tls_server_context_factory,
  210. reactor=self.get_reactor(),
  211. )
  212. def start_listening(self) -> None:
  213. for listener in self.config.worker.worker_listeners:
  214. if listener.type == "http":
  215. self._listen_http(listener)
  216. elif listener.type == "manhole":
  217. _base.listen_manhole(
  218. listener.bind_addresses,
  219. listener.port,
  220. manhole_settings=self.config.server.manhole_settings,
  221. manhole_globals={"hs": self},
  222. )
  223. elif listener.type == "metrics":
  224. if not self.config.metrics.enable_metrics:
  225. logger.warning(
  226. "Metrics listener configured, but "
  227. "enable_metrics is not True!"
  228. )
  229. else:
  230. _base.listen_metrics(
  231. listener.bind_addresses,
  232. listener.port,
  233. )
  234. else:
  235. logger.warning("Unsupported listener type: %s", listener.type)
  236. self.get_replication_command_handler().start_replication(self)
  237. def start(config_options: List[str]) -> None:
  238. try:
  239. config = HomeServerConfig.load_config("Synapse worker", config_options)
  240. except ConfigError as e:
  241. sys.stderr.write("\n" + str(e) + "\n")
  242. sys.exit(1)
  243. # For backwards compatibility let any of the old app names.
  244. assert config.worker.worker_app in (
  245. "synapse.app.appservice",
  246. "synapse.app.client_reader",
  247. "synapse.app.event_creator",
  248. "synapse.app.federation_reader",
  249. "synapse.app.federation_sender",
  250. "synapse.app.frontend_proxy",
  251. "synapse.app.generic_worker",
  252. "synapse.app.media_repository",
  253. "synapse.app.pusher",
  254. "synapse.app.synchrotron",
  255. "synapse.app.user_dir",
  256. )
  257. synapse.events.USE_FROZEN_DICTS = config.server.use_frozen_dicts
  258. synapse.util.caches.TRACK_MEMORY_USAGE = config.caches.track_memory_usage
  259. if config.server.gc_seconds:
  260. synapse.metrics.MIN_TIME_BETWEEN_GCS = config.server.gc_seconds
  261. hs = GenericWorkerServer(
  262. config.server.server_name,
  263. config=config,
  264. version_string=f"Synapse/{SYNAPSE_VERSION}",
  265. )
  266. setup_logging(hs, config, use_worker_options=True)
  267. try:
  268. hs.setup()
  269. # Ensure the replication streamer is always started in case we write to any
  270. # streams. Will no-op if no streams can be written to by this worker.
  271. hs.get_replication_streamer()
  272. except Exception as e:
  273. handle_startup_exception(e)
  274. register_start(_base.start, hs)
  275. # redirect stdio to the logs, if configured.
  276. if not hs.config.logging.no_redirect_stdio:
  277. redirect_stdio_to_logs()
  278. _base.start_worker_reactor("synapse-generic-worker", config)
  279. def main() -> None:
  280. with LoggingContext("main"):
  281. start(sys.argv[1:])
  282. if __name__ == "__main__":
  283. main()