rails / sprockets

Rack-based asset packaging system
MIT License
947 stars 788 forks source link

cache nil values in the CachedEnvironment #723

Closed lsylvester closed 2 years ago

lsylvester commented 2 years ago

Values cached in the CachedEnvironment can be nil - in which case the current ||= caching still calls through to the original environment.

Benchmarking script (this was inside a real app - so your numbers may vary depending on assets/paths in the application):

# frozen_string_literal: true

require 'benchmark/ips'

require_relative "./config/environment"

# Any benchmarking setup goes here...

original_cache = Rails.application.assets.cached

class NewCachedEnvironment < Sprockets::Base
  def initialize(environment)
    initialize_configuration(environment)

    @cache   = environment.cache
    @stats   = {}
    @entries = {}
    @uris    = {}
    @processor_cache_keys = {}
    @resolved_dependencies = {}
  end

  # No-op return self as cached environment.
  def cached
    self
  end
  alias_method :index, :cached

  # Internal: Cache Environment#entries
  def entries(path)
    @entries.fetch(path){ @entries[path] = super(path) }
  end

  # Internal: Cache Environment#stat
  def stat(path)
    @stats.fetch(path){ @stats[path] = super(path) }
  end

  # Internal: Cache Environment#load
  def load(uri)
    @uris.fetch(uri){ @uris[uri] = super(uri) }
  end

  # Internal: Cache Environment#processor_cache_key
  def processor_cache_key(str)
    @processor_cache_keys.fetch(str){ @processor_cache_keys[str] = super(str) }
  end

  # Internal: Cache Environment#resolve_dependency
  def resolve_dependency(str)
    @resolved_dependencies.fetch(str){ @resolved_dependencies[str] = super(str) }
  end

  private
    # Cache is immutable, any methods that try to change the runtime config
    # should bomb.
    def config=(config)
      raise RuntimeError, "can't modify immutable cached environment"
    end
end

new_cache = NewCachedEnvironment.new(Rails.application.assets)

Benchmark.ips do |x|

  x.report("original") do
    original_cache.find_asset("rails-ujs.js")
  end

  x.report("new") do
    new_cache.find_asset("rails-ujs.js")
  end

  x.compare!
end

Result

Warming up --------------------------------------
            original    35.000  i/100ms
                 new    53.000  i/100ms
Calculating -------------------------------------
            original    350.256  (± 6.9%) i/s -      1.750k in   5.020250s
                 new    472.983  (± 8.0%) i/s -      2.385k in   5.076110s

Comparison:
                 new:      473.0 i/s
            original:      350.3 i/s - 1.35x  (± 0.00) slower