[LNT] r248789 - Fix postgres unit tests by closing db connections.

Adam Nemet via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 7 23:27:44 PDT 2015


> On Sep 29, 2015, at 4:49 AM, Kristof Beyls via llvm-commits <llvm-commits at lists.llvm.org> wrote:
> 
> Author: kbeyls
> Date: Tue Sep 29 06:49:15 2015
> New Revision: 248789
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=248789&view=rev
> Log:
> Fix postgres unit tests by closing db connections.
> 
> When running the unit tests on a postgres database, the following error shows:
> 
> sqlalchemy.exc.TimeoutError: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30
> 
> We've also seen this error from time to time on the instance running at
> llvm.org/perf, and it was unclear what the exact reason was for this.  Now that
> we can run the unit tests on postgres, this shows that not closing the db
> connection at the end of a request is at least one of the reasons this error
> can be produced.
> 
> Differential Revision: http://reviews.llvm.org/D13164
> 
> 
> Modified:
>    lnt/trunk/lnt/lnttool/import_data.py
>    lnt/trunk/lnt/lnttool/main.py
>    lnt/trunk/lnt/lnttool/updatedb.py
>    lnt/trunk/lnt/lnttool/viewcomparison.py
>    lnt/trunk/lnt/server/db/v4db.py
>    lnt/trunk/lnt/server/ui/app.py
>    lnt/trunk/lnt/util/ImportData.py
>    lnt/trunk/lnt/util/ServerUtil.py
> 
> Modified: lnt/trunk/lnt/lnttool/import_data.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/lnttool/import_data.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/lnttool/import_data.py (original)
> +++ lnt/trunk/lnt/lnttool/import_data.py Tue Sep 29 06:49:15 2015
> @@ -3,6 +3,8 @@ import os, pprint, sys, time
> import lnt.formats
> import lnt.util.ImportData
> import lnt.server.instance
> +import contextlib
> +
> 
> def action_import(name, args):
>     """import test data into a database"""
> @@ -45,27 +47,26 @@ def action_import(name, args):
>     config = instance.config
> 
>     # Get the database.
> -    db = config.get_database(opts.database, echo=opts.show_sql)
> -
> -    # Load the database.
> -    success = True
> -    for file in args:
> -        result = lnt.util.ImportData.import_and_report(
> -            config, opts.database, db, file,
> -            opts.format, opts.commit, opts.show_sample_count,
> -            opts.no_email, opts.no_report)
> -
> -        success &= result.get('success', False)
> -        if opts.quiet:
> -            continue
> -
> -        if opts.show_raw_result:
> -            pprint.pprint(result)
> -        else:
> -            lnt.util.ImportData.print_report_result(result, sys.stdout,
> -                                                    sys.stderr,
> -                                                    opts.verbose)
> -
> -    if not success:
> -        raise SystemExit, 1
> +    with contextlib.closing(config.get_database(opts.database,
> +                                                echo=opts.show_sql)) as db:
> +        # Load the database.
> +        success = True
> +        for file in args:
> +            result = lnt.util.ImportData.import_and_report(
> +                config, opts.database, db, file,
> +                opts.format, opts.commit, opts.show_sample_count,
> +                opts.no_email, opts.no_report)
> +
> +            success &= result.get('success', False)
> +            if opts.quiet:
> +                continue
> +
> +            if opts.show_raw_result:
> +                pprint.pprint(result)
> +            else:
> +                lnt.util.ImportData.print_report_result(result, sys.stdout,
> +                                                        sys.stderr,
> +                                                        opts.verbose)
> 
> +        if not success:
> +            raise SystemExit, 1
> 
> Modified: lnt/trunk/lnt/lnttool/main.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/lnttool/main.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/lnttool/main.py (original)
> +++ lnt/trunk/lnt/lnttool/main.py Tue Sep 29 06:49:15 2015
> @@ -5,6 +5,7 @@ import os
> import sys
> import tempfile
> from optparse import OptionParser, OptionGroup
> +import contextlib
> 
> import werkzeug.contrib.profiler
> 
> @@ -293,57 +294,59 @@ def action_send_daily_report(name, args)
>     config = instance.config
> 
>     # Get the database.
> -    db = config.get_database(opts.database)
> +    with contextlib.closing(config.get_database(opts.database)) as db:
> 
> -    # Get the testsuite.
> -    ts = db.testsuite[opts.testsuite]
> +        # Get the testsuite.
> +        ts = db.testsuite[opts.testsuite]
> 
> -    if opts.today:
> -        date = datetime.datetime.utcnow()
> -    else:
> -        # Get a timestamp to use to derive the daily report to generate.
> -        latest = ts.query(ts.Run).\
> -            order_by(ts.Run.start_time.desc()).limit(1).first()
> -
> -        # If we found a run, use it's start time (rounded up to the next hour,
> -        # so we make sure it gets included).
> -        if latest:
> -            date = latest.start_time + datetime.timedelta(hours=1)
> -        else:
> -            # Otherwise, just use now.
> +        if opts.today:
>             date = datetime.datetime.utcnow()
> +        else:
> +            # Get a timestamp to use to derive the daily report to generate.
> +            latest = ts.query(ts.Run).\
> +                order_by(ts.Run.start_time.desc()).limit(1).first()
> +
> +            # If we found a run, use it's start time (rounded up to the next
> +            # hour, so we make sure it gets included).
> +            if latest:
> +                date = latest.start_time + datetime.timedelta(hours=1)
> +            else:
> +                # Otherwise, just use now.
> +                date = datetime.datetime.utcnow()
> +
> +        # Generate the daily report.
> +        note("building report data...")
> +        report = lnt.server.reporting.dailyreport.DailyReport(
> +            ts, year=date.year, month=date.month, day=date.day,
> +            day_start_offset_hours=date.hour, for_mail=True,
> +            num_prior_days_to_include=opts.days,
> +            filter_machine_regex=opts.filter_machine_regex)
> +        report.build()
> +
> +        note("generating HTML report...")
> +        ts_url = "%s/db_%s/v4/%s" \
> +            % (config.zorgURL, opts.database, opts.testsuite)
> +        subject = "Daily Report: %04d-%02d-%02d" % (
> +            report.year, report.month, report.day)
> +        html_report = report.render(ts_url, only_html_body=False)
> +
> +        if opts.subject_prefix is not None:
> +            subject = "%s %s" % (opts.subject_prefix, subject)
> +
> +        # Form the multipart email message.
> +        msg = email.mime.multipart.MIMEMultipart('alternative')
> +        msg['Subject'] = subject
> +        msg['From'] = opts.from_address
> +        msg['To'] = to_address
> +        msg.attach(email.mime.text.MIMEText(html_report, "html"))
> +
> +        # Send the report.
> +        if not opts.dry_run:
> +            s = smtplib.SMTP(opts.host)
> +            s.sendmail(opts.from_address, [to_address],
> +                       msg.as_string())
> +            s.quit()
> 
> -    # Generate the daily report.
> -    note("building report data...")
> -    report = lnt.server.reporting.dailyreport.DailyReport(
> -        ts, year=date.year, month=date.month, day=date.day,
> -        day_start_offset_hours=date.hour, for_mail=True,
> -        num_prior_days_to_include=opts.days,
> -        filter_machine_regex=opts.filter_machine_regex)
> -    report.build()
> -
> -    note("generating HTML report...")
> -    ts_url = "%s/db_%s/v4/%s" % (config.zorgURL, opts.database, opts.testsuite)
> -    subject = "Daily Report: %04d-%02d-%02d" % (
> -        report.year, report.month, report.day)
> -    html_report = report.render(ts_url, only_html_body=False)
> -
> -    if opts.subject_prefix is not None:
> -        subject = "%s %s" % (opts.subject_prefix, subject)
> -
> -    # Form the multipart email message.
> -    msg = email.mime.multipart.MIMEMultipart('alternative')
> -    msg['Subject'] = subject
> -    msg['From'] = opts.from_address
> -    msg['To'] = to_address
> -    msg.attach(email.mime.text.MIMEText(html_report, "html"))
> -
> -    # Send the report.
> -    if not opts.dry_run:
> -        s = smtplib.SMTP(opts.host)
> -        s.sendmail(opts.from_address, [to_address],
> -                   msg.as_string())
> -        s.quit()
> 
> def action_send_run_comparison(name, args):
>     """send a run-vs-run comparison email"""
> @@ -397,47 +400,47 @@ def action_send_run_comparison(name, arg
>     config = instance.config
> 
>     # Get the database.
> -    db = config.get_database(opts.database)
> +    with contextlib.closing(config.get_database(opts.database)) as db:
> 
> -    # Get the testsuite.
> -    ts = db.testsuite[opts.testsuite]
> +        # Get the testsuite.
> +        ts = db.testsuite[opts.testsuite]
> 
> -    # Lookup the two runs.
> -    run_a_id = int(run_a_id)
> -    run_b_id = int(run_b_id)
> -    run_a = ts.query(ts.Run).\
> -        filter_by(id=run_a_id).first()
> -    run_b = ts.query(ts.Run).\
> -        filter_by(id=run_b_id).first()
> -    if run_a is None:
> -        parser.error("invalid run ID %r (not in database)" % (run_a_id,))
> -    if run_b is None:
> -        parser.error("invalid run ID %r (not in database)" % (run_b_id,))
> -
> -    # Generate the report.
> -    reports = lnt.server.reporting.runs.generate_run_report(
> -        run_b, baseurl=config.zorgURL, only_html_body=False, result=None,
> -        compare_to=run_a, baseline=None,
> -        aggregation_fn=min)
> -    subject, text_report, html_report, _ = reports
> -
> -    if opts.subject_prefix is not None:
> -        subject = "%s %s" % (opts.subject_prefix, subject)
> -
> -    # Form the multipart email message.
> -    msg = email.mime.multipart.MIMEMultipart('alternative')
> -    msg['Subject'] = subject
> -    msg['From'] = opts.from_address
> -    msg['To'] = opts.to_address
> -    msg.attach(email.mime.text.MIMEText(text_report, 'plain'))
> -    msg.attach(email.mime.text.MIMEText(html_report, 'html'))
> -
> -    # Send the report.
> -    if not opts.dry_run:
> -        s = smtplib.SMTP(opts.host)
> -        s.sendmail(opts.from_address, [opts.to_address],
> -                   msg.as_string())
> -        s.quit()
> +        # Lookup the two runs.
> +        run_a_id = int(run_a_id)
> +        run_b_id = int(run_b_id)
> +        run_a = ts.query(ts.Run).\
> +            filter_by(id=run_a_id).first()
> +        run_b = ts.query(ts.Run).\
> +            filter_by(id=run_b_id).first()
> +        if run_a is None:
> +            parser.error("invalid run ID %r (not in database)" % (run_a_id,))
> +        if run_b is None:
> +            parser.error("invalid run ID %r (not in database)" % (run_b_id,))
> +
> +        # Generate the report.
> +        reports = lnt.server.reporting.runs.generate_run_report(
> +            run_b, baseurl=config.zorgURL, only_html_body=False, result=None,
> +            compare_to=run_a, baseline=None,
> +            aggregation_fn=min)
> +        subject, text_report, html_report, _ = reports
> +
> +        if opts.subject_prefix is not None:
> +            subject = "%s %s" % (opts.subject_prefix, subject)
> +
> +        # Form the multipart email message.
> +        msg = email.mime.multipart.MIMEMultipart('alternative')
> +        msg['Subject'] = subject
> +        msg['From'] = opts.from_address
> +        msg['To'] = opts.to_address
> +        msg.attach(email.mime.text.MIMEText(text_report, 'plain'))
> +        msg.attach(email.mime.text.MIMEText(html_report, 'html'))
> +
> +        # Send the report.
> +        if not opts.dry_run:
> +            s = smtplib.SMTP(opts.host)
> +            s.sendmail(opts.from_address, [opts.to_address],
> +                       msg.as_string())
> +            s.quit()
> 
> ###
> 
> 
> Modified: lnt/trunk/lnt/lnttool/updatedb.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/lnttool/updatedb.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/lnttool/updatedb.py (original)
> +++ lnt/trunk/lnt/lnttool/updatedb.py Tue Sep 29 06:49:15 2015
> @@ -1,5 +1,6 @@
> import os
> from optparse import OptionParser, OptionGroup
> +import contextlib
> 
> import lnt.server.instance
> from lnt.testing.util.commands import note, warning, error, fatal
> @@ -29,48 +30,50 @@ def action_updatedb(name, args):
> 
>     if opts.testsuite is None:
>         parser.error("--testsuite is required")
> -        
> +
>     path, = args
> 
>     # Load the instance.
>     instance = lnt.server.instance.Instance.frompath(path)
> 
>     # Get the database and test suite.
> -    db = instance.get_database(opts.database, echo=opts.show_sql)
> -    ts = db.testsuite[opts.testsuite]
> -
> -    # Compute a list of all the runs to delete.
> -    runs_to_delete = list(opts.delete_runs)
> -    if opts.delete_machines:
> -        runs_to_delete.extend(
> -            id
> -            for id, in ts.query(ts.Run.id).\
> -                join(ts.Machine).\
> -                filter(ts.Machine.name.in_(opts.delete_machines)))
> -        
> -    # Delete all samples associated with those runs.
> -    ts.query(ts.Sample).\
> -        filter(ts.Sample.run_id.in_(runs_to_delete)).\
> -        delete(synchronize_session=False)
> -
> -    # Delete all those runs.
> -    ts.query(ts.Run).\
> -        filter(ts.Run.id.in_(runs_to_delete)).\
> -        delete(synchronize_session=False)
> -
> -    # Delete the machines.
> -    for name in opts.delete_machines:
> -        # Delete all FieldChanges associated with this machine.
> -        ids = ts.query(ts.FieldChange.id).\
> -            join(ts.Machine).filter(ts.Machine.name == name).all()
> -        for i in ids:
> -            ts.query(ts.FieldChange).filter(ts.FieldChange.id == i[0]).delete()
> -
> -        num_deletes = ts.query(ts.Machine).filter_by(name=name).delete()
> -        if num_deletes == 0:
> -            warning("unable to find machine named: %r" % name)
> -
> -    if opts.commit:
> -        db.commit()
> -    else:
> -        db.rollback()
> +    with contextlib.closing(instance.get_database(opts.database,
> +                                                  echo=opts.show_sql)) as db:
> +        ts = db.testsuite[opts.testsuite]
> +
> +        # Compute a list of all the runs to delete.
> +        runs_to_delete = list(opts.delete_runs)
> +        if opts.delete_machines:
> +            runs_to_delete.extend(
> +                id
> +                for id, in ts.query(ts.Run.id).\
> +                    join(ts.Machine).\
> +                    filter(ts.Machine.name.in_(opts.delete_machines)))
> +
> +        # Delete all samples associated with those runs.
> +        ts.query(ts.Sample).\
> +            filter(ts.Sample.run_id.in_(runs_to_delete)).\
> +            delete(synchronize_session=False)
> +
> +        # Delete all those runs.
> +        ts.query(ts.Run).\
> +            filter(ts.Run.id.in_(runs_to_delete)).\
> +            delete(synchronize_session=False)
> +
> +        # Delete the machines.
> +        for name in opts.delete_machines:
> +            # Delete all FieldChanges associated with this machine.
> +            ids = ts.query(ts.FieldChange.id).\
> +                join(ts.Machine).filter(ts.Machine.name == name).all()
> +            for i in ids:
> +                ts.query(ts.FieldChange).filter(ts.FieldChange.id == i[0]).\
> +                    delete()
> +
> +            num_deletes = ts.query(ts.Machine).filter_by(name=name).delete()
> +            if num_deletes == 0:
> +                warning("unable to find machine named: %r" % name)
> +
> +        if opts.commit:
> +            db.commit()
> +        else:
> +            db.rollback()
> 
> Modified: lnt/trunk/lnt/lnttool/viewcomparison.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/lnttool/viewcomparison.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/lnttool/viewcomparison.py (original)
> +++ lnt/trunk/lnt/lnttool/viewcomparison.py Tue Sep 29 06:49:15 2015
> @@ -8,6 +8,7 @@ import time
> import urllib
> import webbrowser
> from optparse import OptionParser, OptionGroup
> +import contextlib
> 
> import lnt.util.ImportData
> from lnt.testing.util.commands import note, warning, error, fatal
> @@ -86,22 +87,22 @@ def action_view_comparison(name, args):
>         lnt.server.db.migrate.update_path(db_path)
> 
>         # Import the two reports.
> -        db = config.get_database('default')
> -        result = lnt.util.ImportData.import_and_report(
> -            config, 'default', db, report_a_path,
> -            '<auto>', commit=True)
> -        result = lnt.util.ImportData.import_and_report(
> -            config, 'default', db, report_b_path,
> -            '<auto>', commit=True)
> -
> -        # Dispatch another thread to start the webbrowser.
> -        comparison_url = '%s/v4/nts/2?compare_to=1' % (url,)
> -        note("opening comparison view: %s" % (comparison_url,))
> -        thread.start_new_thread(start_browser, (comparison_url,True))
> -
> -        # Run the webserver.
> -        app = lnt.server.ui.app.App.create_with_instance(instance)
> -        app.debug = True
> -        app.run(opts.hostname, opts.port, use_reloader=False)
> +        with contextlib.closing(config.get_database('default')) as db:
> +            result = lnt.util.ImportData.import_and_report(
> +                config, 'default', db, report_a_path,
> +                '<auto>', commit=True)
> +            result = lnt.util.ImportData.import_and_report(
> +                config, 'default', db, report_b_path,
> +                '<auto>', commit=True)
> +
> +            # Dispatch another thread to start the webbrowser.
> +            comparison_url = '%s/v4/nts/2?compare_to=1' % (url,)
> +            note("opening comparison view: %s" % (comparison_url,))
> +            thread.start_new_thread(start_browser, (comparison_url, True))
> +
> +            # Run the webserver.
> +            app = lnt.server.ui.app.App.create_with_instance(instance)
> +            app.debug = True
> +            app.run(opts.hostname, opts.port, use_reloader=False)
>     finally:
>         shutil.rmtree(tmpdir)
> 
> Modified: lnt/trunk/lnt/server/db/v4db.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/v4db.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/db/v4db.py (original)
> +++ lnt/trunk/lnt/server/db/v4db.py Tue Sep 29 06:49:15 2015
> @@ -124,6 +124,10 @@ class V4DB(object):
>         assert (self.real_sample_type and self.status_sample_type), \
>             "sample types not initialized!"
> 
> +    def close(self):
> +        if self.session is not None:
> +            self.session.close()
> +
>     @property
>     def testsuite(self):
>         # This is the start of "magic" part of V4DB, which allows us to get
> 
> Modified: lnt/trunk/lnt/server/ui/app.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/app.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/server/ui/app.py (original)
> +++ lnt/trunk/lnt/server/ui/app.py Tue Sep 29 06:49:15 2015
> @@ -85,6 +85,13 @@ class Request(flask.Request):
> 
>         return self.testsuite
> 
> +    def close(self):
> +        db = getattr(self, 'db', None)
> +        if db is not None:
> +            db.close()
> +        return super(Request, self).close()
> +
> +
> class App(flask.Flask):
>     @staticmethod
>     def create_with_instance(instance):
> 
> Modified: lnt/trunk/lnt/util/ImportData.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/util/ImportData.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/util/ImportData.py (original)
> +++ lnt/trunk/lnt/util/ImportData.py Tue Sep 29 06:49:15 2015
> @@ -122,19 +122,19 @@ def import_and_report(config, db_name, d
>         # Load the shadow database to import into.
>         db_config = config.databases[db_name]
>         shadow_name = db_config.shadow_import
> -        shadow_db = config.get_database(shadow_name)
> -        if shadow_db is None:
> -            raise ValueError,("invalid configuration, shadow import "
> -                              "database %r does not exist") % shadow_name
> +        with closing(config.get_database(shadow_name)) as shadow_db:
> +            if shadow_db is None:
> +                raise ValueError, ("invalid configuration, shadow import "
> +                                   "database %r does not exist") % shadow_name
> 
> -        # Perform the shadow import.
> -        shadow_result = import_and_report(config, shadow_name,
> -                                          shadow_db, file, format, commit,
> -                                          show_sample_count, disable_email,
> -                                          disable_report)
> +            # Perform the shadow import.
> +            shadow_result = import_and_report(config, shadow_name,
> +                                              shadow_db, file, format, commit,
> +                                              show_sample_count, disable_email,
> +                                              disable_report)
> 
> -        # Append the shadow result to the result.
> -        result['shadow_result'] = shadow_result
> +            # Append the shadow result to the result.
> +            result['shadow_result'] = shadow_result
> 
>     result['success'] = True
>     return result
> 
> Modified: lnt/trunk/lnt/util/ServerUtil.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/util/ServerUtil.py?rev=248789&r1=248788&r2=248789&view=diff
> ==============================================================================
> --- lnt/trunk/lnt/util/ServerUtil.py (original)
> +++ lnt/trunk/lnt/util/ServerUtil.py Tue Sep 29 06:49:15 2015
> @@ -45,11 +45,12 @@ def submitFileToInstance(path, file, com
>     instance = lnt.server.instance.Instance.frompath(path)
>     config = instance.config
>     db_name = 'default'
> -    db = config.get_database(db_name)
> -    if db is None:
> -        raise ValueError("no default database in instance: %r" % (path,))
> -    return lnt.util.ImportData.import_and_report(
> -        config, db_name, db, file, format='<auto>', commit=commit)
> +    with closing(config.get_database(db_name)) as db:
> +        if db is None:
> +            raise ValueError("no default database in instance: %r" % (path,))
> +        return lnt.util.ImportData.import_and_report(
> +            config, db_name, db, file, format='<auto>', commit=commit)
> +

Hi Kristof,

Isn’t this missing “import contextlib” and contextlib.closing later?

Adam


> 
> def submitFile(url, file, commit, verbose):
>     # If this is a real url, submit it using urllib.
> 
> 
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits



More information about the llvm-commits mailing list