3

I have a problem where the dynamic finders used in my Ruby on Rails application almost always work, but very rarely a NoMethodError will be thrown.

Here is a copy of the exception stack from when things do go wrong:

2013-10-16 05:34:04.723 [Rails/26343   ] :
NoMethodError (undefined method `find_by_id' for #<Class:0xa972ef8>):
  app/models/sas_ui/supergroup_instance.rb:394:in `event_by_id'
  app/models/trace.rb:73:in `event_by_id'
  app/controllers/tview_controller.rb:102:in `select_event'
  lib/tview_logging_control.rb:22:in `call'

The code which causes the exception to be thrown (supergroup_instance line 394):

event = SasUi::Event.find_by_id(event_db_id)

SasUi::Event is a subclass of SasUi, which is a subclass of ActiveRecord::Base.

Here is the database entry:

                                         Table "public.events"

   Column   |            Type             |                      Modifiers
------------+-----------------------------+---------------------------------------
 id         | integer                     | not null default nextval('events_id_seq'::regclass)
 sg_inst_id | integer                     | not null
 sas_id     | integer                     | not null
 system_id  | integer                     |
 trail_id   | bigint                      |
 event_id   | integer                     |
 time       | timestamp without time zone |
 sqn        | bigint                      |
 instance   | integer                     |
 data       | bytea                       |
Indexes:
    "events_pkey" PRIMARY KEY, btree (id)
    "index_events_on_sg_inst_id" btree (sg_inst_id)
Foreign-key constraints:
    "events_sg_inst_id_fk" FOREIGN KEY (sg_inst_id) REFERENCES supergroup_instances(id) ON DELETE CASCADE

This works 99.99% of the time, we have a few hundred customers using this software, but very occasionally, one customer sees this problem. Logging out of the application and back in seems to resolve the problem.

There are multiple threads in the application accessing these models, which is another possible avenue to explore.

As I understand it, dynamic finders work by overriding the method missing method, and splitting the name of the called method on the _ character to extract the requested attribute names. If the extracted attributes exist in attribute_names in the model, then the query will be made, otherwise the original NoMethodError will be thrown.

attribute_names has the following documentation:

Returns an array of column names as strings if it’s not an abstract class and table exists. Otherwise it returns an empty array.

There are no suggestive entries in the postgres logs or application logs, except for the exception listed above.

My initial thoughts are that there may be some intermittent database problem, where for some reason postgres reports that the table doesn't exist. Restarting the app does not restart the database, but does resolve the problem.

I wondered if it could alternatively be down to rails caching tables, and that cache somehow being corrupted.

Has anyone seen this before or know what could cause this to happen? Alternatively, any suggestions on any further diagnosis steps would be appreciated.

I know these finders are deprecated in more recent releases, but moving on to a new version is not an option for this release of software, and I'd like to get to the bottom of this, in case it indicates some deeper problems.

Here are the software versions / platform:

 - Rails: 3.0.20
 - Ruby: 1.9.2p180
 - Postgres: 9.0.3
 - SunOS <blanked-out> 5.10 Generic_137138-09 i86pc i386 i86pc

Adding a bit more information, in case the cause is related to autoload:

SasUi::Event - class declaration: class SasUi::Event < SasUi
SasUi::Event - class location on disk: ../app/models/sas_ui/event.rb

There are two other events classes:
Data::Event - class declaration: class Data::Event < ActiveRecord::Base
Data::Event - class location on disk: ../app/models/data/event.rb

Event - class declaration: class Event < DelegateClass(SasUi::Event)
Event - class location on disk: ../app/models/event.rb
Cœur
  • 37,241
  • 25
  • 195
  • 267
Gary
  • 61
  • 2
  • Could you add the full execution stack? – Stefan Oct 23 '13 at 08:01
  • Have done, sorry, should have done that from the start! – Gary Oct 23 '13 at 08:06
  • 2
    ActiveRecord should appear in the stack trace, something like `from .../active_record/dynamic_matchers.rb:22:in 'method_missing'`. – Stefan Oct 23 '13 at 08:08
  • It doesn't appear at all, which is very strange. Is it possible that rails could somehow have called on the wrong class? Seems unlikely, but I'm stumped here. – Gary Oct 23 '13 at 08:13
  • 1
    Seems like you are defining `SasUi::Event` somewhere so Rails is not autoloading your class. – Stefan Oct 23 '13 at 08:14
  • Thanks Stefan, it's appreciated.I'm going to go and research autoloading now, in case that is the cause. I'll get back to this question with any findings, and let you know. – Gary Oct 23 '13 at 08:16
  • 7
    are you sure that this is your class declaration? `SasUi::Event < SasUi` so `SasUi` is used as a namespace and as your superclass? i don't think that this is a good idea... – phoet Oct 23 '13 at 09:08
  • I am 100% sure this is right. I copied and pasted that from the source code. (I didn't write this code, just supporting it. The original writer has since left us.) - I wonder if I can catch the exception and log the class path, which may tell me if the wrong class has been loaded here. – Gary Oct 23 '13 at 09:15
  • thanks phoet and Stefan for your help. I think this could well be auto-loading, which would explain the intermittent nature of the problem. This other question - http://stackoverflow.com/questions/17049144/rails-create-using-wrong-class - has some suggestions. I am going to add the require explicitly to the top of the class using this model. Unfortunately, I won't know for a while if this has worked, due to the nature of the problem. – Gary Oct 23 '13 at 10:00
  • I patched the customers system, putting an explicit require at the top of the ruby file which the errors were being reported in, and things are looking good so far, with no reported reoccurrences yet. I am fairly confident that this is the solution to my problem. The proper fix should probably be to rename the classes to remove the clashes, using modules instead of namespaces, and to also not have the namespace matching the parent class name. – Gary Nov 04 '13 at 07:58
  • 3
    Be mindful of things that conflict with Rails load patterns, for example using class-name literals in model load-time code: class SasUi < ActiveRecord::Base CHILD_CLASSES = [ SasUi::Event ] end sometimes yields unexpected and erratic results since SasUi::Event may or may not be available or fully loaded when SasUi is eval'd, while this should work: class SasUi < ActiveRecord::Base CHILD_CLASSES = [ 'SasUi::Event' ] def self.child_classes CHILD_CLASSES.map(&:constantize) end end since it defers evaluation of SasUi::Event to after load-time – bbozo Nov 18 '13 at 09:28
  • @Gary, you should write this up as a solution and accept it so others can benefit if they have similar issues. Glad you sorted out your problem. – Mike H-R Nov 20 '13 at 02:57

1 Answers1

0

Use modules as namespaces, not classes.

What you have done (class under class) is called inner class and should not be reached outside upper class.

Nested classes question.

Community
  • 1
  • 1
organium
  • 59
  • 10