You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2009/02/19 14:49:29 UTC
svn commit: r745866 - in
/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store:
IndexSplitDeadlockTest.java _Suite.java
Author: kahatlen
Date: Thu Feb 19 13:49:28 2009
New Revision: 745866
URL: http://svn.apache.org/viewvc?rev=745866&view=rev
Log:
DERBY-2991: Index split deadlock
Added a JUnit test that exercises code paths that will be touched by
the fix for the index split deadlock. The test is disabled until the
fix has been checked in.
Added:
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java (with props)
Modified:
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java
Added: 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=745866&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java Thu Feb 19 13:49:28 2009
@@ -0,0 +1,269 @@
+/*
+ * Derby - Class org.apache.derbyTesting.functionTests.tests.store.IndexSplitDeadlockTest
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+package org.apache.derbyTesting.functionTests.tests.store;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import junit.framework.Test;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
+import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
+import org.apache.derbyTesting.junit.JDBC;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+/**
+ * Test that executes the code paths changed by the fix for the index split
+ * deadlock (DERBY-2991). The main purpose is to test that index scans are
+ * able to reposition in cases where they release the latch on the leaf page
+ * on which they are positioned (typically because they had to wait for a
+ * lock, or because they returned control to the caller after fetching a
+ * bulk of rows).
+ */
+public class IndexSplitDeadlockTest extends BaseJDBCTestCase {
+
+ /** List of obstructor threads to wait for after running the test. */
+ private List obstructors;
+
+ public IndexSplitDeadlockTest(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ Test test = TestConfiguration.embeddedSuite(
+ IndexSplitDeadlockTest.class);
+ test = new CleanDatabaseTestSetup(test);
+ test = DatabasePropertyTestSetup.setLockTimeouts(test, 2, 4);
+ return test;
+ }
+
+ protected void tearDown() throws Exception {
+ rollback();
+ getConnection().setAutoCommit(false); // required by JDBC.dropSchema()
+ JDBC.dropSchema(getConnection().getMetaData(), "APP");
+
+ // Go through all the obstructors and call waitFor() so that we
+ // detect errors that happened in another thread.
+ if (obstructors != null) {
+ for (Iterator it = obstructors.iterator(); it.hasNext(); ) {
+ Obstructor o = (Obstructor) it.next();
+ o.waitFor();
+ }
+ }
+ obstructors = null;
+
+ super.tearDown();
+ }
+
+ // --------------------------------------------------------------------
+ // Test cases for calls to BTreeScan.reposition() in BTreeMaxScan
+ // --------------------------------------------------------------------
+
+ // NOTE: There is a call in fetchMax() that cannot be reached because the
+ // scan state is alway SCAN_INIT when that method is called, and it only
+ // calls reposition() if the scan state is SCAN_INPROGRESS. Therefore,
+ // there's no test case for fetchMax().
+
+ public void testBTreeMaxScan_fetchMaxRowFromBeginning() throws Exception {
+ getConnection().setAutoCommit(false);
+
+ Statement s = createStatement();
+ s.executeUpdate("create table max_scan(x int)");
+ s.executeUpdate("create index idx on max_scan(x)");
+
+ // We need to make sure that we have at least two leaf pages. Each
+ // 4K index page can hold ~200 rows.
+ PreparedStatement ins = prepareStatement(
+ "insert into max_scan values ?");
+ for (int i = 0; i < 500; i++) {
+ ins.setInt(1, i * 2);
+ ins.executeUpdate();
+ }
+ commit();
+
+ // Now make sure that the right-most leaf is empty, so that we must
+ // fetch the max value from the beginning.
+ s.executeUpdate("delete from max_scan where x > 50");
+
+ // Obtain lock in another thread to block scans. Release lock after
+ // two seconds.
+ obstruct("update max_scan set x = x where x = 10", 2000);
+
+ // Give the other thread time to obtain the lock.
+ Thread.sleep(1000);
+
+ // Perform a max scan (from beginning because last page is empty).
+ // Will force repositioning because we must wait for the lock and
+ // release the latch.
+ JDBC.assertSingleValueResultSet(s.executeQuery(
+ "select max(x) from max_scan --DERBY-PROPERTIES index=IDX"),
+ "50");
+ }
+
+ // --------------------------------------------------------------------
+ // Test cases for calls to BTreeScan.reposition() in BTreeForwardScan
+ // --------------------------------------------------------------------
+
+ /**
+ * Test first call to reposition() in BTreeForwardScan.fetchRows().
+ * This call happens when a new batch of rows is requested from a scan
+ * that's in progress.
+ */
+ public void testBTreeForwardScan_fetchRows1() throws SQLException {
+
+ // Create a table and an index and populate them
+ Statement s = createStatement();
+ s.executeUpdate("create table t (x int)");
+ s.executeUpdate("create index idx on t(x)");
+ PreparedStatement ins = prepareStatement("insert into t values ?");
+ for (int i = 0; i < 400; i++) {
+ ins.setInt(1, i);
+ ins.executeUpdate();
+ }
+
+ // Start an index scan and fetch some rows so that it's in the
+ // INPROGRESS state. Just fetch a small number of rows so that we
+ // are still positioned on the left-most leaf page.
+ ResultSet rs = s.executeQuery(
+ "select * from t --DERBY-PROPERTIES index=IDX");
+ for (int i = 0; i < 30; i++) {
+ assertTrue(rs.next());
+ assertEquals(i, rs.getInt(1));
+ }
+
+ // In another transaction, insert values smaller than the values
+ // currently in the index. This causes a split of the left-most leaf.
+ // Before DERBY-2991 we'd get a lock timeout here.
+ Connection c2 = openDefaultConnection();
+ Statement s2 = c2.createStatement();
+ for (int i = 0; i < 300; i++) {
+ s2.executeUpdate("insert into t values -1");
+ }
+ s2.close();
+ c2.close();
+
+ // Continue the index scan. This will trigger a full repositioning
+ // from the root of the B-tree since the page on which we were
+ // positioned has been split.
+ for (int i = 30; i < 400; i++) {
+ assertTrue(rs.next());
+ assertEquals(i, rs.getInt(1));
+ }
+ assertFalse(rs.next());
+ }
+
+ // --------------------------------------------------------------------
+ // Helpers
+ // --------------------------------------------------------------------
+
+ /**
+ * <p>
+ * In a separate thread, and in a separate transaction, execute the
+ * SQL text and wait for the specified period of time, before the
+ * transaction is rolled back. This method can be used to hold locks
+ * and thereby block the main thread for a certain amount of time.
+ * </p>
+ *
+ * <p>
+ * If an exception is thrown while executing the SQL, the exception is
+ * stored and rethrown from the tearDown() method in the main execution
+ * thread, so that it is detected by the JUnit framework.
+ * </p>
+ *
+ * @param sql the SQL text to execute
+ * @param blockMillis how many milliseconds to wait until the transaction
+ * is rolled back
+ */
+ private void obstruct(String sql, long blockMillis) {
+ if (obstructors == null) {
+ obstructors = new ArrayList();
+ }
+ obstructors.add(new Obstructor(sql, blockMillis));
+ }
+
+ /**
+ * Helper class for the obstruct() method. Executes SQL in a separate
+ * thread and stores any exceptions thrown.
+ */
+ private class Obstructor implements Runnable {
+ private final String sql;
+ private final long blockMillis;
+ private final Thread thread;
+ private Exception error;
+
+ /**
+ * Create and start an obstructor thread.
+ * @param sql the SQL text to execute
+ * @param blockMillis the time in milliseconds to keep the
+ * transaction active
+ */
+ Obstructor(String sql, long blockMillis) {
+ this.sql = sql;
+ this.blockMillis = blockMillis;
+ thread = new Thread(this);
+ thread.start();
+ }
+
+ /**
+ * Run the SQL in a separate transaction and block for the specified
+ * amount of time.
+ */
+ public void run() {
+ try {
+ Connection c = openDefaultConnection();
+ try {
+ c.setAutoCommit(false);
+ Statement s = c.createStatement();
+ s.execute(sql);
+ s.close();
+ Thread.sleep(blockMillis);
+ } finally {
+ c.rollback();
+ c.close();
+ }
+ } catch (Exception e) {
+ error = e;
+ }
+ }
+
+ /**
+ * Wait for the obstructor thread to complete. If an error occurred
+ * while the thread was running, the exception will be rethrown by
+ * this method.
+ *
+ * @throws Exception if an error occurred while the thread was running
+ */
+ void waitFor() throws Exception {
+ thread.join();
+ Exception e = error;
+ error = null;
+ if (e != null) {
+ throw e;
+ }
+ }
+
+ }
+
+}
Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/IndexSplitDeadlockTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java?rev=745866&r1=745865&r2=745866&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java Thu Feb 19 13:49:28 2009
@@ -60,6 +60,10 @@
suite.addTest(OfflineBackupTest.suite());
suite.addTest(LiveLockTest.suite());
suite.addTest(ClobReclamationTest.suite());
+
+ // Disabled until DERBY-2991 has been fixed
+ //suite.addTest(IndexSplitDeadlockTest.suite());
+
// Encryption only supported for Derby in J2SE/J2EE environments.
// J2ME (JSR169) does not support encryption.
if (JDBC.vmSupportsJDBC3()) {