Run database updates in a transaction (#8265)

Fixes: #6467
This commit is contained in:
Richard van der Hoff 2020-09-07 11:41:50 +01:00 committed by GitHub
parent 765437df54
commit 5b452df23b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 5 deletions

1
changelog.d/8265.bugfix Normal file
View file

@ -0,0 +1 @@
Fix logstanding bug which could lead to incomplete database upgrades on SQLite.

View file

@ -19,12 +19,15 @@ import logging
import os
import re
from collections import Counter
from typing import TextIO
from typing import Optional, TextIO
import attr
from synapse.config.homeserver import HomeServerConfig
from synapse.storage.engines import BaseDatabaseEngine
from synapse.storage.engines.postgres import PostgresEngine
from synapse.storage.types import Cursor
from synapse.storage.types import Connection, Cursor
from synapse.types import Collection
logger = logging.getLogger(__name__)
@ -47,7 +50,12 @@ class UpgradeDatabaseException(PrepareDatabaseException):
pass
def prepare_database(db_conn, database_engine, config, databases=["main", "state"]):
def prepare_database(
db_conn: Connection,
database_engine: BaseDatabaseEngine,
config: Optional[HomeServerConfig],
databases: Collection[str] = ["main", "state"],
):
"""Prepares a physical database for usage. Will either create all necessary tables
or upgrade from an older schema version.
@ -57,15 +65,24 @@ def prepare_database(db_conn, database_engine, config, databases=["main", "state
Args:
db_conn:
database_engine:
config (synapse.config.homeserver.HomeServerConfig|None):
config :
application config, or None if we are connecting to an existing
database which we expect to be configured already
databases (list[str]): The name of the databases that will be used
databases: The name of the databases that will be used
with this physical database. Defaults to all databases.
"""
try:
cur = db_conn.cursor()
# sqlite does not automatically start transactions for DDL / SELECT statements,
# so we start one before running anything. This ensures that any upgrades
# are either applied completely, or not at all.
#
# (psycopg2 automatically starts a transaction as soon as we run any statements
# at all, so this is redundant but harmless there.)
cur.execute("BEGIN TRANSACTION")
version_info = _get_or_create_schema_state(cur, database_engine)
if version_info: