为什么我得到" OpenSSL :: SSL :: SSLError:SSL_read:sslv3 alert bad record mac"来自Heroku Redis的间歇性?

时间:2018-05-08 07:43:12

标签: heroku redis heroku-redis

从Redis To Go切换到Heroku Redis后,我们的Ruby on Rails应用程序中的Redis代码获得了一个" OpenSSL :: SSL :: SSLError:SSL_read:sslv3 alert bad record mac"每天错误几次。

任何想法为什么?

3 个答案:

答案 0 :(得分:4)

我相信您遇到了多处理问题,其中分叉进程关闭了父进程的 Redis 连接。我刚刚在 resque 中发现了一个导致相同错误的错误,该错误受到此问题的困扰。

https://github.com/resque/resque/pull/1739

答案 1 :(得分:0)

它没有直接解决问题,但这是Heroku支持不得不说的:

  

由于该问题的频繁和不一致,这个问题很难诊断。我们有很多关于它的报告,包括目的地,应用程序语言,Heroku Dyno配置以及许多其他细节。这个问题已被写入并由工程师诊断,但细节再次使我们几乎不可能这样做。

     

此外,我们没有管理传出连接的基础架构。我们确实有原始形式的网络使用信息(传输的字节数,数据包的数量等),但与Heroku路由器处理请求的传入连接不同,出站连接都是标准的"由我们的基础设施提供商提供。没有Heroku特定的基础设施可以处理出站连接。唯一感兴趣的项目是Dynos使用的虚拟接口,以及Dyno主机的网络配置,但同样没有什么特别之处。它使用基础架构平台提供的主机通信所需的网络配置。

     

到目前为止,我和工程师都没有对这些问题提出简明的答案,鉴于它们的不一致性,我们目前的建议是通过连接错误处理,根据需要进行日志记录以及重试来更好地处理这些问题。

     

如果您有关于一致可重现方式的详细信息,则会发生此错误,这对我们有很大帮助。

答案 2 :(得分:0)

我在Ruby on Rails应用程序中通过重试这些错误来解决此问题。到目前为止,似乎已经摆脱了错误。自从引入这种解决方法以来,我们过去每天只有大约5天的时间。

config/initializers/redis_heroku_error_handling.rb中:

# Hack to retry errors on Heroku Redis: https://stackoverflow.com/questions/50228454/why-am-i-getting-opensslsslsslerror-ssl-read-sslv3-alert-bad-record-mac

raise "Ensure this hack still works!" if Redis::VERSION != "3.3.5"

module RedisHerokuErrorHandlingHack
  def call(command)
    attempts_left = 3

    begin
      super
    rescue OpenSSL::SSL::SSLError => e
      raise unless e.message.include?("SSL_read: sslv3 alert bad record mac")

      attempts_left -= 1
      raise unless attempts_left > 0

      retry
    end
  end
end

class Redis::Client
  prepend RedisHerokuErrorHandlingHack
end

spec/initializers/redis_heroku_error_handling_spec.rb中:

require "rails_helper"

describe RedisHerokuErrorHandlingHack do
  it "retries 'bad record mac' errors" do
    exception = OpenSSL::SSL::SSLError.new("SSL_read: sslv3 alert bad record mac")
    fail_then_maybe_succeed(exception: exception, fail_times: 2)

    expect($redis.get("hello")).to eq("success response")
  end

  it "fails if a few retries didn't help" do
    exception = OpenSSL::SSL::SSLError.new("SSL_read: sslv3 alert bad record mac")
    fail_then_maybe_succeed(exception: exception, fail_times: 3)

    expect { $redis.get("hello") }.to raise_error(/bad record mac/)
  end

  it "does not retry other errors" do
    fail_then_maybe_succeed(exception: "Boom", fail_times: 2)

    expect { $redis.get("hello") }.to raise_error("Boom")
  end

  private

  def fail_then_maybe_succeed(exception:, fail_times:)
    attempt_count = 0

    allow_any_instance_of(Redis::Client).to receive(:process) do |*args|
      attempt_count += 1

      if attempt_count <= fail_times
        raise exception
      else
        "success response"
      end
    end
  end
end