Почему я периодически получаю сообщение "OpenSSL :: SSL :: SSLError: SSL_read: sslv3 alert bad record mac" от Heroku Redis? - PullRequest
0 голосов
/ 08 мая 2018

После перехода с Redis To Go на Heroku Redis код Redis в нашем приложении Ruby on Rails несколько раз в день выдает ошибку «OpenSSL :: SSL :: SSLError: SSL_read: sslv3 alert bad record mac».

Есть идеи почему?

Ответы [ 2 ]

0 голосов
/ 17 июля 2018

Я работал над этим в приложении Ruby on Rails, повторяя эти ошибки. До сих пор, кажется, избавился от ошибок. Раньше мы получали около 5 в день, а в последний день - с момента введения этого обходного пути - не было ни одного.

В config/initializers/redis_heroku_error_handling.rb:

# Hack to retry errors on Heroku Redis: /10621146/pochemu-ya-periodicheski-poluchay-soobschenie-openssl-ssl-sslerror-sslread-sslv3-alert-bad-record-mac-heroku-redis

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
0 голосов
/ 08 мая 2018

Это не решает проблему напрямую, но вот что сказала поддержка Heroku:

Эту проблему было трудно диагностировать из-за ее редкости и непоследовательности. У нас есть много сообщений о том, что это происходит в разных направлениях, языках приложений, конфигурациях Heroku Dyno и многих других деталях. Эта проблема была написана и диагностируется инженерами, но опять же из-за подробностей мы практически не можем это сделать.

Кроме того, у нас нет инфраструктуры для управления исходящими соединениями. У нас есть информация об использовании сети в необработанном виде (количество переданных байтов, количество пакетов и т. Д.), Но в отличие от входящих соединений, в которых маршрутизатор Heroku обрабатывает запросы, исходящие соединения являются «стандартными», как это предусмотрено нашим провайдером инфраструктуры. Не существует специфической инфраструктуры Heroku для работы с исходящими соединениями. Единственный интересный момент - это виртуальный интерфейс, который использует Dynos, и, соответственно, конфигурация сети хоста Dyno, но, опять же, в этом нет ничего особенного. Он использует платформу инфраструктуры, предоставленную конфигурацию сети, необходимую для связи хоста.

Пока что ни я, ни инженеры не дали краткого ответа на эти проблемы, учитывая их несоответствие, и наша текущая рекомендация заключается в том, что эти проблемы лучше решать с помощью обработки ошибок соединения, регистрации по мере необходимости и повторных попыток.

Если у вас есть сведения о том, как эта ошибка воспроизводится последовательно, она значительно нам поможет.

...