← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/test-no-sequence-reset into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/test-no-sequence-reset into lp:launchpad with lp:~wgrant/launchpad/test-no-sequence-deps-translations as a prerequisite.

Commit message:
Don't reset sequences between tests. It's expensive and few tests needed it.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/test-no-sequence-reset/+merge/273388

Don't reset sequences between tests.

It costs about 50ms per test, and only 41 tests actually depended on known sequence values.

Before: DatabaseLayer.testTearDown                      10 calls taking 0.6s.
After:  DatabaseLayer.testTearDown                      10 calls taking negligible time.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/test-no-sequence-reset into lp:launchpad.
=== modified file 'lib/lp/testing/layers.py'
--- lib/lp/testing/layers.py	2013-08-14 11:15:51 +0000
+++ lib/lp/testing/layers.py	2015-10-05 10:08:14 +0000
@@ -719,20 +719,15 @@
     @profiled
     def setUp(cls):
         cls._is_setup = True
-        # Read the sequences we'll need from the test template database.
-        reset_sequences_sql = LaunchpadTestSetup(
-            dbname='launchpad_ftest_template').generateResetSequencesSQL()
         # Allocate a template for this test instance
         if os.environ.get('LP_TEST_INSTANCE'):
             template_name = '_'.join([LaunchpadTestSetup.template,
                 os.environ.get('LP_TEST_INSTANCE')])
-            cls._db_template_fixture = LaunchpadTestSetup(
-                dbname=template_name, reset_sequences_sql=reset_sequences_sql)
+            cls._db_template_fixture = LaunchpadTestSetup(dbname=template_name)
             cls._db_template_fixture.setUp()
         else:
             template_name = LaunchpadTestSetup.template
-        cls._db_fixture = LaunchpadTestSetup(template=template_name,
-            reset_sequences_sql=reset_sequences_sql)
+        cls._db_fixture = LaunchpadTestSetup(template=template_name)
         cls.force_dirty_database()
         # Nuke any existing DB (for persistent-test-services) [though they
         # prevent this !?]
@@ -747,7 +742,7 @@
         # point for addressing that mismatch.
         cls._db_fixture.tearDown()
         # Bring up the db, so that it is available for other layers.
-        cls._ensure_db()
+        cls._db_fixture.setUp()
 
     @classmethod
     @profiled
@@ -771,28 +766,8 @@
         pass
 
     @classmethod
-    def _ensure_db(cls):
-        cls._db_fixture.setUp()
-        # Ensure that the database is connectable. Because we might have
-        # just created it, keep trying for a few seconds incase PostgreSQL
-        # is taking its time getting its house in order.
-        attempts = 60
-        for count in range(0, attempts):
-            try:
-                cls.connect().close()
-            except psycopg2.Error:
-                if count == attempts - 1:
-                    raise
-                time.sleep(0.5)
-            else:
-                break
-
-    @classmethod
     @profiled
     def testTearDown(cls):
-        # Ensure that the database is connectable
-        cls.connect().close()
-
         cls._db_fixture.tearDown()
 
         # Fail tests that forget to uninstall their database policies.
@@ -804,7 +779,7 @@
         # Reset/bring up the db - makes it available for either the next test,
         # or a subordinate layer which builds on the db. This wastes one setup
         # per db layer teardown per run, but thats tolerable.
-        cls._ensure_db()
+        cls._db_fixture.setUp()
 
     @classmethod
     @profiled

=== modified file 'lib/lp/testing/pgsql.py'
--- lib/lp/testing/pgsql.py	2015-10-01 22:36:23 +0000
+++ lib/lp/testing/pgsql.py	2015-10-05 10:08:14 +0000
@@ -19,10 +19,6 @@
 
 from lp.services.config import config
 from lp.services.database import activity_cols
-from lp.services.database.postgresql import (
-    generateResetSequencesSQL,
-    resetSequences,
-    )
 
 
 class ConnectionWrapper:
@@ -179,7 +175,7 @@
     _reset_db = True
 
     def __init__(self, template=None, dbname=dynamic, dbuser=None,
-            host=None, port=None, reset_sequences_sql=None):
+                 host=None, port=None):
         '''Construct the PgTestSetup
 
         Note that dbuser is not used for setting up or tearing down
@@ -221,7 +217,6 @@
             self.host = host
         if port is not None:
             self.port = port
-        self.reset_sequences_sql = reset_sequences_sql
 
     def _connectionString(self, dbname, dbuser=None):
         connection_parameters = ['dbname=%s' % dbname]
@@ -238,15 +233,6 @@
             dbname = self.dbname
         return psycopg2.connect(self._connectionString(dbname))
 
-    def generateResetSequencesSQL(self):
-        """Return a SQL statement that resets all sequences."""
-        con = self.superuser_connection()
-        cur = con.cursor()
-        try:
-            return generateResetSequencesSQL(cur)
-        finally:
-            con.close()
-
     def setUp(self):
         '''Create a fresh database (dropping the old if necessary)
 
@@ -257,18 +243,6 @@
         if (self.template, self.dbname) != PgTestSetup._last_db:
             PgTestSetup._reset_db = True
         if not PgTestSetup._reset_db:
-            # The database doesn't need to be reset. We reset the sequences
-            # anyway (because they might have been incremented even if
-            # nothing was committed), making sure not to disturb the
-            # 'committed' flag, and we're done.
-            con = self.superuser_connection()
-            cur = con.cursor()
-            if self.reset_sequences_sql is None:
-                resetSequences(cur)
-            else:
-                cur.execute(self.reset_sequences_sql)
-            con.commit()
-            con.close()
             ConnectionWrapper.committed = False
             ConnectionWrapper.dirty = False
             return
@@ -366,6 +340,7 @@
             l.unlock()
             if debug:
                 sys.stderr.write('released %s\n' % (pid,))
+
         ConnectionWrapper.committed = False
         ConnectionWrapper.dirty = False
         PgTestSetup._last_db = (self.template, self.dbname)

=== modified file 'lib/lp/testing/tests/test_pgsql.py'
--- lib/lp/testing/tests/test_pgsql.py	2015-10-01 22:36:23 +0000
+++ lib/lp/testing/tests/test_pgsql.py	2015-10-05 10:08:14 +0000
@@ -119,82 +119,3 @@
             con.commit()
         finally:
             fixture.tearDown()
-
-    def test_sequences(self):
-        # Sequences may be affected by connections even if the connection
-        # is rolled back. So ensure the database is reset fully, in the
-        # cases where we just rollback the changes we also need to reset all
-        # the sequences.
-
-        # Setup a table that uses a sequence
-        fixture = PgTestSetup()
-        fixture.setUp()
-        try:
-            con = fixture.connect()
-            cur = con.cursor()
-            cur.execute('CREATE TABLE foo (x serial, y integer)')
-            con.commit()
-            con.close()
-            # Fake it so the harness doesn't know a change has been made
-            ConnectionWrapper.committed = False
-        finally:
-            fixture.tearDown()
-
-        sequence_values = []
-        # Insert a row into it and roll back the changes. Each time, we
-        # should end up with the same sequence value
-        for i in range(3):
-            fixture.setUp()
-            try:
-                con = fixture.connect()
-                cur = con.cursor()
-                cur.execute('INSERT INTO foo (y) VALUES (1)')
-                cur.execute("SELECT currval('foo_x_seq')")
-                sequence_values.append(cur.fetchone()[0])
-                con.rollback()
-                con.close()
-            finally:
-                fixture.tearDown()
-
-        # Fail if we got a diffent sequence value at some point
-        for v in sequence_values:
-            self.failUnlessEqual(v, sequence_values[0])
-
-        # Repeat the test, but this time with some data already in the
-        # table
-        fixture.setUp()
-        try:
-            con = fixture.connect()
-            cur = con.cursor()
-            cur.execute('INSERT INTO foo (y) VALUES (1)')
-            con.commit()
-            con.close()
-            # Fake it so the harness doesn't know a change has been made
-            ConnectionWrapper.committed = False
-        finally:
-            fixture.tearDown()
-
-        sequence_values = []
-        # Insert a row into it and roll back the changes. Each time, we
-        # should end up with the same sequence value
-        for i in range(1, 3):
-            fixture.setUp()
-            try:
-                con = fixture.connect()
-                cur = con.cursor()
-                cur.execute('INSERT INTO foo (y) VALUES (1)')
-                cur.execute("SELECT currval('foo_x_seq')")
-                sequence_values.append(cur.fetchone()[0])
-                con.rollback()
-                con.close()
-            finally:
-                fixture.tearDown()
-
-        # Fail if we got a diffent sequence value at some point
-        for v in sequence_values:
-            self.failUnlessEqual(v, sequence_values[0])
-
-        # The database still exists because, after the last commit,
-        # ConnectionWrapper.committed was set to False.
-        # Drop the database here to avoid test ordering issues.
-        fixture.dropDb()


References