class I18n::Backend::Http
Constants
- ALLOWED_STATS
- FAILED_GET
- STATS_NAMESPACE
- VERSION
Public Class Methods
new(options)
click to toggle source
# File lib/i18n/backend/http.rb, line 17 def initialize(options) @options = { http_open_timeout: 1, http_read_timeout: 1, http_open_retries: 0, http_read_retries: 0, polling_interval: 10*60, cache: nil, poll: true, exception_handler: -> (e) { $stderr.puts e }, memory_cache_size: 10, }.merge(options) @http_client = EtagHttpClient.new(@options) @translations = LRUCache.new(@options[:memory_cache_size]) start_polling if @options[:poll] end
Public Instance Methods
available_locales()
click to toggle source
# File lib/i18n/backend/http.rb, line 35 def available_locales @translations.keys.map(&:to_sym).select { |l| l != :i18n } end
stop_polling()
click to toggle source
# File lib/i18n/backend/http.rb, line 39 def stop_polling @stop_polling = true end
Protected Instance Methods
cache_key(locale)
click to toggle source
# File lib/i18n/backend/http.rb, line 106 def cache_key(locale) "i18n/backend/http/translations/#{locale}/v2" end
download_translations(locale, etag:)
click to toggle source
# File lib/i18n/backend/http.rb, line 110 def download_translations(locale, etag:) download_path = path(locale) with_retry do result, new_etag = @http_client.download(download_path, etag: etag) [parse_response(result), new_etag] if result end rescue => e record(:download_fail, tags: ["exception:#{e.class}", "path:#{download_path}"]) @options.fetch(:exception_handler).call(e) [self.class::FAILED_GET, nil] end
fetch_and_update_cached_translations(locale, old_etag, update:)
click to toggle source
# File lib/i18n/backend/http.rb, line 63 def fetch_and_update_cached_translations(locale, old_etag, update:) if cache = @options.fetch(:cache) key = cache_key(locale) interval = @options.fetch(:polling_interval) now = Time.now # capture time before we do slow work to stay on schedule old_value, old_etag, expires_at = cache.read(key) # assumes the cache is more recent then our local storage if old_value && (!update || expires_at > now || !updater?(cache, key, interval)) return [old_value, old_etag] end new_value, new_etag = download_translations(locale, etag: old_etag) new_expires_at = now + interval cache.write(key, [new_value, new_etag, new_expires_at]) [new_value, new_etag] else download_translations(locale, etag: old_etag) end end
lookup(locale, key, scope = [], options = {})
click to toggle source
# File lib/i18n/backend/http.rb, line 54 def lookup(locale, key, scope = [], options = {}) key = ::I18n.normalize_keys(locale, key, scope, options[:separator])[1..-1].join('.') lookup_key translations(locale), key end
lookup_key(translations, key)
click to toggle source
hook for extension with other resolution method
# File lib/i18n/backend/http.rb, line 132 def lookup_key(translations, key) translations[key] end
parse_response(body)
click to toggle source
# File lib/i18n/backend/http.rb, line 123 def parse_response(body) raise "implement parse_response" end
path(locale)
click to toggle source
# File lib/i18n/backend/http.rb, line 127 def path(locale) raise "implement path" end
record(event, options = {})
click to toggle source
# File lib/i18n/backend/http.rb, line 152 def record(event, options = {}) return unless statsd = @options[:statsd_client] raise "Unknown statsd event type to record" unless ALLOWED_STATS.include?(event) statsd.increment("#{STATS_NAMESPACE}.#{event}", tags: options[:tags]) end
start_polling()
click to toggle source
# File lib/i18n/backend/http.rb, line 45 def start_polling Thread.new do until @stop_polling sleep(@options.fetch(:polling_interval)) update_caches end end end
translations(locale)
click to toggle source
# File lib/i18n/backend/http.rb, line 59 def translations(locale) (@translations[locale] ||= fetch_and_update_cached_translations(locale, nil, update: false)).first end
update_caches()
click to toggle source
when download fails we keep our old caches since they are most likely better then nothing
# File lib/i18n/backend/http.rb, line 96 def update_caches @translations.keys.each do |locale| _, old_etag = @translations[locale] result = fetch_and_update_cached_translations(locale, old_etag, update: true) if result && result.first != self.class::FAILED_GET @translations[locale] = result end end end
updater?(cache, key, interval)
click to toggle source
sync with the cache who is going to update the cache this overlaps with the expiration interval, so worst case we will get 2x the interval if all servers are in sync and check updater at the same time
# File lib/i18n/backend/http.rb, line 86 def updater?(cache, key, interval) cache.write( "#{key}-lock", true, expires_in: interval, unless_exist: true ) end
with_retry() { || ... }
click to toggle source
# File lib/i18n/backend/http.rb, line 136 def with_retry open_tries ||= 0 read_tries ||= 0 yield rescue Faraday::ConnectionFailed => e raise unless e.instance_variable_get(:@wrapped_exception).is_a?(Net::OpenTimeout) raise if (open_tries += 1) > @options[:http_open_retries] record :open_retry retry rescue Faraday::TimeoutError => e raise unless e.instance_variable_get(:@wrapped_exception).is_a?(Net::ReadTimeout) raise if (read_tries += 1) > @options[:http_read_retries] record :read_retry retry end