|
- # Copyright 2019 The Matrix.org Foundation C.I.C.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
-
- import logging
- from typing import TYPE_CHECKING, Generic, List, Optional, Type, TypeVar
-
- from synapse.storage._base import SQLBaseStore
- from synapse.storage.database import DatabasePool, make_conn
- from synapse.storage.databases.main.events import PersistEventsStore
- from synapse.storage.databases.state import StateGroupDataStore
- from synapse.storage.engines import create_engine
- from synapse.storage.prepare_database import prepare_database
-
- if TYPE_CHECKING:
- from synapse.server import HomeServer
- from synapse.storage.databases.main import DataStore
-
- logger = logging.getLogger(__name__)
-
-
- DataStoreT = TypeVar("DataStoreT", bound=SQLBaseStore, covariant=True)
-
-
- class Databases(Generic[DataStoreT]):
- """The various databases.
-
- These are low level interfaces to physical databases.
-
- Attributes:
- databases
- main
- state
- persist_events
- """
-
- databases: List[DatabasePool]
- main: "DataStore" # FIXME: https://github.com/matrix-org/synapse/issues/11165: actually an instance of `main_store_class`
- state: StateGroupDataStore
- persist_events: Optional[PersistEventsStore]
-
- def __init__(self, main_store_class: Type[DataStoreT], hs: "HomeServer"):
- # Note we pass in the main store class here as workers use a different main
- # store.
-
- self.databases = []
- main: Optional[DataStoreT] = None
- state: Optional[StateGroupDataStore] = None
- persist_events: Optional[PersistEventsStore] = None
-
- for database_config in hs.config.database.databases:
- db_name = database_config.name
- engine = create_engine(database_config.config)
-
- with make_conn(database_config, engine, "startup") as db_conn:
- logger.info("[database config %r]: Checking database server", db_name)
- engine.check_database(db_conn)
-
- logger.info(
- "[database config %r]: Preparing for databases %r",
- db_name,
- database_config.databases,
- )
- prepare_database(
- db_conn,
- engine,
- hs.config,
- databases=database_config.databases,
- )
-
- database = DatabasePool(hs, database_config, engine)
-
- if "main" in database_config.databases:
- logger.info(
- "[database config %r]: Starting 'main' database", db_name
- )
-
- # Sanity check we don't try and configure the main store on
- # multiple databases.
- if main:
- raise Exception("'main' data store already configured")
-
- main = main_store_class(database, db_conn, hs)
-
- # If we're on a process that can persist events also
- # instantiate a `PersistEventsStore`
- if hs.get_instance_name() in hs.config.worker.writers.events:
- persist_events = PersistEventsStore(hs, database, main, db_conn) # type: ignore[arg-type]
-
- if "state" in database_config.databases:
- logger.info(
- "[database config %r]: Starting 'state' database", db_name
- )
-
- # Sanity check we don't try and configure the state store on
- # multiple databases.
- if state:
- raise Exception("'state' data store already configured")
-
- state = StateGroupDataStore(database, db_conn, hs)
-
- db_conn.commit()
-
- self.databases.append(database)
-
- logger.info("[database config %r]: prepared", db_name)
-
- # Closing the context manager doesn't close the connection.
- # psycopg will close the connection when the object gets GCed, but *only*
- # if the PID is the same as when the connection was opened [1], and
- # it may not be if we fork in the meantime.
- #
- # [1]: https://github.com/psycopg/psycopg2/blob/2_8_5/psycopg/connection_type.c#L1378
-
- db_conn.close()
-
- # Sanity check that we have actually configured all the required stores.
- if not main:
- raise Exception("No 'main' database configured")
-
- if not state:
- raise Exception("No 'state' database configured")
-
- # We use local variables here to ensure that the databases do not have
- # optional types.
- self.main = main # type: ignore[assignment]
- self.state = state
- self.persist_events = persist_events
|