Graphql-ruby: Duplicate type definition found for name

Created on 2 Sep 2017  路  10Comments  路  Source: rmosolgo/graphql-ruby

I define fields on a type dynamically from an ActiveRecord attribute. I wrote a helper method for that and it basically loops over the attributes and calls field(name, type) inside the define block.

MyType = GraphQL::ObjectType.define do 
   Company.graphql_types do |name, type|
      field name, type
   end
end

The problem here is that anytime I change the file that defines these types, GraphQL complains. Apparently, the GraphQL::Schema::Traversal.visit method is doing an object identity check and it rejects types defined by a reloaded type file. I'm not sure what the best way forward would be to avoid this error. Do you have any suggestions?

Most helpful comment

In case anyone else faced this problem, what you need to do is to create a new initializer file with the following content in it:

if Rails.env.development?
  lib_ruby_files = Dir.glob(File.join('app/**', '*.rb'))
  lib_reloader ||= ActiveSupport::FileUpdateChecker.new(lib_ruby_files) do
    lib_ruby_files.each do |lib_file|
      silence_warnings { require_dependency(lib_file) }
    end
  end

  ActionDispatch::Callbacks.to_prepare do
    lib_reloader.execute_if_updated
  end

end

All 10 comments

Sorry, this is a bad side-effect of the current DSL 馃槚 It doesn't play nice with Rails autoloading, since it re-loads the files but code still retains references to old objects.

One thing that might work is to use Procs instead of bare references to the type objects, eg:

# Instead of 
{ company: CompanyType } 
# Try: 
{ company: -> { CompanyType } }

This way, the Proc will be re-evaluated each time and it might find the _new_ version of the type.

Now that I'm reading again, I'm not sure I've understood the problem correctly. Maybe you could use reload_dependency to reload the schema when one of those files changes?

I tried wrapping the types in procs, but it didn't solve my problem. The error is coming from this code block:

elsif !prev_type.equal?(type_defn)
  # If the previous entry in the map isn't the same object we just found, raise.
  raise("Duplicate type definition found for name '#{type_defn.name}'")
end

Link

I think reloading the type definitions is creating a new object, and that seems to be unacceptable to visit method. To be honest, I don't understand why the code needs to check for the object identity here. I'm sure there is a reason, but when I comment out that exception, it still works fine. If it's absolutely necessary to check the object identity, can we at least throw a named exception that can be caught from the schema controller in development mode, so we don't need to monkey patch this code block?

Reading your post again, I might reload schema, too, but where is reload dependency defined? Is that a Rails method?

That solved it. Thank you!

In case anyone else faced this problem, what you need to do is to create a new initializer file with the following content in it:

if Rails.env.development?
  lib_ruby_files = Dir.glob(File.join('app/**', '*.rb'))
  lib_reloader ||= ActiveSupport::FileUpdateChecker.new(lib_ruby_files) do
    lib_ruby_files.each do |lib_file|
      silence_warnings { require_dependency(lib_file) }
    end
  end

  ActionDispatch::Callbacks.to_prepare do
    lib_reloader.execute_if_updated
  end

end

@rmosolgo Hi I'm experiencing this when using a custom code reloading outside of Rails... Tried wrapping type references in lambdas to no avail.

Is there a graphql-ruby way to simply teardown my entire schema, member definitions, and everything else, and start over?

Been searching docs and source all day and can't find anything. Thank you!

@rmosolgo We've been seeing this error on our production logs recently. I don't quite understand what's triggering it. I understand that it's an object identity issue, but what are the circumstances under which we should expect to see reloaded objects, and why does graphql-ruby hold on to outdated code? Is it possible for graphql-ruby to re-attempt a traversal with fresh code?

I think this happens when Rails apps boot on multiple threads. Different threads load constants at slightly different times, but they all exist in the same Ruby process, so they have a conflict in the GraphQL schema.

For GraphQL-Ruby 1.9 or below, see a work-around here: https://github.com/rmosolgo/graphql-ruby/issues/1505#issuecomment-538442074

For GraphQL-Ruby 1.10+, there's an improved schema loading system that can be opted into as described here: https://github.com/rmosolgo/graphql-ruby/issues/1505#issuecomment-585299090

@rmosolgo thanks for the quick response! And sorry for bothering you with a problem you've already addressed in another issue. Hopefully search engines will give that thread more prominence in the future.

Was this page helpful?
0 / 5 - 0 ratings