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

Add CI for synapse_port_db #6140

Merged
merged 21 commits into from Oct 28, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions .buildkite/postgres-config.yaml
@@ -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"
babolivier marked this conversation as resolved.
Show resolved Hide resolved

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
29 changes: 29 additions & 0 deletions .buildkite/scripts/test_synapse_port_db.sh
@@ -0,0 +1,29 @@
#!/bin/bash
richvdh marked this conversation as resolved.
Show resolved Hide resolved
#
# 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
babolivier marked this conversation as resolved.
Show resolved Hide resolved

# 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
16 changes: 16 additions & 0 deletions .buildkite/sqlite-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"
babolivier marked this conversation as resolved.
Show resolved Hide resolved

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 added .buildkite/test_db.db
Binary file not shown.
1 change: 1 addition & 0 deletions changelog.d/6140.misc
@@ -0,0 +1 @@
Add a CI job to test the `synapse_port_db` script.
125 changes: 125 additions & 0 deletions scripts-dev/update_database
@@ -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.
babolivier marked this conversation as resolved.
Show resolved Hide resolved
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():
babolivier marked this conversation as resolved.
Show resolved Hide resolved
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()

9 changes: 6 additions & 3 deletions synapse/storage/background_updates.py
Expand Up @@ -94,13 +94,16 @@ def __init__(self, db_conn, hs):
self._all_done = False

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
def _run_background_updates(self):
def run_background_updates(self, sleep=True):
logger.info("Starting background schema updates")
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:
result = yield self.do_next_background_update(
Expand Down