ActiveRecord Transaction Not Rolled back on a Java OutOfMemoryError

If I raise a regular exception after the save the enclosing transaction
is rolled back. But if the OutOfMemoryError exception occurs the
transaction is committed. Am I missing something obvious?

(Java 1.6.0_03, JRuby 1.3.1, activerecord 2.3.2, active-jdbc-adapter
0.9, Oracle 10g, oracle jdbc driver 9.0.2.0.0)

require ‘rubygems’
gem ‘activerecord-jdbc-adapter’
require ‘active_record’
require ‘yaml’

class Trader < ActiveRecord::Base
end

class Demo
def connect
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(YAML::load_file(‘oome.yml’)['dev
'])
end

def create_trader
ActiveRecord::Base.connection.transaction do
trader = Trader.new
trader.name = ‘Darcy’
trader.save
#force an OutOfMemoryError
a = ‘’
1000000.times do
a << String.new(‘a’*10000)
end
end
end
end

demo = Demo.new
demo.connect
demo.create_trader

This email communication and any files transmitted with it may contain
confidential and or proprietary information and is provided for the use
of the intended recipient only. Any review, retransmission or
dissemination of this information by anyone other than the intended
recipient is prohibited. If you receive this email in error, please
contact the sender and delete this communication and any copies
immediately. Thank you.


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

As a follow-up:

begin
a = ‘’
1000000.times {a << String.new(‘a’*10000)}
rescue Exception => e
puts “Caught NoMemoryError in Ruby”
End

Prints “Caught NoMemoryError in Ruby” when executed in Ruby. Nothing
when executed in Jruby. In our earlier case the consequence is that the
transaction is erroneously committed to the database. Big problem. Any
suggestions (please)?

Hi Darcy,

How about this one:

require ‘java’

begin
a = ‘’
1000000.times {a << String.new(‘a’*10000)}
rescue java.lang.OutOfMemoryError => e
puts “Caught NoMemoryError in Ruby”
end

This should work, but please do note that handing OOM conditions is
tricky, and your actions
inside rescue block might not fully succeed as well, since JVM is
already in serious error condition.

And once you cleaned up your state, it makes sense to propagate the
error up in the stack.
Continuing as if nothing happens is very undesirable…

Thanks,
–Vladimir

On Tue, Mar 23, 2010 at 11:19 PM, Schultz, Darcy L.
[email protected] wrote:

Prints “Caught NoMemoryError in Ruby” when executed in Ruby. Nothing

require ‘active_record’
'])
a << String.new(‘a’*10000)
confidential and or proprietary information and is provided for the use
To unsubscribe from this list, please visit:


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Yes, this sounds like a bug in either ActiveRecord or in AR-JDBC. That
simple example that doesn’t receive a NoMemoryError misses an
important point…it doesn’t handle a bunch of other errors that could
happen either. Any code which wants to handle all possible exceptions
should rescue Exception as a fallback case, which should catch Java
exceptions as well (confirm please…bug if it doesn’t). If there’s
code in AR that only handles known Ruby exceptions it is in error.

I’m sure there’s things we can do to make Java exceptions and Ruby
exceptions flow more naturally, but it is much harder to convert all
Java exceptions (in this case, Errors really) to their Ruby
equivalents without wrapping every Ruby call in the system with a
catch (Throwable)…which would be a real mess.

  • Charlie (mobile)

On Mar 24, 2010, at 4:23 AM, Vladimir S. [email protected]
wrote:

puts “Caught NoMemoryError in Ruby”

1000000.times {a << String.new(‘a’*10000)}

is rolled back. But if the OutOfMemoryError exception occurs the

end
end
confidential and or proprietary information and is provided for the


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Hi Charlie, Darcy,

On Wed, Mar 24, 2010 at 10:13 AM, Charles Oliver N.
[email protected] wrote:

Any code which wants to handle all possible exceptions should rescue
Exception as a fallback case, which should catch Java exceptions as well
(confirm please…bug if it doesn’t).

Things are a bit more complicated, it seems. Take a look at:

It shows that all the possible exceptions out of java code, like
NPE, OOME, StackOverflow, etc, are all handled correctly and are
rescuable with rescue Exceptions. BUT, java exceptions produced when
ruby code is executed are not rescuable with rescue Exception.

This second case is what Darcy is seeing: inside the transaction, some
ruby code causes OOME and that exception is slipped through rescue
Exception.

If there’s code in AR that only handles known Ruby exceptions it is in error.

Looks like even if AR is rescuing with rescue Exception, it won’t
rescue those OOME and StackOverflow errors produced from within ruby
code.

Thanks,
–Vladimir

Hi Darcy,
end
Thanks,

rescue Exception => e
Sent: Tuesday, March 23, 2010 3:04 PM

def connect
trader.save
demo.connect

To unsubscribe from this list, please visit:


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

That’s exactly correct. AR rescues Exception but OOME is one (of
perhaps many) that does not fall under that umbrella (at least in this
circumstance).

Thanks to the both of you for the quick replies.

Yup, you guys got me on this one. So what’s the fix?

You can rescue Java exceptions like you would Ruby exceptions:

~/projects/jruby âž” jruby -rjava -e “begin; raise
java.lang.NullPointerException.new; rescue
java.lang.NullPointerException; puts ‘rescued’; end”
rescued

And you can rescue exceptions that come out of Java code, which get
wrapped in a NativeException Ruby object:

~/projects/jruby âž” jruby -rjava -e “begin; raise
java.lang.System.get_property(nil); rescue Exception => e; puts
'rescued: ’ + e.class.to_s; end”
rescued: NativeException

So there’s some duality here; we don’t wrap all Java exception, and
have wanted to move away from wrapping any Java exceptions if
possible while unifying exception handling. So the problem is that
non-wrapped Java exceptions don’t extend Ruby’s Exception, and so
rescue Exception won’t pick them up without some special handling:

~/projects/jruby âž” jruby -rjava -e “p
java.lang.NullPointerException.ancestors”[Java::JavaLang::NullPointerException,
Java::JavaLang::RuntimeException, Java::JavaLang::Exception,
Java::JavaLang::Throwable, Java::JavaIo::Serializable,
Java::JavaLang::Object, ConcreteJavaProxy, JavaProxy,
JavaProxyMethods, Object, Kernel]

So I see a few options:

  1. Make rescue Exception rescue Java Throwable as well. This will be
    tricky, because we don’t want to intercept exceptions we use for
    flow-control, but we already special-case them anyway. It’s also a
    little problematic to me because you’ll rescue Exception but get
    something that’s not an Exception.
  2. Try some type trickery and make all Java exceptions extend Ruby
    Exception in some way. I’m not sure how this would work, and it would
    certainly change how they appear to Ruby code.
  3. Require that people attempting to rescue everything rescue Object,
    which would pick up Java exceptions. This seems like the least
    desirable, since only in JRuby would you have to do that.

What do you think? I’ve filed
http://jira.codehaus.org/browse/JRUBY-4677 as a bug that needs to be
fixed for 1.5.

On Wed, Mar 24, 2010 at 10:08 AM, Schultz, Darcy L.
[email protected] wrote:

Hi Charlie, Darcy,
It shows that all the possible exceptions out of java code, like
Looks like even if AR is rescuing with rescue Exception, it won’t

would be a real mess.

already in serious error condition.

when executed in Jruby. Â In our earlier case the consequence is that the
If I raise a regular exception after the save the enclosing transaction
require ‘yaml’
 end
  end
of the intended recipient only. Â Any review, retransmission or


To unsubscribe from this list, please visit:

  http://xircles.codehaus.org/manage_email


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email