Upgrade to 3.0.2: NameError: undefined method `call' for class `Redis::Client' (with a "fix")

Just tried upgrading from 3.0.1 to 3.0.2, and got this error during rake db:migrate stage:

rake aborted!
NameError: undefined method `call' for class `Redis::Client'
Did you mean?  caller
/var/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/mini_profiler/profiling_methods.rb:83:in `alias_method'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/mini_profiler/profiling_methods.rb:83:in `profile_method'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/mini_profiler/profiling_methods.rb:65:in `counter_method'
/var/discourse/config/initializers/006-mini_profiler.rb:90:in `<main>'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `load'

Very long story short, I found that pegging redis at version 4.8.0 instead of leaving the redis version unspecified in the Gemfile did the trick. I.e., for the Gemfile:

-gem "redis"
+gem "redis", "4.8.0"

I don’t feel very comfortable mucking about with this, but it seems to me that this is an unintended side effect of redis being upgraded elsewhere since my last upgrade (to 3.0.1), and that a reinstall of Discourse 3.0.1 would now show the same problem.

Hope this helps someone, and please let me know if this leaves my system in a vulnerable state :slight_smile:

We set gem versions on the Gemfile.lock file, and it’s already set to that same version

Quite! Strange, since I had never heard about either Gemfile or Gemfile.lock before. But having 20 other fora to repeat the install on (sigh), it turns out that the whole thing started with a different problem during install where the complaints ended with a recommendation to try deleting the Gemfile.lock:

Bundler found conflicting requirements for the Ruby version:
  In Gemfile:
    actionmailer (= was resolved to, which depends on
      Ruby (>= 2.7.0)

    sassc-rails was resolved to 2.1.2, which depends on
      sprockets-rails was resolved to 3.4.2, which depends on
        Ruby (>= 2.5)

    json was resolved to 2.6.3, which depends on
      Ruby (>= 2.3)
    json_schemer was resolved to 0.2.23, which depends on
      ecma-re-validator (~> 0.3) was resolved to 0.4.0, which depends on
        Ruby (>= 2.6, < 4.0)

    rspec was resolved to 3.12.0, which depends on
      rspec-expectations (~> 3.12.0) was resolved to 3.12.2, which depends on
        diff-lcs (>= 1.2.0, < 2.0) was resolved to 1.5.0, which depends on
          Ruby (>= 1.8)

    web-push was resolved to 3.0.0, which depends on
      Ruby (>= 3.0)

  Current Ruby version:
    Ruby (= 2.7.6)

Bundler could not find compatible versions for gem "hkdf":
  In snapshot (Gemfile.lock):
    hkdf (= 1.0.0)

  In Gemfile:
    web-push was resolved to 1.0.0, which depends on
      hkdf (~> 0.2)

Deleting your Gemfile.lock file and running `bundle install` will rebuild your
snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.

So, I got past that initial error after removing the lock file as suggested, then ran into the NameError: undefined method ‘call’ described earlier. Fixed that by pegging the redis version. Then, everything worked.

So, my script was modified to a) remove Gemfile.lock (because of the error quoted above) and b) automatically change the Gemfile to peg redis at 4.8.0. Hunky dory… I thought. This worked on three out of the 20 “identical” machines! The rest gave this new error:

[discourse@in3020-discourse discourse]$ cd $INSTA; RAILS_ENV=production /usr/local/bin/bundle exec rake db:migrate # stuffing a lot of stuff into the database
rake aborted!
NoMethodError: undefined method `logger=' for Sidekiq:Module
Did you mean?  logger
/var/discourse/config/initializers/100-sidekiq.rb:58:in `<main>'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `load'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `block in load_config_initializer'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport- `instrument'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `load_config_initializer'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `block (2 levels) in <class:Engine>'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `each'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `block in <class:Engine>'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `instance_exec'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `run'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `block in run_initializers'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `each'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `tsort_each_child'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `run_initializers'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `initialize!'
/var/discourse/config/environment.rb:7:in `<main>'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/zeitwerk-2.6.7/lib/zeitwerk/kernel.rb:38:in `require'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `require_environment!'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/railties- `block in run_tasks_blocks'
/var/discourse/vendor/bundle/ruby/2.7.0/gems/rake-13.0.6/exe/rake:27:in `<top (required)>'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/cli/exec.rb:58:in `load'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/cli/exec.rb:58:in `kernel_load'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/cli/exec.rb:23:in `run'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/cli.rb:486:in `exec'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/cli.rb:31:in `dispatch'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/cli.rb:25:in `start'
/usr/local/share/gems/gems/bundler-2.3.26/exe/bundle:48:in `block in <top (required)>'
/usr/local/share/gems/gems/bundler-2.3.26/lib/bundler/friendly_errors.rb:120:in `with_friendly_errors'
/usr/local/share/gems/gems/bundler-2.3.26/exe/bundle:36:in `<top (required)>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => db:migrate => db:load_config => environment
(See full trace by running task with --trace)

Which was really, really strange because the /var/discourse/config/initializers/100-sidekiq.rb files were identical on all machines, and certainly did not contain ‘logger=’, but a ‘logger = …’ statement.

I finally figured out that on the machines where it worked, /usr/local/bin/bundle was version 2.3.20, not 2.3.26 as on the machines where it failed. So in sum, adding this before the install command for discourse did the trick for me (it was not enough to do the bundler pegging at 2.3.20 commands after the bundler install of discourse, it had to be before):

rm Gemfile.lock
perl -pi.bak -e 's/gem "redis"/gem "redis","4.8.0"/;' Gemfile

/usr/bin/gem uninstall bundler -v 2.3.26
/usr/bin/gem install bundler -v 2.3.20
RAILS_ENV=production /usr/local/bin/bundle install
RAILS_ENV=production /usr/local/bin/bundle exec rake db:migrate
RAILS_ENV=production /usr/local/bin/bundle exec rake assets:precompile

Why on Earth some of those machines had bundler 2.3.26 and others had 2.3.20 I have no idea… but it’s probably not your fault :laughing:.

And this is why I use the stable version :laughing:

Ruby 2.7 is EOL and not supported by us anymore. You don’t get that version when installing Discourse following the standard install guide for a while, so I assume this is a custom install?

I recommend sticking to the supported official install guide even for people familiar with Discourse stack, and that is even more important for people unfamiliar with the technologies used.

Yes, doing it the official way would almost certainly have given me fewer headaches (most likely none). The problem is that I’m not allowed to use docker images that are not pre-approved by our IT security department - and yours is not.

So this is indeed a custom, native install on RHEL8 machines, which also needed some SELinux trickery to work.

Maybe there are others out there with the same problem - perhaps it would be useful to them if I post a topic with my installation procedure? Feel free to protest - I can see that this could be taken as an “encouragement” to install it this way even if not strictly necessary, which could lead to more questions for you.

As an aside, when googling for solutions to any SELinux problem, the most frequent advice you will find is “this is how you disable it”:laughing:. But this is not an option here.

Thanks for your help!

Thanks for the context. Just be aware that we do not offer free support for custom installs here.

This install is already using a version of Ruby that doesn’t work with any of the current versions of Discourse, does not respect any of the carefully managed gem versions, and doesn’t use all the manually managed OS level shared libraries we ship. Therefore, breakage is not a question of if but more of a question of when.

1 Like

You might suggest to your security team that an image that is vetted and used by the developers is much, much safer than your attempting to apply security patches to things that you can’t possibly keep track of unless it’s your only job.

. . . . But, that’s likely not helpful.