Add CI for synapse_port_db (#6140)
This adds: * a test sqlite database * a configuration file for the sqlite database * a configuration file for a postgresql database (using the credentials in `.buildkite/docker-compose.pyXX.pgXX.yaml`) as well as a new script named `.buildkite/scripts/test_synapse_port_db.sh` that: 1. installs Synapse 2. updates the test sqlite database to the latest schema and runs background updates on it 3. creates an empty postgresql database 4. run the `synapse_port_db` script to migrate the test sqlite database to the empty postgresql database (with coverage) Step `2` is done via a new script located at `scripts-dev/update_database`. The test sqlite database is extracted from a SyTest run, so that it can be considered as an actual homeserver's database with actual data in it.pull/6270/head
parent
29207b4488
commit
14504ad573
|
@ -0,0 +1,19 @@
|
||||||
|
# Configuration file used for testing the 'synapse_port_db' script.
|
||||||
|
# Tells the script to connect to the postgresql database that will be available in the
|
||||||
|
# CI's Docker setup at the point where this file is considered.
|
||||||
|
server_name: "test"
|
||||||
|
|
||||||
|
report_stats: false
|
||||||
|
|
||||||
|
database:
|
||||||
|
name: "psycopg2"
|
||||||
|
args:
|
||||||
|
user: postgres
|
||||||
|
host: postgres
|
||||||
|
password: postgres
|
||||||
|
database: synapse
|
||||||
|
|
||||||
|
# Suppress the key server warning.
|
||||||
|
trusted_key_servers:
|
||||||
|
- server_name: "matrix.org"
|
||||||
|
suppress_key_server_warning: true
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test script for 'synapse_port_db', which creates a virtualenv, installs Synapse along
|
||||||
|
# with additional dependencies needed for the test (such as coverage or the PostgreSQL
|
||||||
|
# driver), update the schema of the test SQLite database and run background updates on it,
|
||||||
|
# create an empty test database in PostgreSQL, then run the 'synapse_port_db' script to
|
||||||
|
# test porting the SQLite database to the PostgreSQL database (with coverage).
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
cd `dirname $0`/../..
|
||||||
|
|
||||||
|
# Create a virtualenv and use it.
|
||||||
|
virtualenv env
|
||||||
|
source env/bin/activate
|
||||||
|
|
||||||
|
# Install dependencies for this test.
|
||||||
|
pip install psycopg2 coverage coverage-enable-subprocess
|
||||||
|
|
||||||
|
# Install Synapse itself. This won't update any libraries.
|
||||||
|
pip install -e .
|
||||||
|
|
||||||
|
# Make sure the SQLite3 database is using the latest schema and has no pending background update.
|
||||||
|
scripts-dev/update_database --database-config .buildkite/sqlite-config.yaml
|
||||||
|
|
||||||
|
# Create the PostgreSQL database.
|
||||||
|
PGPASSWORD=postgres createdb -h postgres -U postgres synapse
|
||||||
|
|
||||||
|
# Run the script
|
||||||
|
coverage run scripts/synapse_port_db --sqlite-database .buildkite/test_db.db --postgres-config .buildkite/postgres-config.yaml
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Configuration file used for testing the 'synapse_port_db' script.
|
||||||
|
# Tells the 'update_database' script to connect to the test SQLite database to upgrade its
|
||||||
|
# schema and run background updates on it.
|
||||||
|
server_name: "test"
|
||||||
|
|
||||||
|
report_stats: false
|
||||||
|
|
||||||
|
database:
|
||||||
|
name: "sqlite3"
|
||||||
|
args:
|
||||||
|
database: ".buildkite/test_db.db"
|
||||||
|
|
||||||
|
# Suppress the key server warning.
|
||||||
|
trusted_key_servers:
|
||||||
|
- server_name: "matrix.org"
|
||||||
|
suppress_key_server_warning: true
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
Add a CI job to test the `synapse_port_db` script.
|
|
@ -0,0 +1,125 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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 argparse
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from twisted.internet import defer, reactor
|
||||||
|
|
||||||
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
|
from synapse.metrics.background_process_metrics import run_as_background_process
|
||||||
|
from synapse.server import HomeServer
|
||||||
|
from synapse.storage.engines import create_engine
|
||||||
|
from synapse.storage import DataStore
|
||||||
|
from synapse.storage.prepare_database import prepare_database
|
||||||
|
|
||||||
|
logger = logging.getLogger("update_database")
|
||||||
|
|
||||||
|
|
||||||
|
class MockHomeserver(HomeServer):
|
||||||
|
DATASTORE_CLASS = DataStore
|
||||||
|
|
||||||
|
def __init__(self, config, database_engine, db_conn, **kwargs):
|
||||||
|
super(MockHomeserver, self).__init__(
|
||||||
|
config.server_name,
|
||||||
|
reactor=reactor,
|
||||||
|
config=config,
|
||||||
|
database_engine=database_engine,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
self.database_engine = database_engine
|
||||||
|
self.db_conn = db_conn
|
||||||
|
|
||||||
|
def get_db_conn(self):
|
||||||
|
return self.db_conn
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=(
|
||||||
|
"Updates a synapse database to the latest schema and runs background updates"
|
||||||
|
" on it."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
parser.add_argument("-v", action='store_true')
|
||||||
|
parser.add_argument(
|
||||||
|
"--database-config",
|
||||||
|
type=argparse.FileType('r'),
|
||||||
|
required=True,
|
||||||
|
help="A database config file for either a SQLite3 database or a PostgreSQL one.",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
logging_config = {
|
||||||
|
"level": logging.DEBUG if args.v else logging.INFO,
|
||||||
|
"format": "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s",
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.basicConfig(**logging_config)
|
||||||
|
|
||||||
|
# Load, process and sanity-check the config.
|
||||||
|
hs_config = yaml.safe_load(args.database_config)
|
||||||
|
|
||||||
|
if "database" not in hs_config:
|
||||||
|
sys.stderr.write("The configuration file must have a 'database' section.\n")
|
||||||
|
sys.exit(4)
|
||||||
|
|
||||||
|
config = HomeServerConfig()
|
||||||
|
config.parse_config_dict(hs_config, "", "")
|
||||||
|
|
||||||
|
# Create the database engine and a connection to it.
|
||||||
|
database_engine = create_engine(config.database_config)
|
||||||
|
db_conn = database_engine.module.connect(
|
||||||
|
**{
|
||||||
|
k: v
|
||||||
|
for k, v in config.database_config.get("args", {}).items()
|
||||||
|
if not k.startswith("cp_")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update the database to the latest schema.
|
||||||
|
prepare_database(db_conn, database_engine, config=config)
|
||||||
|
db_conn.commit()
|
||||||
|
|
||||||
|
# Instantiate and initialise the homeserver object.
|
||||||
|
hs = MockHomeserver(
|
||||||
|
config,
|
||||||
|
database_engine,
|
||||||
|
db_conn,
|
||||||
|
db_config=config.database_config,
|
||||||
|
)
|
||||||
|
# setup instantiates the store within the homeserver object.
|
||||||
|
hs.setup()
|
||||||
|
store = hs.get_datastore()
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def run_background_updates():
|
||||||
|
yield store.run_background_updates(sleep=False)
|
||||||
|
# Stop the reactor to exit the script once every background update is run.
|
||||||
|
reactor.stop()
|
||||||
|
|
||||||
|
# Apply all background updates on the database.
|
||||||
|
reactor.callWhenRunning(lambda: run_as_background_process(
|
||||||
|
"background_updates", run_background_updates
|
||||||
|
))
|
||||||
|
|
||||||
|
reactor.run()
|
||||||
|
|
|
@ -94,13 +94,16 @@ class BackgroundUpdateStore(SQLBaseStore):
|
||||||
self._all_done = False
|
self._all_done = False
|
||||||
|
|
||||||
def start_doing_background_updates(self):
|
def start_doing_background_updates(self):
|
||||||
run_as_background_process("background_updates", self._run_background_updates)
|
run_as_background_process("background_updates", self.run_background_updates)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _run_background_updates(self):
|
def run_background_updates(self, sleep=True):
|
||||||
logger.info("Starting background schema updates")
|
logger.info("Starting background schema updates")
|
||||||
while True:
|
while True:
|
||||||
yield self.hs.get_clock().sleep(self.BACKGROUND_UPDATE_INTERVAL_MS / 1000.0)
|
if sleep:
|
||||||
|
yield self.hs.get_clock().sleep(
|
||||||
|
self.BACKGROUND_UPDATE_INTERVAL_MS / 1000.0
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = yield self.do_next_background_update(
|
result = yield self.do_next_background_update(
|
||||||
|
|
Loading…
Reference in New Issue