Kristian Waagan wrote:
Blair Zajac wrote:

Hello Blair,

My only *guess* is that there might be a restructuring of a BTree going on, which is not correctly reported - and you might be experiencing a deadlock. To stop guessing and actually start understanding what's going on, it would be great if you could provide a standalone repro.
Such a repro is best handled by creating a bug report in Jira:
https://issues.apache.org/jira/browse/DERBY

Hello Kristian,

Thanks for the quick reply.

I do have a reproduction standalone Java file that runs the tests. I've attached it to the note. Should I still open a ticket if the DERBY-2991 patch works?

More information below.

Other things to try:
o Post the stack trace from the hang, preferably from a Derby debug build to get line numbers.

Here they are

Compiling 10.4.2.0 from the svn tag and using that:

org.apache.derby.iapi.error.StandardException.newException(StandardException.java:286)
org.apache.derby.impl.services.locks.Timeout.createException(Timeout.java:150)
org.apache.derby.impl.services.locks.Timeout.buildException(Timeout.java:249)
org.apache.derby.impl.services.locks.ConcurrentLockSet.lockObject(ConcurrentLockSet.java:597)
org.apache.derby.impl.services.locks.AbstractPool.lockObject(AbstractPool.java:119)
org.apache.derby.impl.store.raw.xact.RowLocking3.lockRecordForWrite(RowLocking3.java:248)
org.apache.derby.impl.store.access.heap.HeapController.lockRow(HeapController.java:504)
org.apache.derby.impl.store.access.heap.HeapController.lockRow(HeapController.java:638)
org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockRowOnPage(B2IRowLocking3.java:335)
org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockNonScanRowOnPage(B2IRowLocking3.java:1091)
org.apache.derby.impl.store.access.btree.BTreeController.doIns(BTreeController.java:706)
org.apache.derby.impl.store.access.btree.BTreeController.insert(BTreeController.java:1264)
org.apache.derby.impl.store.access.btree.index.B2IController.insert(B2IController.java:210)
org.apache.derby.impl.sql.execute.IndexChanger.insertAndCheckDups(IndexChanger.java:439)
org.apache.derby.impl.sql.execute.IndexChanger.doInsert(IndexChanger.java:383)
org.apache.derby.impl.sql.execute.IndexChanger.insert(IndexChanger.java:589)
org.apache.derby.impl.sql.execute.IndexSetChanger.insert(IndexSetChanger.java:268)
org.apache.derby.impl.sql.execute.RowChangerImpl.insertRow(RowChangerImpl.java:453)
org.apache.derby.impl.sql.execute.InsertResultSet.normalInsertCore(InsertResultSet.java:1011)
org.apache.derby.impl.sql.execute.InsertResultSet.open(InsertResultSet.java:487)
org.apache.derby.impl.sql.GenericPreparedStatement.execute(GenericPreparedStatement.java:372)
org.apache.derby.impl.jdbc.EmbedStatement.executeStatement(EmbedStatement.java:1235)
org.apache.derby.impl.jdbc.EmbedStatement.execute(EmbedStatement.java:625)
org.apache.derby.impl.jdbc.EmbedStatement.executeUpdate(EmbedStatement.java:175)
NoProgressTest.executeUpdate(NoProgressTest.java:38)
NoProgressTest.access$100(NoProgressTest.java:10)
NoProgressTest$InsertRunnable.run(NoProgressTest.java:213)


Using the 10.4 branch at r737828.

org.apache.derby.impl.services.locks.Timeout.createException(Timeout.java:150)
org.apache.derby.impl.services.locks.Timeout.buildException(Timeout.java:249)
org.apache.derby.impl.services.locks.ConcurrentLockSet.lockObject(ConcurrentLockSet.java:597)
org.apache.derby.impl.services.locks.AbstractPool.lockObject(AbstractPool.java:119)
org.apache.derby.impl.store.raw.xact.RowLocking3.lockRecordForWrite(RowLocking3.java:248)
org.apache.derby.impl.store.access.heap.HeapController.lockRow(HeapController.java:504)
org.apache.derby.impl.store.access.heap.HeapController.lockRow(HeapController.java:638)
org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockRowOnPage(B2IRowLocking3.java:335)
org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockNonScanRowOnPage(B2IRowLocking3.java:1091)
org.apache.derby.impl.store.access.btree.BTreeController.doIns(BTreeController.java:706)
org.apache.derby.impl.store.access.btree.BTreeController.insert(BTreeController.java:1264)
org.apache.derby.impl.store.access.btree.index.B2IController.insert(B2IController.java:210)
org.apache.derby.impl.sql.execute.IndexChanger.insertAndCheckDups(IndexChanger.java:439)
org.apache.derby.impl.sql.execute.IndexChanger.doInsert(IndexChanger.java:383)
org.apache.derby.impl.sql.execute.IndexChanger.insert(IndexChanger.java:589)
org.apache.derby.impl.sql.execute.IndexSetChanger.insert(IndexSetChanger.java:268)
org.apache.derby.impl.sql.execute.RowChangerImpl.insertRow(RowChangerImpl.java:453)
org.apache.derby.impl.sql.execute.InsertResultSet.normalInsertCore(InsertResultSet.java:1011)
org.apache.derby.impl.sql.execute.InsertResultSet.open(InsertResultSet.java:487)
org.apache.derby.impl.sql.GenericPreparedStatement.execute(GenericPreparedStatement.java:372)
org.apache.derby.impl.jdbc.EmbedStatement.executeStatement(EmbedStatement.java:1235)
org.apache.derby.impl.jdbc.EmbedStatement.execute(EmbedStatement.java:625)
org.apache.derby.impl.jdbc.EmbedStatement.executeUpdate(EmbedStatement.java:175)
NoProgressTest.executeUpdate(NoProgressTest.java:38)
NoProgressTest.access$100(NoProgressTest.java:10)
NoProgressTest$InsertRunnable.run(NoProgressTest.java:213)


With an unpatched trunk at r737572

org.apache.derby.iapi.error.StandardException.newException(StandardException.java:286)
org.apache.derby.impl.services.locks.Timeout.createException(Timeout.java:150)
org.apache.derby.impl.services.locks.Timeout.buildException(Timeout.java:249)
org.apache.derby.impl.services.locks.ConcurrentLockSet.lockObject(ConcurrentLockSet.java:597)
org.apache.derby.impl.services.locks.AbstractPool.lockObject(AbstractPool.java:119)
org.apache.derby.impl.store.raw.xact.RowLocking3.lockRecordForWrite(RowLocking3.java:248)
org.apache.derby.impl.store.access.heap.HeapController.lockRow(HeapController.java:504)
org.apache.derby.impl.store.access.heap.HeapController.lockRow(HeapController.java:638)
org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockRowOnPage(B2IRowLocking3.java:335)
org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockNonScanRowOnPage(B2IRowLocking3.java:1091)
org.apache.derby.impl.store.access.btree.BTreeController.doIns(BTreeController.java:707)
org.apache.derby.impl.store.access.btree.BTreeController.insert(BTreeController.java:1261)
org.apache.derby.impl.store.access.btree.index.B2IController.insert(B2IController.java:210)
org.apache.derby.impl.sql.execute.IndexChanger.insertAndCheckDups(IndexChanger.java:439)
org.apache.derby.impl.sql.execute.IndexChanger.doInsert(IndexChanger.java:383)
org.apache.derby.impl.sql.execute.IndexChanger.insert(IndexChanger.java:589)
org.apache.derby.impl.sql.execute.IndexSetChanger.insert(IndexSetChanger.java:268)
org.apache.derby.impl.sql.execute.RowChangerImpl.insertRow(RowChangerImpl.java:453)
org.apache.derby.impl.sql.execute.InsertResultSet.normalInsertCore(InsertResultSet.java:1022)
org.apache.derby.impl.sql.execute.InsertResultSet.open(InsertResultSet.java:495)
org.apache.derby.impl.sql.GenericPreparedStatement.executeStmt(GenericPreparedStatement.java:416)
org.apache.derby.impl.sql.GenericPreparedStatement.execute(GenericPreparedStatement.java:297)
org.apache.derby.impl.jdbc.EmbedStatement.executeStatement(EmbedStatement.java:1235)
org.apache.derby.impl.jdbc.EmbedStatement.execute(EmbedStatement.java:625)
org.apache.derby.impl.jdbc.EmbedStatement.executeUpdate(EmbedStatement.java:175)
NoProgressTest.executeUpdate(NoProgressTest.java:38)
NoProgressTest.access$100(NoProgressTest.java:10)

o You don't say if you enabled "extended tracing". If not, see http://wiki.apache.org/db-derby/LockDebugging

I've done this.

o Download the patch for DERBY-2991, build Derby and see if it solves your problem.

I downloaded d2991-preview-1e.diff and applied it to r737572 of trunk. I don't see the original problem now. Even going to 500 threads and setting derby.locks.waitTimeout to -1 works.

Will or when will a patch for d2991 be applied to trunk?

Regards,
Blair

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CountDownLatch;

public class NoProgressTest
{
    private static Connection getConnection(String suffix)
        throws java.sql.SQLException
    {
        // String url = "jdbc:derby://localhost/no-progress" + suffix;
        String url = "jdbc:derby:no-progress" + suffix;

        return java.sql.DriverManager.getConnection(url);
    }

    private static void execute(Connection c, String sql)
        throws java.sql.SQLException
    {
        Statement s = c.createStatement();
        try {
            s.execute(sql);
        }
        finally {
            s.close();
        }
    }

    private static void executeUpdate(Connection c, String sql)
        throws java.sql.SQLException
    {
        Statement s = c.createStatement();
        try {
            int count = s.executeUpdate(sql);
            if (count != 1) {
                throw new java.lang.RuntimeException(sql + " updated " +
                                                     count + " rows.");
            }
        }
        finally {
            s.close();
        }
    }

    private static void createSchema()
        throws java.sql.SQLException
    {
        Connection c = getConnection(";create=true");
        try {
            execute(c,
                    "CREATE TABLE facility " +
                    "(pk_facility INT PRIMARY KEY, " +
                    " code CHAR(3) UNIQUE)");
            execute(c,
                    "CREATE TABLE facility_set " +
                    "(pk_facility_set INT PRIMARY KEY)");
            execute(c,
                    "CREATE TABLE facility_set_membership " +
                    "(pk_facility INT, " +
                    "pk_facility_set INT)");
            execute(c,
                    "CREATE UNIQUE INDEX fsm_idx ON " +
                    "facility_set_membership (pk_facility, pk_facility_set)");
            execute(c,
                    "ALTER TABLE facility_set_membership " +
                    "ADD CONSTRAINT fk_fsm_f " +
                    "FOREIGN KEY (pk_facility) " +
                    "REFERENCES facility(pk_facility)");
            execute(c,
                    "ALTER TABLE facility_set_membership " +
                    "ADD CONSTRAINT fk_fsm_fs " +
                    "FOREIGN KEY (pk_facility_set) " +
                    "REFERENCES facility_set(pk_facility_set)");
            execute(c,
                    "CREATE TABLE location " +
                    "(pk_location INT PRIMARY KEY, " +
                    "pk_facility_set INT, " +
                    "path VARCHAR(1024) UNIQUE NOT NULL)");
            execute(c,
                    "ALTER TABLE location " +
                    "ADD CONSTRAINT fk_loc_fs " +
                    "FOREIGN KEY (pk_facility_set) " +
                    "REFERENCES facility_set(pk_facility_set)");
        }
        finally {
            c.close();
        }
    }

    private static void populateFacilityTable(List<String> facilityCodes)
        throws java.sql.SQLException
    {
        Connection c = getConnection("");
        try {
            for (String code : facilityCodes) {
                executeUpdate(c,
                              "INSERT INTO facility (pk_facility, code) " +
                              "VALUES (" + code.hashCode() +
                              ", '" + code + "')");
            }
        }
        finally {
            c.close();
        }
    }

    private static void insertFacilitySet(Connection c,
                                          TreeSet<String> facilitySet)
        throws java.sql.SQLException
    {
        executeUpdate(c,
                      "INSERT INTO facility_set (pk_facility_set) VALUES " +
                      "(" + facilitySet.hashCode() + ")");
        for (String code : facilitySet) {
            executeUpdate(c,
                          "INSERT INTO facility_set_membership " +
                          "(pk_facility, pk_facility_set) VALUES " +
                          "(" + code.hashCode() + ", " +
                          facilitySet.hashCode() + ")");
        }
    }

    private static void populateFacilitySetTable(List<TreeSet<String>> 
facilitySets)
        throws java.sql.SQLException
    {
        Connection c = getConnection("");
        try {
            for (TreeSet<String> facilitySet : facilitySets) {
                insertFacilitySet(c, facilitySet);
            }
        }
        finally {
            c.close();
        }
    }

    private static TreeSet<String> getLocationFacilitySet(Connection c,
                                                          String location)
        throws java.sql.SQLException
    {
        Statement s = c.createStatement();
        try {
            // First get the location's facility_set foriegn key.
            String sql1 =
                "SELECT pk_facility_set FROM location WHERE " +
                "pk_location = " + location.hashCode() + " FOR UPDATE";

            ResultSet rs1 = s.executeQuery(sql1);
            rs1.next();
            int pk_facility_set = rs1.getInt(1);
            rs1.close();

            String sql2 =
                "SELECT facility.code " +
                "FROM facility, facility_set_membership, " +
                "facility_set " +
                "WHERE facility.pk_facility = 
facility_set_membership.pk_facility AND " +
                "facility_set_membership.pk_facility_set = 
facility_set.pk_facility_set ANd " +
                "facility_set.pk_facility_set = " + pk_facility_set;
            ResultSet rs2 = s.executeQuery(sql2);

            TreeSet<String> facilitySet = new TreeSet<String>();
            while (rs2.next()) {
                facilitySet.add(rs2.getString(1));
            }
            rs2.close();
            return facilitySet;
        }
        finally {
            s.close();
        }
    }

    private static class InsertRunnable implements Runnable
    {
        private final String location;
        private final ArrayList<TreeSet<String>> facilitySets;
        private final AtomicInteger ai;
        private final CountDownLatch allThreadsReadyLatch;
        private final CountDownLatch startInsertLatch;

        public InsertRunnable(String location,
                              ArrayList<TreeSet<String>> facilitySets,
                              CountDownLatch allThreadsReadyLatch,
                              CountDownLatch startInsertLatch,
                              AtomicInteger ai)
        {
            this.location = location;
            this.facilitySets = facilitySets;
            this.allThreadsReadyLatch = allThreadsReadyLatch;
            this.startInsertLatch = startInsertLatch;
            this.ai = ai;
        }

        public void run()
        {
            try {
                int i = ai.getAndIncrement();
                TreeSet<String> facilitySet = facilitySets.get(i);

                Connection c = getConnection("");
                c.setAutoCommit(false);

                allThreadsReadyLatch.countDown();
                startInsertLatch.await();

                // Try to INSERT the location.
                try {
                    executeUpdate(c,
                                  "INSERT INTO location (pk_location, " +
                                  "pk_facility_set, path) " +
                                  "VALUES (" +
                                  location.hashCode() + ", " +
                                  facilitySet.hashCode() + ", '" +
                                  location + "')");
                }
                catch (java.lang.Throwable e) {
                    // Get the location's current facility set.
                    TreeSet<String> currentFacilitySet =
                        getLocationFacilitySet(c, location);

                    // Get the union of the current and the input
                    // facility set.
                    TreeSet<String> unionFacilitySet = new TreeSet<String>();
                    unionFacilitySet.addAll(facilitySet);
                    unionFacilitySet.addAll(currentFacilitySet);

                    // If the two sets are different, then add the
                    // union facility set to the database and upate
                    // the foreign key for the location.
                    if (unionFacilitySet != currentFacilitySet) {
                        insertFacilitySet(c, unionFacilitySet);
                        executeUpdate(c,
                                      "UPDATE location " +
                                      "SET pk_facility_set = " + 
unionFacilitySet.hashCode() +
                                      " WHERE pk_location = " + 
location.hashCode() +
                                      " AND pk_facility_set = " + 
currentFacilitySet.hashCode());
                    }
                }

                c.commit();

                System.out.println(Thread.currentThread().toString() + " OK");
            }
            catch (java.lang.InterruptedException e) {
            }
            catch (java.sql.SQLException e) {
            }
        }
    };

    public static void main(String[] args)
        throws java.lang.ClassNotFoundException,
               java.lang.InterruptedException,
               java.sql.SQLException
    {
        System.setProperty("derby.locks.deadlockTrace", "true");
        System.setProperty("derby.locks.monitor", "true");
        System.setProperty("derby.locks.waitTimeout", "-1");

        Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
        // Class.forName("org.apache.derby.jdbc.ClientDriver");

        System.out.println("Creating schema.");

        createSchema();

        String location = "/path/to/foobar";

        final int numberThreads = 500;

        // Create a list of facilities.
        ArrayList<String> facilityCodes = new ArrayList<String>();
        for (int i = 0; i < numberThreads; ++i) {
            String code = String.format("%03d", i);
            facilityCodes.add(code);
        };

        // Create a list of facility sets.
        ArrayList<TreeSet<String>> facilitySets = new 
ArrayList<TreeSet<String>>();
        for (String code : facilityCodes) {
            TreeSet<String> fs = new TreeSet<String>();
            fs.add(code);
            facilitySets.add(fs);
        }

        // Populate the facility table.
        populateFacilityTable(facilityCodes);

        // Populate the facility set table.
        populateFacilitySetTable(facilitySets);

        CountDownLatch allThreadsReadyLatch = new CountDownLatch(numberThreads);
        CountDownLatch startInsertLatch = new CountDownLatch(1);
        AtomicInteger ai = new AtomicInteger(0);

        // Start all the threads.
        System.out.println("Starting " + numberThreads + " threads.");
        ArrayList<Thread> threadList = new ArrayList<Thread>(numberThreads);

        for (int i = 0; i < numberThreads; ++i) {
            Thread t = new Thread(new InsertRunnable(location,
                                                     facilitySets,
                                                     allThreadsReadyLatch,
                                                     startInsertLatch,
                                                     ai));
            t.start();
            threadList.add(t);
        }

        // Wait for all the threads to be ready to start.
        System.out.println("Waiting for " + numberThreads + " threads to be 
ready");
        allThreadsReadyLatch.await();

        // Signal all the threads to start the test.
        System.out.println("Signaling " + numberThreads + " threads to start 
INSERTing");
        startInsertLatch.countDown();

        // Join on all the threads.
        System.out.println("Joining on " + numberThreads + " threads");
        for (Thread t : threadList) {
            t.join();
            System.out.println("Joined on " + t);
        }
        System.out.println("Joins complete");

        {
            Connection c = getConnection("");
            TreeSet<String> facilitySet = getLocationFacilitySet(c, location);
            if (facilitySet.size() == numberThreads) {
                System.out.println("Final location has the expected " +
                                   "facility set with " +
                                   facilitySet.size() +
                                   " members.");
            }
            else {
                throw new java.lang.RuntimeException("Final location does " +
                                                     "have the expected " +
                                                     "facility set with " +
                                                     facilitySet.size() +
                                                     " members.");
            }
        }

        try {
            java.sql.DriverManager.getConnection("jdbc:derby:;shutdown=true");
        }
        catch (java.sql.SQLException e) {
        }
    }
}

Reply via email to