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 bp...@apache.org on 2007/04/18 23:15:21 UTC
svn commit: r530159 - in /db/derby/code/trunk/java:
engine/org/apache/derby/impl/jdbc/ testing/
testing/org/apache/derbyTesting/functionTests/tests/memory/
Author: bpendleton
Date: Wed Apr 18 14:15:21 2007
New Revision: 530159
URL: http://svn.apache.org/viewvc?view=rev&rev=530159
Log:
DERBY-2480: getConnection leaks memory when connecting to non-existent db
This patch was contributed by Jeff Clary (jclary@actuate.com). The test
program was contributed by John Embretsen (John.Embretsen@Sun.com) based
on the original reproduction program contributed by Jeff Clary.
The issue is that repeated calls to java.sql.DriverManager.getConnection(
"jdbc:derby:C:\\DOES_NOT_EXIST") leak memory and eventually lead to an
OutOfMemoryError.
This bug is similar to DERBY-1947 in that ContextManager objects are not
getting removed from the HashSet.
The change adds a call to cleanupOnError to EmbedConnection's constructor.
Added:
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/ConnectionHandlingJunit.java (with props)
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/build.xml (with props)
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
db/derby/code/trunk/java/testing/build.xml
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java?view=diff&rev=530159&r1=530158&r2=530159
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java Wed Apr 18 14:15:21 2007
@@ -369,6 +369,7 @@
throw NO_MEM;
}
catch (Throwable t) {
+ tr.cleanupOnError(t);
throw handleException(t);
} finally {
restoreContextStack();
Modified: db/derby/code/trunk/java/testing/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/build.xml?view=diff&rev=530159&r1=530158&r2=530159
==============================================================================
--- db/derby/code/trunk/java/testing/build.xml (original)
+++ db/derby/code/trunk/java/testing/build.xml Wed Apr 18 14:15:21 2007
@@ -75,6 +75,7 @@
<ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/tests/i18n"/>
<ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/tests/jdbc4"/>
<ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/tests/perf"/>
+ <ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/tests/memory"/>
<ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/tests/upgradeTests"/>
<ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/tests/largedata"/>
<ant dir="${derby.testing.src.dir}/${derby.testing.functest.dir}/multi/stress"/>
Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/ConnectionHandlingJunit.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/ConnectionHandlingJunit.java?view=auto&rev=530159
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/ConnectionHandlingJunit.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/ConnectionHandlingJunit.java Wed Apr 18 14:15:21 2007
@@ -0,0 +1,244 @@
+/**
+ * Derby - Class org.apache.derbyTesting.functionTests.tests.memory.ConnectionHandlingJunit
+ *
+ * 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.memory;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.JDBC;
+
+/**
+ * This class tests Derby's ability to handle multiple connection attempts
+ * against one or more databases, which may or may not exist. Such repeated
+ * connection attempts have been known to cause OutOfMemoryErrors in the past,
+ * see for example DERBY-2480.
+ */
+public class ConnectionHandlingJunit extends BaseJDBCTestCase {
+
+ /** Creates a new instance of this test class
+ * @param name The name of this test instance; may determine which test
+ * fixture to run.
+ */
+ public ConnectionHandlingJunit(String name) {
+ super(name);
+ }
+
+ /**
+ * Creates a Test Suite to be run by a JUnit TestRunner. The elements of
+ * the test suite may depend on the environment in which the TestRunner
+ * is running (classpath, JVM, etc.).
+ *
+ * @return JUnit test suite containing appropriate tests from this class.
+ */
+ public static Test suite() {
+
+ TestSuite suite = new TestSuite("ConnectionHandlingJUnit");
+
+ // Only support for java.sql.DriverManager has been implemented.
+ if (JDBC.vmSupportsJDBC2()) {
+ /* driverMgrTestConnectionsToNonexistentDb:
+ * Only support for DriverManager (JDBC2 and above) for now, for
+ * simplicity (connecting to DB and possibly also loading the driver
+ * manually, to have control over url attributes (database
+ * creation)). This adheres to the following advice in
+ * ...derbyTesting.junit.Connector.java:
+ * "Tests that need finer control over the connection handling
+ * should use the JDBC classes directly, such as DriverManager
+ * or DataSource."
+ */
+
+ TestCase nonExistentDbTest = new ConnectionHandlingJunit(
+ "driverMgrTestConnectionsToNonexistentDb");
+
+ /* run "driverMgrTestConnectionsToNonexistentDb" in embedded mode only
+ * by default, since it is not very useful to continue running in
+ * client/server mode (server in the same JVM) if the JVM's memory
+ * resources are <i>almost</i> exhausted from the embedded test.
+ */
+ suite.addTest(nonExistentDbTest);
+ // to run the test in client/server mode, comment the above line,
+ // uncomment the next and recompile.
+ //suite.addTest(TestConfiguration.clientServerDecorator(nonExistentDbTest));
+
+ }
+
+ return suite;
+ }
+
+
+ /**
+ * <p>This fixture tries a number of times to connect to a non-existent
+ * database, in order to test Derby's ability to handle this situation
+ * without running out of resources (for example Java heap space (memory)).
+ * See
+ * <a href="https://issues.apache.org/jira/browse/DERBY-2480">DERBY-2480</a>
+ * for details.</p>
+ * <p>This test fixture is currently not part of any large JUnit suite
+ * because <b>1)</b> the default number of connection attempts is rather
+ * large, and takes some time to complete (depending on hardware), and
+ * <b>2)</b> if the tested Derby version is still vulnerable to DERBY-2480
+ * or similar issues the JVM will most likely run out of memory (depending
+ * on heap settings), causing subsequent tests to fail, hang or not run at
+ * all.</p>
+ * <p>
+ * <b>Note:</b> The JVM may slow down significantly (even appear to hang)
+ * before an OOME is thrown. Depending on the avaliable resources, the error
+ * may or may not be reported in the logs (derby.log, server console).</p>
+ * <p>
+ * This fixture requires java.sql.DriverManager. This is because simple
+ * and easy control of the connection handling and database creation is
+ * desired (see implementation comments). However, the test logic itself
+ * should also work with other connection mechanisms.</p>
+ *
+ * @throws SQLException if an unexpected exception occurs that is not
+ * examined using assertions.
+ */
+ public void driverMgrTestConnectionsToNonexistentDb() throws SQLException {
+
+ Connection myInvalidConn = null;
+
+ String url = getTestConfiguration().getJDBCUrl("nonexistentDatabase");
+ // Not using the regular helper methods in super class because
+ // we don't want to actually create a database, or connect to an
+ // existing one (current helper classes add ";create=true" if the DB
+ // does not exist). Hence, the JDBC driver will not necessarily be
+ // loaded automatically.
+ loadDriver(url);
+
+ Runtime runtime = Runtime.getRuntime();
+ double memTotalNow; // Total amount of heap memory committed to this JVM
+
+ // ~110k attempts is enough for a 64 MB heap (prior to DERBY-2480 fix)
+ int maxCount = 130000; // max number of connection attempts to try
+ int count = 0; // number of connection attempts so far
+
+ println("Trying " + maxCount + " connection attempts...");
+
+ try {
+ while (count < maxCount) {
+
+ try {
+ // We are expecting an exception here because we are trying to
+ // connect to a DB that does not exist.
+ myInvalidConn = DriverManager.getConnection(url);
+ // The following may happen because of changes to helper methods
+ // such as TestConfiguration.getJDBCUrl(dbName).
+ fail("Got connection to a DB that should not exist");
+ } catch (SQLException e) {
+ // Expected SQLState for "database not found"...
+ // For the client driver the generic 08004 is returned.
+ String expectedState;
+ if (getTestConfiguration().getJDBCClient().isEmbedded()) {
+ // embedded driver
+ expectedState = "XJ004";
+ // in embedded mode, OOMEs are usually wrapped in
+ // SQLExceptions with SQLState 08004
+ if (e.getSQLState().equals("08004")
+ && e.getMessage().matches(".*OutOfMemoryError.*")) {
+ alarm("OutOfMemoryError after " + count
+ + " connection attempts to a "
+ + "non-existing database!");
+ // test should fail on next assertSQLState call,
+ // but may not have enough resources to get that far
+ printStackTrace(e);
+ //fail("OutOfMemoryError: " + e.getMessage());
+ }
+ } else {
+ // client driver
+ expectedState = "08004";
+ // with client driver, OOMEs are often wrapped in
+ // SQLExceptions with SQLState XJ001
+ if (e.getSQLState().equals("XJ001")
+ && e.getMessage().matches(".*OutOfMemoryError.*")) {
+ alarm("OutOfMemoryError after " + count
+ + " connection attempts to a "
+ + "non-existing database!");
+ // test should fail on next assertSQLState call,
+ // but may not have enough resources to do so
+ printStackTrace(e);
+ }
+ }
+ assertSQLState("Wrong SQLState for non-existent database",
+ expectedState, e);
+ }
+
+ count++;
+ // print informational messages (mem) if debug property is true
+ if (getTestConfiguration().isVerbose()) {
+ if (count % 1000 == 0) {
+ memTotalNow = runtime.totalMemory()/(double)(1024*1024);
+ println("Iteration: " + count + "\tTotal memory (MB): "
+ + memTotalNow);
+ }
+ }
+ }
+ } catch(OutOfMemoryError oome) {
+ alarm("OutOfMemory after " + count + " connection attempts!");
+ alarm(oome.getMessage());
+ throw oome;
+ }
+ }
+
+ /**
+ * Will check if the JDBC driver has been loaded and load it if that is not
+ * the case.
+ * Any other exception messages than "No suitable driver" on the first
+ * attempt to get the JDBC driver will result in an assertion failure.
+ *
+ * @param url a valid connection URL for the desired JDBC driver
+ * @throws SQLException if an unexpected exception is thrown
+ */
+ private void loadDriver(String url) throws SQLException {
+ try {
+ DriverManager.getDriver(url);
+ } catch (SQLException e) {
+ // getDriver() failed, JDBC driver probably not loaded.
+ // Expecting SQLState 08001 and message "No suitable driver"...
+ assertSQLState("Unexpected SQLState from getDriver().", "08001", e);
+ assertEquals("Unexpected exception message from getDriver(), ",
+ "No suitable driver", e.getMessage());
+ String driverClass =
+ getTestConfiguration().getJDBCClient().getJDBCDriverName();
+ println("Loading JDBC driver " + driverClass);
+ // load the driver
+ try {
+ Class.forName(driverClass).newInstance();
+ } catch (ClassNotFoundException cnfe) {
+ throw new SQLException("Failed to load JDBC driver '"
+ + driverClass + "', ClassNotFoundException: "
+ + cnfe.getMessage());
+ } catch (IllegalAccessException iae) {
+ throw new SQLException("Failed to load JDBC driver '"
+ + driverClass + "', IllegalAccessException: "
+ + iae.getMessage());
+ } catch (InstantiationException ie) {
+ throw new SQLException("Failed to load JDBC driver '"
+ + driverClass + "', InstantiationException: "
+ + ie.getMessage());
+ }
+ }
+ }
+
+}
Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/ConnectionHandlingJunit.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/build.xml?view=auto&rev=530159
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/build.xml (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/build.xml Wed Apr 18 14:15:21 2007
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<project default="memorytests" basedir="../../../../../../..">
+
+<!-- Properties -->
+
+ <!-- User settings -->
+ <property file="${user.home}/ant.properties"/>
+ <!-- Set property lib dir -->
+ <property name="properties.dir" value="tools/ant/properties"/>
+ <!-- Significant dirs -->
+ <property file="${properties.dir}/dirs.properties"/>
+ <property file="${properties.dir}/derbytesting.properties"/>
+ <property file="${user.home}/properties/derbytesting.properties"/>
+ <property file="${ant.home}/properties/derbytesting.properties"/>
+ <!-- Compiler settings -->
+ <property file="${properties.dir}/${build.compiler}.properties"/>
+ <!-- Compile-time classpath properties files -->
+ <property file="${properties.dir}/extrapath.properties"/>
+ <property file="${properties.dir}/derbytesting.properties"/>
+ <property file="${properties.dir}/compilepath.properties"/>
+
+<!-- Targets -->
+
+ <target name="memorytests"
+ description="Build Derby memory tests">
+ <javac
+ source="1.4"
+ target="1.4"
+ bootclasspath="${empty}"
+ nowarn="on"
+ debug="true"
+ depend="${depend}"
+ deprecation="${deprecation}"
+ optimize="${optimize}"
+ proceed="${proceed}"
+ verbose="${verbose}"
+ srcdir="${derby.testing.src.dir}"
+ destdir="${out.dir}">
+ <classpath>
+ <pathelement path="${compile.classpath}"/>
+ <pathelement path="${junit}"/>
+ </classpath>
+ <include name="${derby.testing.functest.dir}/tests/memory/*.java"/>
+ </javac>
+ </target>
+
+</project>
+
Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memory/build.xml
------------------------------------------------------------------------------
svn:eol-style = native