artemis icon indicating copy to clipboard operation
artemis copied to clipboard

Initializer `graphql.client.preload` fails to load in Rails 7.0

Open arthurchui opened this issue 10 months ago • 4 comments

After upgrading Raiils 6.1 to 7.0, the app fails to boot because the initializer graphql.client.preload cannot load the registered service.

/app/app/lib/my_graphql.rb:1:in `<main>': uninitialized constant GraphqlClient (NameError)

class MyGraphql < GraphqlClient
^^^^^^^^^^^^^
Did you mean?  GraphQL
from <internal:/app/vendor/ruby-3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
from <internal:/app/vendor/ruby-3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
from /app/vendor/bundle/ruby/3.2.0/gems/zeitwerk-2.6.11/lib/zeitwerk/kernel.rb:38:in `require'
from /app/vendor/bundle/ruby/3.2.0/gems/bootsnap-1.15.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
from /app/vendor/bundle/ruby/3.2.0/gems/artemis-0.8.0/lib/artemis/railtie.rb:64:in `block (2 levels) in <class:Railtie>'
from /app/vendor/bundle/ruby/3.2.0/gems/artemis-0.8.0/lib/artemis/railtie.rb:62:in `each'
from /app/vendor/bundle/ruby/3.2.0/gems/artemis-0.8.0/lib/artemis/railtie.rb:62:in `block in <class:Railtie>'
from /app/vendor/bundle/ruby/3.2.0/gems/railties-7.0.8.1/lib/rails/initializable.rb:32:in `instance_exec'

Note that we expect the code to be autoloaded from app/lib/*:

# app/lib/my_graphql.rb
class MyGraphql < GraphqlClient
 ...
end

# app/lib/graphql_client.rb
class GraphqlClient < Artemis::Client # base class
  ...
end

I do not understand the purpose of the initializer graphql.client.preload. Perhaps it can be omitted as the autoloader can load the registered services automatically.

arthurchui avatar Apr 08 '24 21:04 arthurchui

There seems to be a naming convention difference between GraphqlClient and app/lib/graph_client.rb (notice the ql in the class name). the default file loader may have changed from the classic loader to Zeitwerk as part of the Rails upgrade and that may be why you started seeing this after a Rails upgrade. Would you mind double-checking the spelling and naming conventions?

The preloader is required in production because class loading in Rails is not thread-safe. Not loading all the constants during the initialization phase may result in degraded performance due to the loader lock and/or racing condition when 2 or more threads attempt to load the same class at the same time.

yuki24 avatar Apr 09 '24 01:04 yuki24

@arthurchui Do you have any updates on this? I would like to help but let me know if you are okay with closing it if you have solved your problem.

yuki24 avatar Apr 18 '24 04:04 yuki24

Hi @yuki24 , thanks for your response. It is my fault that I made a typo in the description. I have just corrected it.

arthurchui avatar Apr 19 '24 18:04 arthurchui

Apparently, during this phase of initialization, the autoloader isn't activated yet. As a stopgap, I manually require the base class:

# app/lib/my_graphql.rb
require_dependency 'graphql_client'

class MyGraphql < GraphqlClient
 ...
end

Note that require_dependency is an obsolete method for Rails applications running in Zeitwerk mode. Is there a better way to run the preloader? or should we put the graphql classes in /lib as we don't expect to autoload them?

arthurchui avatar Apr 19 '24 18:04 arthurchui

@arthurchui I have tweaked the loader and now it should work both with the classic loader and zeitwerk. The version v1.0.2 includes this fix. Could you give it a try and see if it fixes it?

yuki24 avatar May 02 '24 02:05 yuki24

@yuki24, it works. Thank you!!

arthurchui avatar May 09 '24 20:05 arthurchui