You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-dev@db.apache.org by "Brett Bergquist (JIRA)" <ji...@apache.org> on 2017/10/31 15:05:01 UTC

[jira] [Issue Comment Deleted] (DERBY-6975) Bug in SequenceUpdater.getCurrentValueAndAdvance - break used instead of continue

     [ https://issues.apache.org/jira/browse/DERBY-6975?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Brett Bergquist updated DERBY-6975:
-----------------------------------
    Comment: was deleted

(was: I saw the same thing and posted to the derby developer's mailing list.   I want to follow up on this but have just been swamped at work.   Here is what I saw:

---

I have a simple application that is running with no other access to the database.   A sequence is defined

CREATE SEQUENCE LOG_ENTRY_SEQUENCE AS BIGINT START WITH 1 MINVALUE 1 CYCLE

The application is retrieving the next sequence number with a “VALUES (NEXT VALUE FOR LOG_ENTRY_SEQUENCE)” statement and then that sequence number is being used as an ID for the rows to be inserted int  another database table.   

500,000 rows are going to be inserted.   When this is run, periodically there is an error being logged by derby.

Here is the derby.log

Tue Sep 19 15:55:06 EDT 2017:
Booting Derby version The Apache Software Foundation - Apache Derby - 10.13.1.1
- (1765088): instance a816c00e-015e-9bb4-c83b-ffffb4d52705
on database directory /opt/csemlogger/glassfish4/databases/csemloggerdb with class loader sun.misc.Launcher$AppClassLoader@5c647e05
Loaded from file:/opt/csemlogger/glassfish4/javadb/lib/derby.jar
java.vendor=Oracle Corporation
java.runtime.version=1.8.0_40-b26
user.dir=/
os.name=SunOS
os.arch=amd64
os.version=5.10
derby.system.home=/opt/csemlogger/glassfish4/databases
Database Class Loader started - derby.database.classpath=''
Database Class Loader started - derby.database.classpath='CSEM.csemderby'
Derby could not obtain the locks needed to release the unused, preallocated values for the sequence 'CSEM'.'LOG_ENTRY_SEQUENCE'. As
a result, unexpected gaps may appear in this sequence.
Derby could not obtain the locks needed to release the unused, preallocated values for the sequence 'CSEM'.'LOG_ENTRY_SEQUENCE'. As
a result, unexpected gaps may appear in this sequence.
Derby could not obtain the locks needed to release the unused, preallocated values for the sequence 'CSEM'.'LOG_ENTRY_SEQUENCE'. As
a result, unexpected gaps may appear in this sequence.
Derby could not obtain the locks needed to release the unused, preallocated values for the sequence 'CSEM'.'LOG_ENTRY_SEQUENCE'. As
a result, unexpected gaps may appear in this sequence.

Here is the code used.   Note that it is using JPA, however the LogEnty entity being inserted is not using the sequence.   All LogEntry ID assignment is done manually using the result of the sequence number retrieved (the slot assignment).

private static void initializeLoggerEntryTable(SetupContext ctx, long entryCount) {
        EntityManager em = ctx.getEntityManager();

        Query nextSeqQuery = em.createNativeQuery("VALUES (NEXT VALUE FOR LOG_ENTRY_SEQUENCE)");

        int j = 0;
        // Create a transaction
        ctx.getEntityManager().getTransaction().begin();
        for (int i = 0; i < entryCount; i++) {

            long nextSeq = ((Long) nextSeqQuery.getSingleResult()).longValue();
            long slot = nextSeq % entryCount;

            // See if there is a LogEntry at this slot
            LogEntry logEntry = em.find(LogEntry.class, slot);
            if (null == logEntry) {
                // There is not, so create one
                logEntry = new LogEntry();
                logEntry.setSlot(slot);
                // Persist the chassis template
                em.persist(logEntry);
            }

            // Commit the transaction
            if (++j >= 10) {
                em.getTransaction().commit();
                em.clear();
                em.getTransaction().begin();
                j = 0;
            }
        }
        ctx.getEntityManager().getTransaction().commit();
    }

The sequence is not used outside of this code and there is no other access to the database.   I don’t understand why some internal lock could not be obtained.

Any insight will be greatly appreciated.

---

I worked around this by going to straight SQL and committing often and it worked in my case.  I will resurrect my test case and apply the patch and give it a whirl.
)

> Bug in SequenceUpdater.getCurrentValueAndAdvance - break used instead of continue
> ---------------------------------------------------------------------------------
>
>                 Key: DERBY-6975
>                 URL: https://issues.apache.org/jira/browse/DERBY-6975
>             Project: Derby
>          Issue Type: Bug
>          Components: SQL
>    Affects Versions: 10.14.1.0
>            Reporter: David Sitsky
>         Attachments: fix.patch
>
>
> I have an application that sporadically gets this exception when inserting a lot of rows using a sequence generator.
> {noformat}
> Caused by: ERROR 40XL1: A lock could not be obtained within the time requested 
>         at org.apache.derby.iapi.error.StandardException.newException(StandardException.java) 
>         at org.apache.derby.iapi.error.StandardException.newException(StandardException.java) 
>         at org.apache.derby.impl.sql.catalog.SequenceUpdater.tooMuchContentionException(SequenceUpdater.java) 
>         at org.apache.derby.impl.sql.catalog.SequenceUpdater.getCurrentValueAndAdvance(SequenceUpdater.java) 
>         at org.apache.derby.impl.sql.catalog.DataDictionaryImpl.getCurrentValueAndAdvance(DataDictionaryImpl.java) 
>         at org.apache.derby.impl.sql.execute.BaseActivation.getCurrentValueAndAdvance(BaseActivation.java) 
>         at org.apache.derby.impl.sql.execute.InsertResultSet.getSetAutoincrementValue(InsertResultSet.java) 
>         at org.apache.derby.impl.sql.execute.BaseActivation.getSetAutoincrementValue(BaseActivation.java) 
>         at org.apache.derby.exe.ac560740aax015fx6bc1x68cax000002339e626a.e0(ac560740aax015fx6bc1x68cax000002339e626a.java) 
>         at org.apache.derby.impl.services.reflect.DirectCall.invoke(DirectCall.java) 
>         at org.apache.derby.impl.sql.execute.RowResultSet.getNextRowCore(RowResultSet.java) 
>         at org.apache.derby.impl.sql.execute.NormalizeResultSet.getNextRowCore(NormalizeResultSet.java) 
>         at org.apache.derby.impl.sql.execute.DMLWriteResultSet.getNextRowCore(DMLWriteResultSet.java) 
>         at org.apache.derby.impl.sql.execute.InsertResultSet.getNextRowCore(InsertResultSet.java) 
>         at org.apache.derby.impl.sql.execute.InsertResultSet.open(InsertResultSet.java) 
>         at org.apache.derby.impl.sql.GenericPreparedStatement.executeStmt(GenericPreparedStatement.java) 
>         at org.apache.derby.impl.sql.GenericPreparedStatement.execute(GenericPreparedStatement.java) 
>         ... 25 more 
> {noformat}
> When I look at the code in question, it comes down to this method:
> {code}
>     /**
>      * <p>
>      * Get the next sequence number managed by this generator and advance the number. Could raise an
>      * exception if the legal range is exhausted and wrap-around is not allowed.
>      * Only one thread at a time is allowed through here. We do not want a race between the
>      * two calls to the sequence generator: getCurrentValueAndAdvance() and allocateNewRange().
>      * </p>
>      *
>      * @param returnValue This value is stuffed with the new sequence number.
>      */
>     public synchronized void getCurrentValueAndAdvance
>         ( NumberDataValue returnValue ) throws StandardException
>     {
>         //
>         // We may have to try to get a value from the Sequence Generator twice.
>         // The first attempt may fail because we need to pre-allocate a new chunk
>         // of values.
>         //
>         for ( int i = 0; i < 2; i++ )
>         {
>             //
>             // We try to get a sequence number. The SequenceGenerator method is synchronized
>             // so only one writer should be in there at a time. Lock contention is possible if
>             // someone has selected from SYSSEQUENCES contrary to our advice. In that case,
>             // we raise a TOO MUCH CONTENTION exception.
>             //
>             long[] cvaa = _sequenceGenerator.getCurrentValueAndAdvance();
>             
>             int status = (int) cvaa[ SequenceGenerator.CVAA_STATUS ];
>             long currentValue = cvaa[ SequenceGenerator.CVAA_CURRENT_VALUE ];
>             long lastAllocatedValue = cvaa[ SequenceGenerator.CVAA_LAST_ALLOCATED_VALUE ];
>             long numberOfValuesAllocated = cvaa[ SequenceGenerator.CVAA_NUMBER_OF_VALUES_ALLOCATED ];
>             
>             switch ( status )
>             {
>             case SequenceGenerator.RET_OK:
>                 returnValue.setValue( currentValue );
>                 return;
>                 
>             case SequenceGenerator.RET_MARK_EXHAUSTED:
>                 updateCurrentValueOnDisk( currentValue, null );
>                 returnValue.setValue( currentValue );
>                 return;
>                 
>             case SequenceGenerator.RET_ALLOCATE_NEW_VALUES:
>                 
>                 if ( updateCurrentValueOnDisk( currentValue, lastAllocatedValue ) )
>                 {
>                     _sequenceGenerator.allocateNewRange( currentValue, numberOfValuesAllocated );
>                 }
>                 break;
>             
>             default:
>                 throw unimplementedFeature();
>             }
>         }
>         //
>         // If we get here, then we failed to allocate a new sequence number range.
>         //
>         throw tooMuchContentionException();
>     }
> {code}
> If I understand the intent of this code, in the SequenceGenerator.RET_ALLOCATE_NEW_VALUES case, we allocate a new sequence generator range and then we are meant to re-execute the loop again to use it, however since "break" is used we break out of the for loop and throw the exception.
> It seems if we replace "break" with 'continue" this will work as expected?



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)