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

Kristof Beyls via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 04:49:17 PDT 2015


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)
+
 
 def submitFile(url, file, commit, verbose):
     # If this is a real url, submit it using urllib.




More information about the llvm-commits mailing list