db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject svn commit: r772090 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/store/access/btree/ testing/org/apache/derbyTesting/functionTests/tests/store/
Date Wed, 06 May 2009 07:53:33 GMT
Author: kahatlen
Date: Wed May  6 07:53:32 2009
New Revision: 772090

URL: http://svn.apache.org/viewvc?rev=772090&view=rev
Log:
DERBY-4193: ASSERT FAILED Scan position already saved with multi-threaded insert/update/delete

Forget about the current position before trying to reposition on the
end point of a scan. Then the ASSERT won't be confused if it needs to
save the position again (it fails if it finds that a position is
already saved).

BTreeScan.positionAtStartForBackwardScan() also had this problem, but
since it doesn't have any callers, this method was removed instead of
being fixed.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java?rev=772090&r1=772089&r2=772090&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java
Wed May  6 07:53:32 2009
@@ -387,7 +387,9 @@
             if (latch_released)
             {
                 // lost latch on pos.current_leaf, search the tree again.
-                pos.current_leaf = null;
+                // Forget the current position since we'll reposition on the
+                // rightmost key, which is not necessarily the saved position.
+                pos.init();
                 continue;
             }
             else

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java?rev=772090&r1=772089&r2=772090&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
Wed May  6 07:53:32 2009
@@ -457,7 +457,9 @@
             if (latch_released)
             {
                 // lost latch on pos.current_leaf, search the tree again.
-                pos.current_leaf = null;
+                // Forget the current position since we'll use the start key
+                // to reposition on the start of the scan.
+                pos.init();
                 continue;
             }
             else
@@ -474,163 +476,6 @@
 	}
 
     /**
-     * Position scan at "start" position for a backward scan.
-     * <p>
-     * Positions the scan to the slot just after the first record to be 
-     * returned from the backward scan.  Returns the start page latched, and 
-     * sets "current_slot" to the slot number just right of the first slot
-     * to return.
-     * <p>
-     *
-	 * @exception  StandardException  Standard exception policy.
-     **/
-    protected void positionAtStartForBackwardScan(
-    BTreeRowPosition    pos)
-        throws StandardException
-	{
-		boolean         exact;
-
-        // This routine should only be called from first next() call //
-        if (SanityManager.DEBUG)
-        {
-            SanityManager.ASSERT(
-                (this.scan_state == SCAN_INIT) || 
-                (this.scan_state == SCAN_HOLD_INIT));
-
-            SanityManager.ASSERT(pos.current_rh          == null);
-            SanityManager.ASSERT(pos.current_positionKey         == null);
-        }
-
-        // Loop until you can lock the row previous to the first row to be
-        // returned by the scan, while holding the page latched, without
-        // waiting.  If you have to wait, drop the latch, wait for the lock -
-        // which makes it likely if you wait for the lock you will loop just
-        // once, find the same lock satisfies the search and since you already
-        // have the lock it will be granted.
-        while (true)
-        {
-            // Find the starting page and row slot, must start at root and
-            // search either for leftmost leaf, or search for specific key.
-            ControlRow root = ControlRow.get(this, BTree.ROOTPAGEID); 
-
-            // include search of tree in page visited stats.
-            stat_numpages_visited += root.getLevel() + 1;
-
-            if (init_startKeyValue == null)
-            {
-                // No start given, position at last slot + 1 of rightmost leaf 
-                pos.current_leaf = (LeafControlRow) root.searchRight(this);
-
-                pos.current_slot = pos.current_leaf.page.recordCount();
-                exact     = false;
-            }
-            else
-            {
-                /*
-                if (SanityManager.DEBUG)
-                    SanityManager.THROWASSERT(
-                        "Code not ready yet for positioned backward scans.");
-                        */
-
-
-                if (SanityManager.DEBUG)
-                    SanityManager.ASSERT(
-                        (init_startSearchOperator == ScanController.GE) ||
-                        (init_startSearchOperator == ScanController.GT));
-
-                // Search for the starting row.
-
-                SearchParameters sp = new SearchParameters(
-                    init_startKeyValue, 
-                    ((init_startSearchOperator == ScanController.GE) ? 
-                        SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH : 
-                        SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH),
-                    init_template, this, false);
-
-                pos.current_leaf = (LeafControlRow) root.search(sp);
-
-                pos.current_slot = sp.resultSlot;
-                exact     = sp.resultExact;
-
-                // The way that backward scans are used, the caller calls next()
-                // to position on the first row.  If the result of the
-                // search that found the starting page and slot was not
-                // exact, then the page/slot will refer to the row before
-                // the first qualifying row.  The first call to next()
-                // will therefore move to the first (potentially) qualifying
-                // row.  However, if the search was exact, then we don't
-                // want to move the position on the first call to next.
-                // In that case, by decrementing the slot, the first call
-                // to next will put us back	on the starting row.
-
-
-                if (exact)
-                {
-                    // the search has found exactly the start position key 
-                    if (init_startSearchOperator == ScanController.GE)
-                    {
-                        // insure backward scan returns this row by moving
-                        // slot to one after this row.
-                        pos.current_slot++;
-                    }
-                    else
-                    {
-                        // no work necessary leave startslot positioned on the
-                        // row, we will skip this record
-                        if (SanityManager.DEBUG)
-                            SanityManager.ASSERT(
-                                init_startSearchOperator == ScanController.GT);
-                    }
-                }
-                else
-                {
-                    // the search positioned one before the start position key,
-                    // move it to one "after"
-                    pos.current_slot++;
-                }
-            }
-
-            boolean latch_released = 
-                !this.getLockingPolicy().lockScanRow(
-                    this, this.getConglomerate(), pos,
-                    init_lock_fetch_desc,
-                    pos.current_lock_template,
-                    pos.current_lock_row_loc,
-                    true, init_forUpdate, lock_operation);
-
-            // special test to see if latch release code works
-            if (SanityManager.DEBUG)
-            {
-                latch_released = 
-                    test_errors(
-                        this,
-                        "BTreeScan_positionAtStartPosition", pos,
-                        this.getLockingPolicy(), pos.current_leaf, latch_released);
-            }
-
-            if (latch_released)
-            {
-                // lost latch on pos.current_leaf, search the tree again.
-                pos.current_leaf = null;
-                continue;
-            }
-            else
-            {
-                // success! got all the locks, while holding the latch.
-                break;
-            }
-        }
-
-        this.scan_state          = SCAN_INPROGRESS;
-
-        if (SanityManager.DEBUG)
-            SanityManager.ASSERT(pos.current_leaf != null);
-
-        // System.out.println("backward scan end start position: " +
-        //       " current_slot = " + this.current_slot );
-	}
-
-    /**
      * Position scan to 0 slot on next page.
      * <p>
      * Position to next page, keeping latch on previous page until we have 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java?rev=772090&r1=772089&r2=772090&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java
Wed May  6 07:53:32 2009
@@ -532,6 +532,165 @@
     // method too.
 
     // --------------------------------------------------------------------
+    // Test cases for bugs related to saving position and repositioning
+    // --------------------------------------------------------------------
+
+    /**
+     * Test that a max scan works when it needs to wait more than once in order
+     * to lock the last record in the index. This used to cause an assert
+     * failure in sane builds before DERBY-4193.
+     */
+    public void testMultipleLastKeyWaitsInMaxScan() throws Exception {
+        setAutoCommit(false);
+
+        // Create a table with an index and a couple of rows.
+        Statement s = createStatement();
+        s.execute("create table max_scan(x int, y int)");
+        s.execute("create index idx on max_scan(x)");
+        s.execute("insert into max_scan(x) values 1,2,3");
+        commit();
+
+        // Start a thread that (1) obtains an exclusive lock on the last
+        // row, (2) waits for the main thread to perform a max scan that will
+        // be blocked by the lock, (3) inserts values greater than the current
+        // max so that the main thread needs to rescan when it wakes up, (4)
+        // commit to allow the main thread to continue, and (5) immediately
+        // insert more rows greater than the previous max so that the main
+        // thread is likely to have to wait for a lock a second time.
+        new AsyncThread(new AsyncTask() {
+            public void doWork(Connection conn) throws Exception {
+                conn.setAutoCommit(false);
+                Statement s = conn.createStatement();
+                s.execute("update max_scan set y = x where x = 3");
+                s.close();
+
+                // Give the main thread time to start executing select max(x)
+                // and wait for the lock.
+                Thread.sleep(2000);
+
+                // Insert rows greater than the current max.
+                PreparedStatement ps = conn.prepareStatement(
+                        "insert into max_scan(x) values 4");
+                for (int i = 0; i < 300; i++) {
+                    ps.execute();
+                }
+
+                // Commit and release locks to allow the main thread to
+                // continue.
+                conn.commit();
+
+                // Insert some more rows so that the main thread is likely to
+                // have to wait again. Note that there is a possibility that
+                // the main thread manages to obtain the lock on the last row
+                // before we manage to insert a new row, in which case it
+                // won't have to wait for us and we're not actually testing
+                // a max scan that needs to wait more than once to lock the
+                // last row.
+                for (int i = 0; i < 300; i++) {
+                    ps.execute();
+                }
+
+                // Block for a while before releasing locks, so that the main
+                // thread will have to wait if it didn't obtain the lock on the
+                // last row before we did.
+                Thread.sleep(500);
+                conn.commit();
+
+                ps.close();
+            }
+        });
+
+        // Give the other thread a little while to start and obtain the
+        // lock on the last record.
+        Thread.sleep(1000);
+
+        // The last record should be locked now, so this call will have to
+        // wait initially. This statement used to cause an assert failure in
+        // debug builds before DERBY-4193.
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select max(x) from max_scan " +
+                               "--DERBY-PROPERTIES index=IDX"),
+                "4");
+    }
+
+    /**
+     * Test that a forward scan works even in the case that it has to wait
+     * for the previous key lock more than once. This used to cause an assert
+     * failure in sane builds before DERBY-4193.
+     */
+    public void testMultiplePrevKeyWaitsInForwardScan() throws Exception {
+        setAutoCommit(false);
+
+        // Isolation level should be serializable so that the scan needs
+        // a previous key lock.
+        getConnection().setTransactionIsolation(
+                Connection.TRANSACTION_SERIALIZABLE);
+
+        // Create a table with an index and a couple of rows.
+        Statement s = createStatement();
+        s.execute("create table fw_scan(x int)");
+        s.execute("create index idx on fw_scan(x)");
+        s.execute("insert into fw_scan(x) values 100,200,300");
+        commit();
+
+        new AsyncThread(new AsyncTask() {
+            public void doWork(Connection conn) throws Exception {
+                conn.setAutoCommit(false);
+                PreparedStatement ps =
+                        conn.prepareStatement("insert into fw_scan values 1");
+
+                // Insert one row right before the first row to be returned
+                // by the scan. This will be the previous key that the scan
+                // will attempt to lock. Wait for two seconds to allow the
+                // scan to start and attempt to lock the record.
+                ps.execute();
+                Thread.sleep(2000);
+
+                // Before we commit and release the lock, insert more rows
+                // between the locked row and the first row of the scan, so
+                // that another row holds the previous key for the scan when
+                // it wakes up.
+                for (int i = 0; i < 300; i++) {
+                    ps.execute();
+                }
+                conn.commit();
+
+                // The scan will wake up and try to lock the row that has
+                // now become the row immediately to the left of its starting
+                // position. Try to beat it to it so that it has to wait a
+                // second time in order to obtain the previous key lock. This
+                // used to trigger an assert failure in the scan before
+                // DERBY-4193.
+                for (int i = 0; i < 300; i++) {
+                    ps.execute();
+                }
+
+                // Wait a little while to give the scan enough time to wake
+                // up and make another attempt to lock the previous key before
+                // we release the locks.
+                Thread.sleep(500);
+                conn.rollback();
+                ps.close();
+            }
+        });
+
+        // Give the other thread a second to start and obtain a lock that
+        // blocks the scan.
+        Thread.sleep(1000);
+
+        // The key to the left of the first key to be returned by the scan
+        // should be locked now. This call will have to wait for the previous
+        // key lock at least once. If it has to wait a second time (dependent
+        // on the exact timing between this thread and the other thread) the
+        // assert error from DERBY-4193 will be exposed.
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select x from fw_scan " +
+                               "--DERBY-PROPERTIES index=IDX\n" +
+                               "where x >= 100 and x < 200"),
+                "100");
+    }
+
+    // --------------------------------------------------------------------
     // Helpers
     // --------------------------------------------------------------------
 



Mime
View raw message