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.
 
 
 
 
 
 

139 lines
5.2 KiB

  1. # Copyright 2019 The Matrix.org Foundation C.I.C.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import logging
  15. from typing import TYPE_CHECKING, Generic, List, Optional, Type, TypeVar
  16. from synapse.storage._base import SQLBaseStore
  17. from synapse.storage.database import DatabasePool, make_conn
  18. from synapse.storage.databases.main.events import PersistEventsStore
  19. from synapse.storage.databases.state import StateGroupDataStore
  20. from synapse.storage.engines import create_engine
  21. from synapse.storage.prepare_database import prepare_database
  22. if TYPE_CHECKING:
  23. from synapse.server import HomeServer
  24. from synapse.storage.databases.main import DataStore
  25. logger = logging.getLogger(__name__)
  26. DataStoreT = TypeVar("DataStoreT", bound=SQLBaseStore, covariant=True)
  27. class Databases(Generic[DataStoreT]):
  28. """The various databases.
  29. These are low level interfaces to physical databases.
  30. Attributes:
  31. databases
  32. main
  33. state
  34. persist_events
  35. """
  36. databases: List[DatabasePool]
  37. main: "DataStore" # FIXME: https://github.com/matrix-org/synapse/issues/11165: actually an instance of `main_store_class`
  38. state: StateGroupDataStore
  39. persist_events: Optional[PersistEventsStore]
  40. def __init__(self, main_store_class: Type[DataStoreT], hs: "HomeServer"):
  41. # Note we pass in the main store class here as workers use a different main
  42. # store.
  43. self.databases = []
  44. main: Optional[DataStoreT] = None
  45. state: Optional[StateGroupDataStore] = None
  46. persist_events: Optional[PersistEventsStore] = None
  47. for database_config in hs.config.database.databases:
  48. db_name = database_config.name
  49. engine = create_engine(database_config.config)
  50. with make_conn(database_config, engine, "startup") as db_conn:
  51. logger.info("[database config %r]: Checking database server", db_name)
  52. engine.check_database(db_conn)
  53. logger.info(
  54. "[database config %r]: Preparing for databases %r",
  55. db_name,
  56. database_config.databases,
  57. )
  58. prepare_database(
  59. db_conn,
  60. engine,
  61. hs.config,
  62. databases=database_config.databases,
  63. )
  64. database = DatabasePool(hs, database_config, engine)
  65. if "main" in database_config.databases:
  66. logger.info(
  67. "[database config %r]: Starting 'main' database", db_name
  68. )
  69. # Sanity check we don't try and configure the main store on
  70. # multiple databases.
  71. if main:
  72. raise Exception("'main' data store already configured")
  73. main = main_store_class(database, db_conn, hs)
  74. # If we're on a process that can persist events also
  75. # instantiate a `PersistEventsStore`
  76. if hs.get_instance_name() in hs.config.worker.writers.events:
  77. persist_events = PersistEventsStore(hs, database, main, db_conn) # type: ignore[arg-type]
  78. if "state" in database_config.databases:
  79. logger.info(
  80. "[database config %r]: Starting 'state' database", db_name
  81. )
  82. # Sanity check we don't try and configure the state store on
  83. # multiple databases.
  84. if state:
  85. raise Exception("'state' data store already configured")
  86. state = StateGroupDataStore(database, db_conn, hs)
  87. db_conn.commit()
  88. self.databases.append(database)
  89. logger.info("[database config %r]: prepared", db_name)
  90. # Closing the context manager doesn't close the connection.
  91. # psycopg will close the connection when the object gets GCed, but *only*
  92. # if the PID is the same as when the connection was opened [1], and
  93. # it may not be if we fork in the meantime.
  94. #
  95. # [1]: https://github.com/psycopg/psycopg2/blob/2_8_5/psycopg/connection_type.c#L1378
  96. db_conn.close()
  97. # Sanity check that we have actually configured all the required stores.
  98. if not main:
  99. raise Exception("No 'main' database configured")
  100. if not state:
  101. raise Exception("No 'state' database configured")
  102. # We use local variables here to ensure that the databases do not have
  103. # optional types.
  104. self.main = main # type: ignore[assignment]
  105. self.state = state
  106. self.persist_events = persist_events