Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Use dummy fallback engines if imports fail #12979

Merged
merged 5 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/12979.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a bug introduced in Synapse 1.60 where Synapse would fail to start if the `sqlite3` module was not available.
2 changes: 1 addition & 1 deletion synapse/storage/databases/main/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
)
from synapse.storage.databases.main.events_worker import EventCacheEntry
from synapse.storage.databases.main.search import SearchEntry
from synapse.storage.engines.postgres import PostgresEngine
from synapse.storage.engines import PostgresEngine
from synapse.storage.util.id_generators import AbstractStreamIdGenerator
from synapse.storage.util.sequence import SequenceGenerator
from synapse.types import JsonDict, StateMap, get_domain_from_id
Expand Down
38 changes: 34 additions & 4 deletions synapse/storage/engines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,35 @@
# 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.
from typing import Any, Mapping
from typing import Any, Mapping, NoReturn

from ._base import BaseDatabaseEngine, IncorrectDatabaseSetup
from .postgres import PostgresEngine
from .sqlite import Sqlite3Engine

# The classes `PostgresEngine` and `Sqlite3Engine` must always be importable, because
# we use `isinstance(engine, PostgresEngine)` to write different queries for postgres
# and sqlite. But the database driver modules are both optional: they may not be
# installed. To account for this, create dummy classes on import failure so we can
# still run `isinstance()` checks.
try:
from .postgres import PostgresEngine
except ImportError:

class PostgresEngine(BaseDatabaseEngine): # type: ignore[no-redef]
def __new__(cls, *args: object, **kwargs: object) -> NoReturn: # type: ignore[misc]
raise RuntimeError(
f"Cannot create {cls.__name__} -- psycopg2 module is not installed"
)


try:
from .sqlite import Sqlite3Engine
except ImportError:

class Sqlite3Engine(BaseDatabaseEngine): # type: ignore[no-redef]
def __new__(cls, *args: object, **kwargs: object) -> NoReturn: # type: ignore[misc]
raise RuntimeError(
f"Cannot create {cls.__name__} -- sqlite3 module is not installed"
)


def create_engine(database_config: Mapping[str, Any]) -> BaseDatabaseEngine:
Expand All @@ -30,4 +54,10 @@ def create_engine(database_config: Mapping[str, Any]) -> BaseDatabaseEngine:
raise RuntimeError("Unsupported database engine '%s'" % (name,))


__all__ = ["create_engine", "BaseDatabaseEngine", "IncorrectDatabaseSetup"]
__all__ = [
"create_engine",
"BaseDatabaseEngine",
"PostgresEngine",
"Sqlite3Engine",
"IncorrectDatabaseSetup",
]
24 changes: 10 additions & 14 deletions synapse/storage/engines/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import logging
from typing import TYPE_CHECKING, Any, Mapping, NoReturn, Optional, Tuple, cast

import psycopg2.extensions

from synapse.storage.engines._base import (
BaseDatabaseEngine,
IncorrectDatabaseSetup,
Expand All @@ -23,18 +25,14 @@
from synapse.storage.types import Cursor

if TYPE_CHECKING:
import psycopg2 # noqa: F401

from synapse.storage.database import LoggingDatabaseConnection


logger = logging.getLogger(__name__)


class PostgresEngine(BaseDatabaseEngine["psycopg2.connection"]):
class PostgresEngine(BaseDatabaseEngine[psycopg2.extensions.connection]):
def __init__(self, database_config: Mapping[str, Any]):
import psycopg2.extensions

super().__init__(psycopg2, database_config)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)

Expand Down Expand Up @@ -69,7 +67,9 @@ def get_db_locale(self, txn: Cursor) -> Tuple[str, str]:
return collation, ctype

def check_database(
self, db_conn: "psycopg2.connection", allow_outdated_version: bool = False
self,
db_conn: psycopg2.extensions.connection,
allow_outdated_version: bool = False,
) -> None:
# Get the version of PostgreSQL that we're using. As per the psycopg2
# docs: The number is formed by converting the major, minor, and
Expand Down Expand Up @@ -176,16 +176,14 @@ def supports_returning(self) -> bool:
return True

def is_deadlock(self, error: Exception) -> bool:
import psycopg2.extensions

if isinstance(error, psycopg2.DatabaseError):
# https://www.postgresql.org/docs/current/static/errcodes-appendix.html
# "40001" serialization_failure
# "40P01" deadlock_detected
return error.pgcode in ["40001", "40P01"]
return False

def is_connection_closed(self, conn: "psycopg2.connection") -> bool:
def is_connection_closed(self, conn: psycopg2.extensions.connection) -> bool:
return bool(conn.closed)

def lock_table(self, txn: Cursor, table: str) -> None:
Expand All @@ -205,18 +203,16 @@ def server_version(self) -> str:
else:
return "%i.%i.%i" % (numver / 10000, (numver % 10000) / 100, numver % 100)

def in_transaction(self, conn: "psycopg2.connection") -> bool:
import psycopg2.extensions

def in_transaction(self, conn: psycopg2.extensions.connection) -> bool:
return conn.status != psycopg2.extensions.STATUS_READY

def attempt_to_set_autocommit(
self, conn: "psycopg2.connection", autocommit: bool
self, conn: psycopg2.extensions.connection, autocommit: bool
) -> None:
return conn.set_session(autocommit=autocommit)

def attempt_to_set_isolation_level(
self, conn: "psycopg2.connection", isolation_level: Optional[int]
self, conn: psycopg2.extensions.connection, isolation_level: Optional[int]
) -> None:
if isolation_level is None:
isolation_level = self.default_isolation_level
Expand Down
3 changes: 1 addition & 2 deletions synapse/storage/prepare_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@

from synapse.config.homeserver import HomeServerConfig
from synapse.storage.database import LoggingDatabaseConnection
from synapse.storage.engines import BaseDatabaseEngine
from synapse.storage.engines.postgres import PostgresEngine
from synapse.storage.engines import BaseDatabaseEngine, PostgresEngine
from synapse.storage.schema import SCHEMA_COMPAT_VERSION, SCHEMA_VERSION
from synapse.storage.types import Cursor

Expand Down