Title: [798] trunk/activerecord-jdbc: - add connection livelihood check on error
Revision
798
Author
nicksieger
Date
2007-11-13 16:34:22 -0500 (Tue, 13 Nov 2007)

Log Message

- add connection livelihood check on error
- configurable retries and connection livelihood statement, use
  config[:retry_count] (default 5) and 
  config[:connection_alive_sql] (default "select 1")

Modified Paths

Diff

Modified: trunk/activerecord-jdbc/lib/active_record/connection_adapters/jdbc_adapter.rb (797 => 798)


--- trunk/activerecord-jdbc/lib/active_record/connection_adapters/jdbc_adapter.rb	2007-11-13 18:13:46 UTC (rev 797)
+++ trunk/activerecord-jdbc/lib/active_record/connection_adapters/jdbc_adapter.rb	2007-11-13 21:34:22 UTC (rev 798)
@@ -272,6 +272,8 @@
 
       def initialize(config)
         @config = config.symbolize_keys!
+        @config[:retry_count] ||= 5
+        @config[:connection_alive_sql] ||= "select 1"
         if @config[:jndi]
           begin
             configure_jndi
@@ -304,8 +306,6 @@
       # one index, one row per column in the index), so a simple block-based
       # filter like that used for tables doesn't really work here.  Callers
       # should filter the return from this method instead.
-      #
-      # TODO: fix to use reconnect correctly
       def indexes(table_name, name = nil, schema_name = nil)
         with_connection_retry_guard do |conn|
           metadata = conn.getMetaData

Modified: trunk/activerecord-jdbc/src/java/jdbc_adapter/JdbcAdapterInternalService.java (797 => 798)


--- trunk/activerecord-jdbc/src/java/jdbc_adapter/JdbcAdapterInternalService.java	2007-11-13 18:13:46 UTC (rev 797)
+++ trunk/activerecord-jdbc/src/java/jdbc_adapter/JdbcAdapterInternalService.java	2007-11-13 21:34:22 UTC (rev 798)
@@ -58,6 +58,7 @@
 import org.jruby.RubyString;
 import org.jruby.RubySymbol;
 import org.jruby.RubyTime;
+import org.jruby.exceptions.RaiseException;
 import org.jruby.javasupport.Java;
 import org.jruby.javasupport.JavaObject;
 import org.jruby.runtime.Arity;
@@ -218,31 +219,6 @@
         return recv.getRuntime().getFalse();
     }
 
-    private static ResultSet intoResultSet(IRubyObject inp) {
-        return (ResultSet)((inp instanceof JavaObject ? ((JavaObject)inp) : (((JavaObject)(inp.getInstanceVariable("@java_object"))))).getValue());
-    }   
-
-    private static boolean isConnectionBroken(Connection c) {
-        // TODO: better way of determining if the connection is active
-        try {
-            return c.isClosed();
-        } catch (SQLException sx) {
-            return true;
-        }
-    }
-
-    private static IRubyObject setConnection(IRubyObject recv, Connection c) {
-        Connection prev = getConnection(recv);
-        if (prev != null) {
-            try {
-                prev.close();
-            } catch(Exception e) {}
-        }
-        recv.setInstanceVariable("@connection", wrappedConnection(recv,c));
-        recv.dataWrapStruct(c);
-        return recv;
-    }
-
     public static IRubyObject connection(IRubyObject recv) {
         Connection c = getConnection(recv);
         if (c == null) {
@@ -252,10 +228,7 @@
     }
 
     public static IRubyObject reconnect(IRubyObject recv) {
-        IRubyObject connection_factory = recv.getInstanceVariable("@connection_factory");
-        JdbcConnectionFactory factory = (JdbcConnectionFactory)
-                ((JavaObject) connection_factory.getInstanceVariable("@java_object")).getValue();        
-        setConnection(recv, factory.newConnection());
+        setConnection(recv, getConnectionFactory(recv).newConnection());
         return recv;
     }
 
@@ -270,17 +243,27 @@
     }
 
     private static IRubyObject withConnectionAndRetry(IRubyObject recv, SQLBlock block) {
-        final int TRIES = 10;
+        int tries = 1;
         int i = 0;
         Throwable toWrap = null;
-        while (i < TRIES) {
+        while (i < tries) {
             Connection c = getConnection(recv);
             try {
                 return block.call(c);
-            } catch (SQLException e) {
+            } catch (Exception e) {
+                Throwable root = e;
+                while (root.getCause() != null && root.getCause() != root) {
+                    root = root.getCause();
+                }
+                if (i == 0) {
+                    tries = (int) config_value(recv, "retry_count").convertToInteger().getLongValue();
+                    if (tries <= 0) {
+                        tries = 1;
+                    }
+                }
                 i++;
                 toWrap = e;
-                if (isConnectionBroken(c)) {
+                if (isConnectionBroken(recv, c)) {
                     reconnect(recv);
                 } else {
                     throw wrap(recv, e);
@@ -1020,7 +1003,64 @@
         return recv.getRuntime().newArgumentError(exception.getMessage());
     }
 
+    private static ResultSet intoResultSet(IRubyObject inp) {
+        return (ResultSet)((inp instanceof JavaObject ? ((JavaObject)inp) : (((JavaObject)(inp.getInstanceVariable("@java_object"))))).getValue());
+    }   
+
+    private static boolean isConnectionBroken(IRubyObject recv, Connection c) {
+        try {
+            IRubyObject alive = config_value(recv, "connection_alive_sql");
+            if (select_p(recv, alive).isTrue()) {
+                String connectionSQL = alive.toString();
+                Statement s = c.createStatement();
+                try {
+                    s.execute(connectionSQL);
+                } finally {
+                    try { s.close(); } catch (SQLException ignored) {}
+                }
+                return true;
+            } else {
+                return !c.isClosed();
+            }
+        } catch (SQLException sx) {
+            return true;
+        }
+    }
+
+    private static IRubyObject setConnection(IRubyObject recv, Connection c) {
+        Connection prev = getConnection(recv);
+        if (prev != null) {
+            try {
+                prev.close();
+            } catch(Exception e) {}
+        }
+        recv.setInstanceVariable("@connection", wrappedConnection(recv,c));
+        recv.dataWrapStruct(c);
+        return recv;
+    }
+
     private static IRubyObject wrappedConnection(IRubyObject recv, Connection c) {
         return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), c), Block.NULL_BLOCK);
     }
-}
+
+    private static JdbcConnectionFactory getConnectionFactory(IRubyObject recv) throws RaiseException {
+        IRubyObject connection_factory = recv.getInstanceVariable("@connection_factory");
+        JdbcConnectionFactory factory = null;
+        try {
+            factory = (JdbcConnectionFactory) ((JavaObject) connection_factory.getInstanceVariable("@java_object")).getValue();
+        } catch (Exception e) {
+            factory = null;
+        }
+        if (factory == null) {
+            throw recv.getRuntime().newRuntimeError("@connection_factory not set properly");
+        }
+        return factory;
+    }
+
+    private static IRubyObject config_value(IRubyObject recv, String key) {
+        Ruby runtime = recv.getRuntime();
+        IRubyObject config_hash = recv.getInstanceVariable("@config");
+        return config_hash.callMethod(runtime.getCurrentContext(), 
+                "[]", new IRubyObject[] {runtime.newSymbol(key)}, Block.NULL_BLOCK);
+    }
+}
\ No newline at end of file

Modified: trunk/activerecord-jdbc/test/simple.rb (797 => 798)


--- trunk/activerecord-jdbc/test/simple.rb	2007-11-13 18:13:46 UTC (rev 797)
+++ trunk/activerecord-jdbc/test/simple.rb	2007-11-13 21:34:22 UTC (rev 798)
@@ -1,3 +1,5 @@
+ActiveRecord::Schema.verbose = false
+
 module MigrationSetup
   def setup
     CreateEntries.up
@@ -110,9 +112,26 @@
 
   def test_invalid
     e = Entry.new(:title => @title, :content => @content, :rating => ' ')
-    p e.valid?
+    assert e.valid?
   end
 
+  def test_reconnect
+    @connection.reconnect!
+    assert_equal 1, Entry.count
+  end
+
+  def test_connection_valid
+    assert_raises(ArgumentError) do
+      @connection.raw_connection.with_connection_retry_guard do |c|
+        begin
+          stmt = c.createStatement
+          stmt.execute "bogus sql"
+        ensure
+          stmt.close rescue nil
+        end
+      end
+    end
+  end
 end
 
 module MultibyteTestMethods
_______________________________________________
Jruby-extras-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/jruby-extras-devel

Reply via email to