2013-05-25 9 views
7

私は最近、デフォルトのSimple I18nバックエンドから私のI18nのRedisバックエンドに切り替えます。翻訳を簡単に処理できるようにしましたが、すべてのページでかなりのパフォーマンスが発生したことがわかりました。RailsのI18nではRedisが遅すぎますか?

私はMBPにRails 3.2とRedis 2.6.4をインストールしたベンチマークをいくつか試してみました。私はクライアントとしてhiredis-rbを使用しています。

2つの異なるバックエンドを実行すると、かなり明確な違いになります。シンプルなバックエンドとの最初の呼び出しに短い遅延があります - 私は翻訳がメモリにロードされていると仮定 - と、その後の素晴らしいパフォーマンス:

pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.143246 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.00415 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.004153 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.004056 

Redisのバックエンドは常に遅いです:

pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.122448 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.263564 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.232637 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.122304 

なぜこれがI18nでは遅いのか、私には絶対的な意味があります。私は、コードベース全体で何十億ものI18nコールを待ち行列に入れています。

pry(main)> keys = $redis.keys[0..500] 
pry(main)> Benchmark.realtime { $redis.mget keys } 
=> 0.04264 

しかし、私は本当に既存のI18nバックエンドのいずれかでこれを行うにはきれいな方法が表示されない:私は、フロントまで一緒にバッチそれらをすることができれば、私は良い形になるだろう。誰かがこの問題に取り組んでいますか?

EDIT

私はクリス・ヒールドの提案を取り、メモ化、単純なキャッシュバストを持つバックエンドを作成しました。要点はここまでです:

https://gist.github.com/wheeyls/5650947

私は数日のためにこれを試してみましょう、その後、宝石にそれを回します。

UPDATE

私のソリューションは現在、宝石のように提供されています:

https://github.com/wheeyls/cached_key_value_store

そして私もこの問題についてブログ:

http://about.g2crowd.com/faster-i18nredis-on-rails/

+0

Redisサーバーは、これらのベンチマークを実行しているボックスとの関係でどこですか? – deefour

+0

これは私のローカルマシン上で実行されました。私は明確にするために私の質問を編集します。 – Wheeyls

答えて

3

ネットワークトラフィックは常に遅くなります地元の仕事よりも。メモリ内のキャッシュを検討し、キャッシュを無効にするかどうかを判断するために、各リクエスト(または短いタイマー)で現在のローカリゼーションバージョンをプルするだけです。 Memoizationモジュール(the source here)があり、I18nインターフェイスに混ぜるだけのようです。次に、#lookupメソッドを調整して、5分ごとにRedisで更新されたロケールバージョンを確認し、新しい翻訳が保存されるときにロケールバージョンをインクリメントするようにします。

これにより、すべての翻訳がメモリにキャッシュされるため、非常に高速な検索が可能になり、翻訳の変更を即座に行うことができます。翻訳には最大5分かかりますが、キャッシュ・パージを明示的に行う必要はありません。

希望する場合は、遅くて5分の有効期限を使用するのではなく、before_filterですべての要求をチェックするようにすることができます。これにより、再要求が増えますが、古い翻訳は表示されません。

module I18n 
    module Backend 
    class CachedKeyValueStore < KeyValue 
     include Memoize 

     def store_translations(locale, data, options = {}) 
     @store.incr "locale_version:#{locale}" 
     reset_memoizations!(locale) 
     super 
     end 

     def lookup(locale, key, scope = nil, options = {}) 
     ensure_freshness(locale) 
     flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale, 
      key, scope, options[:separator]).to_sym 
     flat_hash = memoized_lookup[locale.to_sym] 
     flat_hash.key?(flat_key) ? flat_hash[flat_key] : (flat_hash[flat_key] = super) 
     end 

     def ensure_freshness(locale) 
     @last_check ||= 0 

     if @last_check < 5.minutes.ago 
      @last_check = Time.now 
      current_version = @store.get "locale_version:#{locale}" 
      if @last_version != current_version 
      reset_memoizations! locale 
      @last_version = current_version 
      end 
     end 
     end 
    end 
    end 
end 

私は、国際化のソースを読んでからこれをハッキングし、私は全くそれをテストしていないので、それはいくつかの作業が必要になる場合がありますが、私はそれが十分にアイデアを伝えると思います。

+0

Nifty。これは基本的には私が心に留めていた解決策です。なぜ誰もこれに対処していないように思えるのだろうか? – Wheeyls

+0

Gistはここにあります:https://gist.github.com/wheeyls/5650947 – Wheeyls

+0

ニース。私はこれを覚えて、それを使用後の積み重ねに貼りつけるつもりです。 :) –

関連する問題