Почему я периодически получаю сообщение "OpenSSL::SSL::SSLError: SSL_read: sslv3 alert bad record mac" от Heroku Redis?
После перехода с Redis To Go на Heroku Redis код Redis в нашем приложении Ruby on Rails несколько раз в день выдает ошибку "OpenSSL::SSL::SSLError: SSL_read: sslv3 alert bad record mac".
Есть идеи почему?
3 ответа
Я полагаю, что вы столкнулись с проблемой многопроцессорности, когда разветвленный процесс закрывает соединение Redis родительского процесса. Я только что нашел ошибку, которая приводит к той же ошибке в
resque
которые пострадали от этой проблемы.
Это не решает проблему напрямую, но вот что сказала поддержка Heroku:
Эту проблему было трудно диагностировать из-за ее редкости и непоследовательности. У нас есть много сообщений о том, что это происходит в разных направлениях, языках приложений, конфигурациях Heroku Dyno и многих других деталях. Эта проблема была написана и диагностируется инженерами, но, опять же, детали сделали для нас почти невозможным сделать это.
Кроме того, у нас нет инфраструктуры для управления исходящими соединениями. У нас есть информация об использовании сети в необработанном виде (количество переданных байтов, количество пакетов и т. Д.), Но в отличие от входящих соединений, в которых маршрутизатор Heroku обрабатывает запросы, исходящие соединения являются "стандартными", как это предусмотрено нашим провайдером инфраструктуры. Не существует специфической инфраструктуры Heroku для работы с исходящими соединениями. Единственный интересный объект - это виртуальный интерфейс, который использует Dynos, и, соответственно, конфигурация сети хоста Dyno, но, опять же, в этом нет ничего особенного. Он использует платформу инфраструктуры, предоставленную конфигурацию сети, необходимую для связи хоста.
Пока что ни я, ни инженеры не дали краткого ответа на эти проблемы, учитывая их несоответствие, и наша текущая рекомендация заключается в том, что эти проблемы лучше решать с помощью обработки ошибок соединения, регистрации по мере необходимости и повторных попыток.
Если у вас есть сведения о том, как эта ошибка воспроизводится последовательно, это поможет нам значительно.
Я работал над этим в приложении Ruby on Rails, повторяя эти ошибки. До сих пор, кажется, избавился от ошибок. Раньше мы получали около 5 в день, а в прошлый день - с момента введения этого обходного пути - не было ни одного.
В config/initializers/redis_heroku_error_handling.rb
:
# Hack to retry errors on Heroku Redis: https://stackru.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