petergoldstein/dalli

Dalli 3.0.5 causes "TypeError: no implicit conversion of true into String" from Rails `cache` calls

ryanfb opened this issue · 13 comments

Running Rails 6.1.4.1 under Ruby 3.0.3, if I upgrade from Dalli 3.0.4 to 3.0.5 I start encountering TypeError: no implicit conversion of true into String in cache calls from ERB views.

Sample backtrace:

[GEM_ROOT]/gems/activesupport-6.1.4.1/lib/active_support/core_ext/string/output_safety.rb:169 :in `concat`
[GEM_ROOT]/gems/activesupport-6.1.4.1/lib/active_support/core_ext/string/output_safety.rb:169 :in `safe_concat`
[GEM_ROOT]/gems/actionview-6.1.4.1/lib/action_view/helpers/text_helper.rb:59 :in `safe_concat`
[GEM_ROOT]/gems/actionview-6.1.4.1/lib/action_view/helpers/cache_helper.rb:169 :in `cache`
[PROJECT_ROOT]/app/views/layouts/_navigation_links.html.erb:22 :in `_app_views_layouts__navigation_links_html_erb`
[GEM_ROOT]/gems/actionview-6.1.4.1/lib/action_view/base.rb:247 :in `public_send` 

Here, [PROJECT_ROOT]/app/views/layouts/_navigation_links.html.erb:22 is <% cache do %>. I also encounter it when I pass cache an array, e.g. <% cache [@playlist, @limit] do %>.

If I revert Dalli from 3.0.5 to 3.0.4, the problem goes away.

Feel free to let me know if you think this should be filed against Rails upstream instead.

@ryanfb Thanks for the report. Would you mind providing your cache configuration as well? That would help me track down the issue.

Thanks.

My production Rails cache config is:

  if ENV['MEMCACHEDCLOUD_SERVERS']
    config.cache_store = :mem_cache_store, ENV['MEMCACHEDCLOUD_SERVERS'].split(','), {
      username: ENV['MEMCACHEDCLOUD_USERNAME'], password: ENV['MEMCACHEDCLOUD_PASSWORD']
    }
  end

The Memcached Cloud server it's connecting to shows "Protocol: Memcached, Redis version: 1.4.17" (SASL authentication on).

@ryanfb Thanks.

Do you have a local environment which isn't SASL that you could confirm or refute that the same behavior is happening? As I noted here - #842 - I'm concerned about supporting SASL behavior in general, and it's certainly possible that the refactoring in 3.0.5 broke something there. If we could determine whether this is SASL-related or not, that would help narrow it down appreciably.

And on the same app, connecting to your cloud SASL memcached, can you use a simple Dalli::Client from the console without error? That is, does something like:

dc = Dalli::Client.new( [[ENV['MEMCACHEDCLOUD_SERVERS']], {
      username: ENV['MEMCACHEDCLOUD_USERNAME'], password: ENV['MEMCACHEDCLOUD_PASSWORD']
    }
dc.set('a', 'av')
val = dc.get('a')
puts val

work for you?

Let me try and see if I can reproduce locally with/without SASL. I have everything Dockerized for local development/testing, though that doesn't include the production caching setup, but it may be possible to add.

I may also be able to check against the live Memcached Cloud servers by running Dall::Client 3.0.4 vs. 3.0.5 in my local console with the commands above connecting to the production cloud URL—I'm hesitant to do it from the production console because (due to the way Heroku works) I believe that would likely require re-deploying 3.0.5 and breaking the site.

Was able to check Dalli::Client 3.0.4 with the commands above connecting to the remote cloud server and setting/getting the correct value.

If I upgrade to 3.0.5 and try the same, dc.set('a', 'av') raises Dalli::RingError: No server available from /Users/ryan/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/dalli-3.0.5/lib/dalli/ring.rb:46:in 'server_for_key'.

@ryanfb Thanks so much. As I read it, that strongly suggests a SASL error. I'm able to run that script against a non-SASL connection on 3.0.4 and 3.0.5 and it works fine with either. And the fact that you're getting a no server available means that the connect is probably failing.

Let me dig into the changes there and see if I can track it down.

@ryanfb Any chance you could do one additional piece of spelunking? I'd like to see the log output in this case. Could you do the following in the console:

logger = Dalli.logger
logger.level = Logger::DEBUG 
dc = Dalli::Client.new( [[ENV['MEMCACHEDCLOUD_SERVERS']], {
      username: ENV['MEMCACHEDCLOUD_USERNAME'], password: ENV['MEMCACHEDCLOUD_PASSWORD']
    }
dc.set('a', 'av')
val = dc.get('a')
puts val

, capture the log output, and attach it to this ticket? I'd like to see the logging inside the SASL handshake.

@ryanfb I've just released Dalli 3.0.6 with what I believe is a fix for this issue. My limited testing indicates that this is working with Memcachier's SASL. And the fix makes sense.

Would you be able to confirm with 3.0.6? Thanks.

I was also encountering a new Dalli::RingError: No server available error. It was consistently reproducible for me with 3.0.5.

I just installed 3.0.6 and it resolved the issue.

(sorry, would have commented earlier--I just figured out the cause was a stealth dalli upgrade done by my CI system and not an outage on my hosted memcached server)

Thanks for the verification @apiology . Sorry for the issue.

And @apiology - if you have insight on #842 - that would be appreciated. Getting SASL working in CI is probably my next significant focus.

Closing this issue