Технические заметки одного Евтуховича

Рассказ о серых трудовых буднях инженера программных систем

Изменение формата сессии при переходе с Rails 3.0 на 3.2

| Комментарии

Групон сейчас активно переводится с rails 3.0 на rails 3.2. Самая досадная неожиданность, которая встретилась в процессе переезда — это изменение формата хранения flash сообщений в сессии. Если бы при этом flash сообщение пропадало, то это было бы не страшно, но при декодировании сессии происходит исключение, и пользователь видит 500 ошибку до тех пор, пока не почистит куки. Конечно, пользователей, у которых в сессии будет flash сообщение в момент переключения с версии 3.0 на 3.2 будет не так и много, но это пользователи, потерянные навсегда.

Об этом даже завели баг в трекере на гитхабе. Jose Valim сделал по этому поводу коммит, в котором добавил комментарий к классу FlashHash с просьбой не менять сигнатуру класса без надобности. Также в трекере предлагали поменять ключ сессии, то есть разлогинить всех пользователей с сайта. Я не знаю, можно ли будет потом объяснить такой поступок нашему отделу продаж, потому что то, что они заметно упадут, проверено на опыте.

Поэтому я пошел дальше, и решил поправить эту ошибку. Изучение того, как работает Marshal.load особой радости не принесло, потому что читать код на C без хорошего знания внутренностей ruby — занятие достаточно бесполезное.

Я посмотрел, что поменялось, и достаточно быстро пришел к хорошему решению — отнаследовать класс FlashHash от Hash.

# config/initializers/session_hack.rb

module ActionDispatch
  class Flash
    remove_const :FlashHash # чтобы можно было полностью переопределить класс

    class FlashNow < Hash
      # здесь полная копия класса FlashHash из
      # action_dispatch/middleware/flash.rb в геме actionpack
    end
  end
end

Но мне оно не очень понравилось, слишком какой-то сильный хак, который надо будет потом поддерживать и про него не забыть. Поэтому я решил, что разлогинить пользователей, у которых есть flash сообщение в сессии — это не очень большой грех (а их ведь мало совсем), поэтому поступил вот так.

# config/initializers/session_hack.rb
module ActiveSupport
  class MessageVerifier
    def compat_verify(signed_message)
      raise InvalidSignature if signed_message.blank?

      data, digest = signed_message.split("--")
      if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
        result = Marshal.load(Base64.decode64(data)) rescue {} # а вот здесь все валилось
      else
        raise InvalidSignature
      end
    end
  end
end

Неясно в этой истории только одно. Зачем в фреймворке, который используется „в бою“ на огромном количестве систем, вносить несовместимые изменения, которые можно было бы не вносить. Учитывая то, что с flash сообщениями уже были проблемы при переходе с 2.3 на 3.0, я думаю, может быть стоит по-другому сериализовать данные в сессию, потому что загадочный Marhsal с его „шикарным“ ArgumentError иногда вызывает недоумение.

rails

Комментарии