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.
 
 
 
 
 
 

338 lines
13 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, TCPListenerConfig
  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.purge_events import PurgeEventsStore
  79. from synapse.storage.databases.main.push_rule import PushRulesWorkerStore
  80. from synapse.storage.databases.main.pusher import PusherWorkerStore
  81. from synapse.storage.databases.main.receipts import ReceiptsWorkerStore
  82. from synapse.storage.databases.main.registration import RegistrationWorkerStore
  83. from synapse.storage.databases.main.relations import RelationsWorkerStore
  84. from synapse.storage.databases.main.room import RoomWorkerStore
  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.task_scheduler import TaskSchedulerWorkerStore
  94. from synapse.storage.databases.main.transactions import TransactionWorkerStore
  95. from synapse.storage.databases.main.ui_auth import UIAuthWorkerStore
  96. from synapse.storage.databases.main.user_directory import UserDirectoryStore
  97. from synapse.storage.databases.main.user_erasure_store import UserErasureWorkerStore
  98. from synapse.util import SYNAPSE_VERSION
  99. from synapse.util.httpresourcetree import create_resource_tree
  100. logger = logging.getLogger("synapse.app.generic_worker")
  101. class GenericWorkerStore(
  102. # FIXME(https://github.com/matrix-org/synapse/issues/3714): We need to add
  103. # UserDirectoryStore as we write directly rather than going via the correct worker.
  104. UserDirectoryStore,
  105. StatsStore,
  106. UIAuthWorkerStore,
  107. EndToEndRoomKeyStore,
  108. PresenceStore,
  109. DeviceInboxWorkerStore,
  110. DeviceWorkerStore,
  111. TagsWorkerStore,
  112. AccountDataWorkerStore,
  113. CensorEventsStore,
  114. ClientIpWorkerStore,
  115. # KeyStore isn't really safe to use from a worker, but for now we do so and hope that
  116. # the races it creates aren't too bad.
  117. KeyStore,
  118. RoomWorkerStore,
  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. PurgeEventsStore,
  134. StateGroupWorkerStore,
  135. SignatureWorkerStore,
  136. UserErasureWorkerStore,
  137. ReceiptsWorkerStore,
  138. StreamWorkerStore,
  139. EventsWorkerStore,
  140. RegistrationWorkerStore,
  141. SearchStore,
  142. TransactionWorkerStore,
  143. LockStore,
  144. SessionStore,
  145. TaskSchedulerWorkerStore,
  146. ):
  147. # Properties that multiple storage classes define. Tell mypy what the
  148. # expected type is.
  149. server_name: str
  150. config: HomeServerConfig
  151. class GenericWorkerServer(HomeServer):
  152. DATASTORE_CLASS = GenericWorkerStore # type: ignore
  153. def _listen_http(self, listener_config: ListenerConfig) -> None:
  154. assert listener_config.http_options is not None
  155. # We always include a health resource.
  156. resources: Dict[str, Resource] = {"/health": HealthResource()}
  157. for res in listener_config.http_options.resources:
  158. for name in res.names:
  159. if name == "metrics":
  160. resources[METRICS_PREFIX] = MetricsResource(RegistryProxy)
  161. elif name == "client":
  162. resource: Resource = ClientRestResource(self)
  163. resources[CLIENT_API_PREFIX] = resource
  164. resources.update(build_synapse_client_resource_tree(self))
  165. resources["/.well-known"] = well_known_resource(self)
  166. elif name == "federation":
  167. resources[FEDERATION_PREFIX] = TransportLayerServer(self)
  168. elif name == "media":
  169. if self.config.media.can_load_media_repo:
  170. media_repo = self.get_media_repository_resource()
  171. # We need to serve the admin servlets for media on the
  172. # worker.
  173. admin_resource = JsonResource(self, canonical_json=False)
  174. register_servlets_for_media_repo(self, admin_resource)
  175. resources.update(
  176. {
  177. MEDIA_R0_PREFIX: media_repo,
  178. MEDIA_V3_PREFIX: media_repo,
  179. LEGACY_MEDIA_PREFIX: media_repo,
  180. "/_synapse/admin": admin_resource,
  181. }
  182. )
  183. else:
  184. logger.warning(
  185. "A 'media' listener is configured but the media"
  186. " repository is disabled. Ignoring."
  187. )
  188. elif name == "health":
  189. # Skip loading, health resource is always included
  190. continue
  191. if name == "openid" and "federation" not in res.names:
  192. # Only load the openid resource separately if federation resource
  193. # is not specified since federation resource includes openid
  194. # resource.
  195. resources[FEDERATION_PREFIX] = TransportLayerServer(
  196. self, servlet_groups=["openid"]
  197. )
  198. if name in ["keys", "federation"]:
  199. resources[SERVER_KEY_PREFIX] = KeyResource(self)
  200. if name == "replication":
  201. resources[REPLICATION_PREFIX] = ReplicationRestResource(self)
  202. # Attach additional resources registered by modules.
  203. resources.update(self._module_web_resources)
  204. self._module_web_resources_consumed = True
  205. root_resource = create_resource_tree(resources, OptionsResource())
  206. _base.listen_http(
  207. self,
  208. listener_config,
  209. root_resource,
  210. self.version_string,
  211. max_request_body_size(self.config),
  212. self.tls_server_context_factory,
  213. reactor=self.get_reactor(),
  214. )
  215. def start_listening(self) -> None:
  216. for listener in self.config.worker.worker_listeners:
  217. if listener.type == "http":
  218. self._listen_http(listener)
  219. elif listener.type == "manhole":
  220. if isinstance(listener, TCPListenerConfig):
  221. _base.listen_manhole(
  222. listener.bind_addresses,
  223. listener.port,
  224. manhole_settings=self.config.server.manhole_settings,
  225. manhole_globals={"hs": self},
  226. )
  227. else:
  228. raise ConfigError(
  229. "Can not using a unix socket for manhole at this time."
  230. )
  231. elif listener.type == "metrics":
  232. if not self.config.metrics.enable_metrics:
  233. logger.warning(
  234. "Metrics listener configured, but "
  235. "enable_metrics is not True!"
  236. )
  237. else:
  238. if isinstance(listener, TCPListenerConfig):
  239. _base.listen_metrics(
  240. listener.bind_addresses,
  241. listener.port,
  242. )
  243. else:
  244. raise ConfigError(
  245. "Can not use a unix socket for metrics at this time."
  246. )
  247. else:
  248. logger.warning("Unsupported listener type: %s", listener.type)
  249. self.get_replication_command_handler().start_replication(self)
  250. def start(config_options: List[str]) -> None:
  251. try:
  252. config = HomeServerConfig.load_config("Synapse worker", config_options)
  253. except ConfigError as e:
  254. sys.stderr.write("\n" + str(e) + "\n")
  255. sys.exit(1)
  256. # For backwards compatibility let any of the old app names.
  257. assert config.worker.worker_app in (
  258. "synapse.app.appservice",
  259. "synapse.app.client_reader",
  260. "synapse.app.event_creator",
  261. "synapse.app.federation_reader",
  262. "synapse.app.federation_sender",
  263. "synapse.app.frontend_proxy",
  264. "synapse.app.generic_worker",
  265. "synapse.app.media_repository",
  266. "synapse.app.pusher",
  267. "synapse.app.synchrotron",
  268. "synapse.app.user_dir",
  269. )
  270. synapse.events.USE_FROZEN_DICTS = config.server.use_frozen_dicts
  271. synapse.util.caches.TRACK_MEMORY_USAGE = config.caches.track_memory_usage
  272. if config.server.gc_seconds:
  273. synapse.metrics.MIN_TIME_BETWEEN_GCS = config.server.gc_seconds
  274. hs = GenericWorkerServer(
  275. config.server.server_name,
  276. config=config,
  277. version_string=f"Synapse/{SYNAPSE_VERSION}",
  278. )
  279. setup_logging(hs, config, use_worker_options=True)
  280. try:
  281. hs.setup()
  282. # Ensure the replication streamer is always started in case we write to any
  283. # streams. Will no-op if no streams can be written to by this worker.
  284. hs.get_replication_streamer()
  285. except Exception as e:
  286. handle_startup_exception(e)
  287. register_start(_base.start, hs)
  288. # redirect stdio to the logs, if configured.
  289. if not hs.config.logging.no_redirect_stdio:
  290. redirect_stdio_to_logs()
  291. _base.start_worker_reactor("synapse-generic-worker", config)
  292. def main() -> None:
  293. with LoggingContext("main"):
  294. start(sys.argv[1:])
  295. if __name__ == "__main__":
  296. main()