elasticsearch-rails: Invalid single-table inheritance type: SubClass is not a subclass of Class

I encountered some serious headaches while working with Elasticsearch-Model in development.

My problem is that the Invalid single-table inheritance type: X is not a subclass of Y exception was being thrown when I tried to instantiate records with the Multiple adapter after my code had been reloaded for the first time. I would have to restart my Rails server every time I made any changes to my code.

What I found was that Elasticsearch::Model::Registry.models would endlessly accumulate reloaded classes each time my code was being reloaded. If I had five classes that included Elasticsearch::Model, it would increase by 5 each time my code reloaded. I have monkey-patched the Registry class with:

  module Elasticsearch
    module Model
      class Registry
        def add(klass)
+         existing_model = @models.detect { |model| model.name == klass.name }
+         @models.delete(existing_model)
          @models << klass
        end
      end
    end
  end

in an initializer and only for development environment to fix this particular issue to ensure this Registry only contains the newly-reloaded classes.

After this change, I continued to encounter the same error. What I found was that Elasticsearch::Model::Adapter::Multiple::Records was caching the result of __type_for_hit in a class variable @@__types, and after my code was reloaded, the stale classes were being returned instead of the newly reloaded classes. I unset this class variable by hooking into Active Support’s reloader:

Rails.application.reloader.before_class_unload do
  Elasticsearch::Model::Adapter::Multiple::Records.remove_class_variable(:@@__types) }
end

This is in the same environment block that the above monkey-patch is in to avoid affecting production environment. This allowed my records to be instantiated even after my code had been reloaded and fixed my issue.

This issue took a ton of time and effort (and frustration) to debug and fix, it would be nice to see the team provide better support for STI since this is a core feature in Rails. Hopefully this report will help you guys improve on some of these weak spots. I’m happy to provide any other information you might need.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 11
  • Comments: 18 (7 by maintainers)

Most upvoted comments

@PhilCoggins thanks for the extra info, I’ll keep investigating.

I would consider the ActiveRecord::SubclassNotFound a derivative of the core issue.

Somehow, when a klass is first added to the @models array, it initially contains no descendants, but subsequently performing a search the descendant is added.

When code is reloaded, however, the klass is added to the @models array, and a search is performed, the descendants don’t get added.

Using your branch of elasticsearch-model on my test project:

irb(main):001:0> require_dependency 'animal'
=> true
# Open connection
irb(main):002:0> Animal.connection
# Animal is in registry
irb(main):003:0> Elasticsearch::Model::Registry.all
=> [Animal(id: integer, name: text, type: text)]
# The model contains no descendants (ActiveRecord has not yet loaded this class)
irb(main):004:0> Elasticsearch::Model::Registry.all[0].descendants
=> []
irb(main):005:0> Elasticsearch::Model.search("Fluffy").records.to_a
=> [#<Dog id: 4, name: "Fluffy", type: "Dog">]
# Descendants now show up
irb(main):006:0> Elasticsearch::Model::Registry.all[0].descendants
=> [Dog(id: integer, name: text, type: text)]
irb(main):007:0> reload!
Reloading...
=> true
# Code reloaded, registry updated with new class, no descendants
irb(main):008:0> Elasticsearch::Model::Registry.all[0].descendants
=> []
# Blows up
irb(main):009:0> Elasticsearch::Model.search("Fluffy").records.to_a
Traceback (most recent call last):
        1: from (irb):9
ActiveRecord::SubclassNotFound (The single-table inheritance mechanism failed to locate the subclass: 'Dog'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Animal.inheritance_column to use another column for that information.)

I hope this helps.