db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r1591703 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/reference/ engine/org/apache/derby/impl/sql/catalog/ testing/org/apache/derbyTesting/functionTests/tests/lang/
Date Thu, 01 May 2014 16:30:44 GMT
Author: rhillegas
Date: Thu May  1 16:30:44 2014
New Revision: 1591703

URL: http://svn.apache.org/r1591703
Log:
DERBY-6554: Fix some bugs in cache management for sequence generators; commit derby-6554-01-ad-bugfixes.diff.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java?rev=1591703&r1=1591702&r2=1591703&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java Thu May
 1 16:30:44 2014
@@ -716,10 +716,10 @@ public interface Property { 
 	 * The size of the sequence generator cache 
 	 * used by the data dictionary.  Database.  Static.
 	 * <p>
-	 * Externally visible but undocumented.
+	 * Externally visible and documented.
 	 */
 	String	LANG_SEQGEN_CACHE_SIZE = "derby.language.sequenceGeneratorCacheSize";
-	int		LANG_SEQGEN_CACHE_SIZE_DEFAULT =32;
+	int		LANG_SEQGEN_CACHE_SIZE_DEFAULT = 1000;
 
 	/**
 	 * Name of the implementation of SequencePreallocator which is used

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=1591703&r1=1591702&r2=1591703&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
Thu May  1 16:30:44 2014
@@ -10253,10 +10253,13 @@ public final class	DataDictionaryImpl
 
             boolean baseRowExists = heapCC.fetch(
                     rowLocation, row.getRowArray(), columnToUpdate, wait);
-            if (SanityManager.DEBUG) {
-                // We're not prepared for a non-existing base row.
-                SanityManager.ASSERT(baseRowExists, "base row not found");
-            }
+
+            //
+            // We will fail to find the row if it is still locked by the transaction
+            // which created the sequence. In that case, we will leak values
+            // the next time the generator is referenced.
+            //
+            if ( !baseRowExists ) { return false; }
 
 			NumberDataValue oldValueOnDisk = (NumberDataValue) row.getColumn( columnNum );
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java?rev=1591703&r1=1591702&r2=1591703&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java
Thu May  1 16:30:44 2014
@@ -20,6 +20,8 @@
  */
 package org.apache.derby.impl.sql.catalog;
 
+import java.util.HashMap;
+
 import org.apache.derby.catalog.SequencePreallocator;
 import org.apache.derby.iapi.db.Database;
 import org.apache.derby.iapi.error.StandardException;
@@ -104,10 +106,14 @@ public abstract class SequenceUpdater im
     // DataDictionary where this generator is cached.
     protected DataDictionaryImpl _dd;
 
-    // This is the key used to lookup this generator in the cache.
+    //
+    // The following state needs to be reset whenever this Cachable is re-used.
+    //
+    
+    /** This is the key used to lookup this generator in the cache. */
     protected String _uuidString;
 
-    // This is the object which allocates ranges of sequence values
+    /** This is the object which allocates ranges of sequence values */
     protected SequenceGenerator _sequenceGenerator;
 
     ///////////////////////////////////////////////////////////////////////////////////
@@ -187,11 +193,32 @@ public abstract class SequenceUpdater im
         // Flush current value to disk. This prevents us from leaking values when DDL
         // is performed. The metadata caches are invalidated and cleared when DDL is performed.
         // We flush the current value to disk on database shutdown also.
+        // The call to updateCurrentValueOnDisk can fail if someone is holding a lock
+        // on the SYS.SYSSEQUENCES row. This can happen if the user disregards our
+        // advice and scans that catalog. This can also happen if the transaction which
+        // creates the sequences is open for a long time and some later sequence creation
+        // causes us to evict the old, uncommitted sequence generator from the cache.
         //
-        if ( _sequenceGenerator != null )
-        {
-            boolean gapClosed = updateCurrentValueOnDisk( null, peekAtCurrentValue() );
+        boolean gapClosed = false;
 
+        try {
+            if ( _sequenceGenerator == null ) { gapClosed = true; }
+            else
+            {
+                gapClosed = updateCurrentValueOnDisk( null, peekAtCurrentValue() );
+            }
+        }
+        catch (StandardException se)
+        {
+            // The too much contention exception is redundant because the problem is logged
+            // by the message below
+            if ( !SQLState.LANG_TOO_MUCH_CONTENTION_ON_SEQUENCE.equals( se.getMessageId()
) )
+            {
+                throw se;
+            }
+        }
+        finally
+        {
             // log an error message if we failed to flush the preallocated values.
             if ( !gapClosed )
             {
@@ -204,10 +231,10 @@ public abstract class SequenceUpdater im
 
                 Monitor.getStream().println( errorMessage );
             }
-        }
 
-        _uuidString = null;
-        _sequenceGenerator = null;
+            _uuidString = null;
+            _sequenceGenerator = null;
+        }
 	}
     
 	public boolean isDirty() { return false; }
@@ -421,9 +448,11 @@ public abstract class SequenceUpdater im
 
         if ( nestedTransaction != null )
         {
+            boolean retval = false;
+            
             try
             {
-                return updateCurrentValueOnDisk( nestedTransaction, oldValue, newValue, false
);
+                retval = updateCurrentValueOnDisk( nestedTransaction, oldValue, newValue,
false );
             }
             catch (StandardException se)
             {
@@ -437,6 +466,8 @@ public abstract class SequenceUpdater im
                 // transaction commits by default.
                 nestedTransaction.commit();
                 nestedTransaction.destroy();
+
+                return retval;
             }
         }
         

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceTest.java?rev=1591703&r1=1591702&r2=1591703&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceTest.java
Thu May  1 16:30:44 2014
@@ -21,6 +21,7 @@
 package org.apache.derbyTesting.functionTests.tests.lang;
 
 import java.sql.Connection;
+import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -46,6 +47,8 @@ public class SequenceTest extends Genera
     private static final String BETA = "BETA";
     private static final String[] LEGAL_USERS = {TEST_DBO, ALPHA, BETA};
 
+    private static  final   String  TOO_MANY_UNUSED_SEQUENCES = "X0Y93";
+
     public SequenceTest(String name) {
         super(name);
         // TODO Auto-generated constructor stub
@@ -639,4 +642,113 @@ public class SequenceTest extends Genera
         expectExecutionError( conn, MISSING_OBJECT, peekAtSequence );
         expectCompilationError( conn, OBJECT_DOES_NOT_EXIST, "values next value for seq_6137"
);
     }
+    
+    /**
+     * Verify that sequence numbers pick up where they left off after eviction from
+     * the sequence cache.
+     */
+    public void test_17_6554_cacheEviction() throws Exception
+    {
+        Connection control = openUserConnection( TEST_DBO );
+
+        // set the size of the sequence generator cache to 5 entries.
+        // bounce the database so that the value takes effect.
+        goodStatement( control, "call syscs_util.syscs_set_database_property( 'derby.language.sequenceGeneratorCacheSize',
'5' )" );
+        getTestConfiguration().shutdownDatabase();
+
+        Connection conn1 = openUserConnection( ALPHA );
+        Connection conn2 = openUserConnection( ALPHA );
+
+        goodStatement( conn1, "create sequence s000" );
+        assertResults
+            (
+             conn1,
+             "values next value for s000",
+             new String[][]
+             {
+                 { "-2147483648" },
+             },
+             true
+             );
+        goodStatement( conn1, "create sequence s001" );
+        assertResults
+            (
+             conn1,
+             "values next value for s001",
+             new String[][]
+             {
+                 { "-2147483648" },
+             },
+             true
+             );
+        assertResults
+            (
+             conn1,
+             "values next value for s001",
+             new String[][]
+             {
+                 { "-2147483647" },
+             },
+             true
+             );
+
+        // now create enough sequences to evict the first two sequences from the cache
+        goodStatement( conn2, "create sequence s002" );
+        goodStatement( conn2, "create sequence s003" );
+        goodStatement( conn2, "create sequence s004" );
+        goodStatement( conn2, "create sequence s005" );
+        goodStatement( conn2, "create sequence s006" );
+        goodStatement( conn2, "create sequence s007" );
+        goodStatement( conn2, "create sequence s008" );
+        goodStatement( conn2, "create sequence s009" );
+        goodStatement( conn2, "create sequence s010" );
+
+        // now we pick up where we left off
+        assertResults
+            (
+             conn1,
+             "values next value for s000",
+             new String[][]
+             {
+                 { "-2147483647" },
+             },
+             true
+             );
+        assertResults
+            (
+             conn1,
+             "values next value for s001",
+             new String[][]
+             {
+                 { "-2147483646" },
+             },
+             true
+             );
+
+        // restore the original size of the sequence generator cache
+        control = openUserConnection( TEST_DBO );
+
+        goodStatement( control, "call syscs_util.syscs_set_database_property( 'derby.language.sequenceGeneratorCacheSize',
'1000' )" );
+        getTestConfiguration().shutdownDatabase();
+    }
+    
+    //////////////////////////////////////////////////////////////////////
+    //
+    //  SQL ROUTINES
+    //
+    //////////////////////////////////////////////////////////////////////
+
+    public  static  void    createSequence( String sequenceName )
+        throws Exception
+    {
+        Connection  conn = DriverManager.getConnection( "jdbc:default:connection" );
+
+        conn.prepareStatement( "create sequence " + sequenceName ).execute();
+
+        ResultSet   rs = conn.prepareStatement( "values next value for " + sequenceName ).executeQuery();
+        rs.next();
+        assertEquals( -2147483648L, rs.getLong( 1 ) );
+        rs.close();
+    }
+
 }



Mime
View raw message