[Nitro] OG RFC: Catching an early/fatal exception.

Mark Van De Vyver mvyver at gmail.com
Thu Sep 20 07:56:34 EDT 2007


> Scenario:
> You're writing a spec, an Og object is defined at the same 'level' as
> the describe's.  'Og.start' is run.
> The 'query' call in the first call to sql.rb:create_field_map,
> generates an exception.
> NOTE: We are within the 'unless map =
> klass.instance_variable_get("@field_map")' branch
> sql.rb:handle_sql_exception() is called and a new StoreException is raised.

This issue wasn't entirely new:
http://rubyforge.org/pipermail/nitro-general/2006-May/thread.html#4274

The difference in what I've done is to handle the StoreException along
the way and so I throw an exception at the end of Og.start - currently
things are just logged.  This may be a point of contention?
I'll get the patch together tomorrow and post to the list.

I've worked on the assumption that StoreException is the lowest level
OG store related exception we'll see, and if you do see it then not
much has worked - so Og will shut down as described in the first post
(disconnect and unmanage any classes).
Adpater, and driver code _should_ override the functions that generate
StoreException - if they don't there is some informative feedback.

I intend the DBI adapter to raise some dbi annotated exceptions - the
DBI gem does this already so will distinguish mine from those -
anyway, getting ahead of my self.

Due to the netbeans bug I posted about my specs are hosed from rake's
point of view. So I'd appreciate someone applying the patch and seeing
what it does to current specs and their current code.

I've got things to the point that when you run the following (with a
broken adapter):

## Start
begin
  require "rubygems"
rescue LoadError => ex
  # drink it!
end

require "og"

$DBG = true

     class Person
        attr_accessor :name, String
        attr_accessor :age, Integer
        attr_accessor :occupation, String
        attr_accessor :type, Integer
        attr_accessor :notes, String
      end

Og.start

Og.start

## End

You see (note the consecutive Og.start):

 INFO: Og uses the Dbi store.
DEBUG: Og manageable classes: [Person]
DEBUG: SELECT * FROM `ogperson` LIMIT 1
ERROR: DB error undefined method `query' for
#<DBI::DatabaseHandle:0xb766c9f4>, [SELECT * FROM `ogperson` LIMIT 1]
ERROR: /usr/src/nitro-repo/og/lib/og/store/sql.rb:642:in `query_statement'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:627:in `query'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:1126:in `create_field_map'
/usr/src/nitro-repo/og/lib/og/store/sql/evolution.rb:74:in `evolve_schema'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:90:in `og_create_schema'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:323:in `enchant'
/usr/src/nitro-repo/og/lib/og/manager.rb:228:in `manage'
/usr/src/nitro-repo/og/lib/og/manager.rb:154:in `with_store'
/usr/src/nitro-repo/og/lib/og/manager.rb:227:in `manage'
/usr/src/nitro-repo/og/lib/og/manager.rb:343:in `manage_classes'
/usr/src/nitro-repo/og/lib/og/manager.rb:343:in `each'
/usr/src/nitro-repo/og/lib/og/manager.rb:343:in `manage_classes'
/usr/src/nitro-repo/og/lib/og.rb:209:in `start'
/usr/src/nitro-repo/og/lib/exception_example_1.rb:19
/usr/lib/ruby/gems/1.8/gems/ruby-debug-ide-0.1.8/lib/ruby-debug.rb:79:in
`debug_load'
/usr/lib/ruby/gems/1.8/gems/ruby-debug-ide-0.1.8/lib/ruby-debug.rb:79:in `main'
/usr/lib/ruby/gems/1.8/gems/ruby-debug-ide-0.1.8/bin/rdebug-ide:74
/usr/bin/rdebug-ide:16:in `load'
/usr/bin/rdebug-ide:16
 INFO: Og has encountered a low level SQL related exception - inspect
the info log for additionaldetails.
 INFO: To obtain additional details consider the following:
  - set Og.raise_store_exceptions = true.
  - activate existing logging ($DBG=true).
  - increase the log intensity: Logger.get.level = Logger::DEBUG.
  - inspect the error log.
ERROR: Og failed to query the SQL store.  A schema field map has not
been created for Person, rebuild = true.
ERROR: Og failed to complete a schema evolution.  Table fields have
not been added or removed for Person.
ERROR: Og failed to create the schema.  The table, indices and joins
have not been created or evolved for Person, using Og::DbiAdapter.
ERROR: Og failed to complete enchanting the class Person, managed by memory.
ERROR: Og failed to return a store to the thread pool for Og::DbiAdapter.
ERROR: Og failed to manage this class.  Og related funtionality has
not been injected into the class Person.
ERROR: Og failed to manage at least one of a collection of classes.
ERROR: Og failed to start cleanly. Classes will now be unmanaged and
the store closed.
ERROR: Og::StoreException in Og.setup:
ERROR: Og::StoreException
ERROR: /usr/src/nitro-repo/og/lib/og/store/sql.rb:685:in `handle_sql_exception'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:630:in `query'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:1126:in `create_field_map'
/usr/src/nitro-repo/og/lib/og/store/sql/evolution.rb:74:in `evolve_schema'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:90:in `og_create_schema'
/usr/src/nitro-repo/og/lib/og/store/sql.rb:323:in `enchant'
/usr/src/nitro-repo/og/lib/og/manager.rb:228:in `manage'
/usr/src/nitro-repo/og/lib/og/manager.rb:154:in `with_store'
/usr/src/nitro-repo/og/lib/og/manager.rb:227:in `manage'
/usr/src/nitro-repo/og/lib/og/manager.rb:343:in `manage_classes'
/usr/src/nitro-repo/og/lib/og/manager.rb:343:in `each'
/usr/src/nitro-repo/og/lib/og/manager.rb:343:in `manage_classes'
/usr/src/nitro-repo/og/lib/og.rb:209:in `start'
/usr/src/nitro-repo/og/lib/exception_example_1.rb:19
/usr/lib/ruby/gems/1.8/gems/ruby-debug-ide-0.1.8/lib/ruby-debug.rb:79:in
`debug_load'
/usr/lib/ruby/gems/1.8/gems/ruby-debug-ide-0.1.8/lib/ruby-debug.rb:79:in `main'
/usr/lib/ruby/gems/1.8/gems/ruby-debug-ide-0.1.8/bin/rdebug-ide:74
/usr/bin/rdebug-ide:16:in `load'
/usr/bin/rdebug-ide:16
[0



and just to check that all is still OK, using:

Og.start({:adapter => :mysql, :user => "root", :password => "", :name
=> "test"})

 INFO: Og uses the Mysql store.
DEBUG: Og manageable classes: [Person]
DEBUG: CREATE TABLE `ogperson` (`name` text, `age` integer,
`occupation` text, `type` integer, `notes` text, `oid` integer
AUTO_INCREMENT PRIMARY KEY)
DEBUG: SELECT * FROM `ogperson` LIMIT 1


Cheers
Mark


> Problem:
> That StoreException is never caught.
> The Og.start returns silently.
> There is still a connection open to the DB backend, the DB backend is untouched.
> manager.rb:put_store is entered, @pool is undefined, and it is from
> _here_ that we return to og.rb:start where the exception is _logged_,
> but not raised.
>
> So we've seen a fatal error, the spec never receives an exception -
> just the Og.start returning 'nil', with a DB connection open and
> classes still 'managed'.
>
> Your hint that something is not right might be a later exception:
> Error code:
> Error message: database is locked(database is locked)
>
> And off you go on that tangent :)
> Anyway, I digress.....
>
> Questions:
> 1) Where should these exceptions be handled?
> There are three places this exception could be handled:
>  a) sql.rb:handle_sql_exception
>  b) sql.rb:create_field_map
>  c) manager.rb:put_store
>  d) both a) and b)
>
> Votes? Instructions?
>
> 2) What should be done with the DB connection and the manged classes?
> From my understanding, because
> 'klass.instance_variable_get("@field_map")' returned nil/false in
> sql.rb:create_field_map we should really shutdown the DB connection,
> unmanage the classes, and scream blue-murder.  The unmanage classes
> step is because I think Og's manger doesn't like being given the same
> class twice - but this might be an impression left from the OGTABLE
> warning just patched?  Anyway, safety first, and I'd vote for
> something like the following somewhere (where?) in 1 d):
>         if manager.close_store == nil && manager.unmanage_classes == []
>           true
>         else
>           raise <SomeSeriousException>
>         end
>
> Comments _please_
>
> Mark
>


More information about the Nitro-general mailing list