identity_cache 0.5.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
 - data/.github/probots.yml +2 -0
 - data/.github/workflows/ci.yml +26 -0
 - data/.gitignore +1 -0
 - data/.rubocop.yml +5 -0
 - data/.travis.yml +24 -9
 - data/CHANGELOG.md +21 -0
 - data/Gemfile +5 -1
 - data/README.md +28 -26
 - data/Rakefile +14 -5
 - data/dev.yml +9 -16
 - data/gemfiles/Gemfile.latest-release +6 -0
 - data/gemfiles/Gemfile.rails-edge +6 -0
 - data/gemfiles/Gemfile.rails52 +6 -0
 - data/identity_cache.gemspec +26 -10
 - data/lib/identity_cache.rb +49 -46
 - data/lib/identity_cache/belongs_to_caching.rb +12 -40
 - data/lib/identity_cache/cache_fetcher.rb +6 -5
 - data/lib/identity_cache/cache_hash.rb +2 -2
 - data/lib/identity_cache/cache_invalidation.rb +4 -11
 - data/lib/identity_cache/cache_key_generation.rb +17 -65
 - data/lib/identity_cache/cache_key_loader.rb +128 -0
 - data/lib/identity_cache/cached.rb +7 -0
 - data/lib/identity_cache/cached/association.rb +87 -0
 - data/lib/identity_cache/cached/attribute.rb +123 -0
 - data/lib/identity_cache/cached/attribute_by_multi.rb +37 -0
 - data/lib/identity_cache/cached/attribute_by_one.rb +88 -0
 - data/lib/identity_cache/cached/belongs_to.rb +93 -0
 - data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
 - data/lib/identity_cache/cached/prefetcher.rb +51 -0
 - data/lib/identity_cache/cached/primary_index.rb +97 -0
 - data/lib/identity_cache/cached/recursive/association.rb +68 -0
 - data/lib/identity_cache/cached/recursive/has_many.rb +9 -0
 - data/lib/identity_cache/cached/recursive/has_one.rb +9 -0
 - data/lib/identity_cache/cached/reference/association.rb +16 -0
 - data/lib/identity_cache/cached/reference/has_many.rb +105 -0
 - data/lib/identity_cache/cached/reference/has_one.rb +100 -0
 - data/lib/identity_cache/configuration_dsl.rb +53 -215
 - data/lib/identity_cache/encoder.rb +95 -0
 - data/lib/identity_cache/expiry_hook.rb +36 -0
 - data/lib/identity_cache/fallback_fetcher.rb +2 -1
 - data/lib/identity_cache/load_strategy/eager.rb +28 -0
 - data/lib/identity_cache/load_strategy/lazy.rb +71 -0
 - data/lib/identity_cache/load_strategy/load_request.rb +20 -0
 - data/lib/identity_cache/load_strategy/multi_load_request.rb +27 -0
 - data/lib/identity_cache/memoized_cache_proxy.rb +127 -58
 - data/lib/identity_cache/parent_model_expiration.rb +45 -11
 - data/lib/identity_cache/query_api.rb +128 -394
 - data/lib/identity_cache/railtie.rb +8 -0
 - data/lib/identity_cache/record_not_found.rb +6 -0
 - data/lib/identity_cache/should_use_cache.rb +1 -0
 - data/lib/identity_cache/version.rb +3 -2
 - data/lib/identity_cache/with_primary_index.rb +136 -0
 - data/lib/identity_cache/without_primary_index.rb +24 -3
 - data/performance/cache_runner.rb +28 -34
 - data/performance/cpu.rb +3 -2
 - data/performance/externals.rb +4 -3
 - data/performance/profile.rb +6 -5
 - data/railgun.yml +16 -0
 - metadata +44 -73
 - data/Gemfile.rails42 +0 -6
 - data/Gemfile.rails50 +0 -6
 - data/test/attribute_cache_test.rb +0 -110
 - data/test/cache_fetch_includes_test.rb +0 -46
 - data/test/cache_hash_test.rb +0 -14
 - data/test/cache_invalidation_test.rb +0 -139
 - data/test/deeply_nested_associated_record_test.rb +0 -19
 - data/test/denormalized_has_many_test.rb +0 -214
 - data/test/denormalized_has_one_test.rb +0 -160
 - data/test/fetch_multi_test.rb +0 -308
 - data/test/fetch_test.rb +0 -258
 - data/test/fixtures/serialized_record.mysql2 +0 -0
 - data/test/fixtures/serialized_record.postgresql +0 -0
 - data/test/helpers/active_record_objects.rb +0 -106
 - data/test/helpers/database_connection.rb +0 -72
 - data/test/helpers/serialization_format.rb +0 -51
 - data/test/helpers/update_serialization_format.rb +0 -27
 - data/test/identity_cache_test.rb +0 -29
 - data/test/index_cache_test.rb +0 -161
 - data/test/memoized_attributes_test.rb +0 -59
 - data/test/memoized_cache_proxy_test.rb +0 -107
 - data/test/normalized_belongs_to_test.rb +0 -107
 - data/test/normalized_has_many_test.rb +0 -231
 - data/test/normalized_has_one_test.rb +0 -9
 - data/test/prefetch_associations_test.rb +0 -379
 - data/test/readonly_test.rb +0 -109
 - data/test/recursive_denormalized_has_many_test.rb +0 -131
 - data/test/save_test.rb +0 -82
 - data/test/schema_change_test.rb +0 -112
 - data/test/serialization_format_change_test.rb +0 -16
 - data/test/test_helper.rb +0 -140
 
| 
         @@ -0,0 +1,136 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module IdentityCache
         
     | 
| 
      
 3 
     | 
    
         
            +
              module WithPrimaryIndex
         
     | 
| 
      
 4 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                include WithoutPrimaryIndex
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def expire_cache
         
     | 
| 
      
 9 
     | 
    
         
            +
                  expire_primary_index
         
     | 
| 
      
 10 
     | 
    
         
            +
                  super
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 14 
     | 
    
         
            +
                def expire_primary_index # :nodoc:
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.class.expire_primary_key_cache_index(id)
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 19 
     | 
    
         
            +
                def primary_cache_index_key # :nodoc:
         
     | 
| 
      
 20 
     | 
    
         
            +
                  self.class.cached_primary_index.cache_key(id)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def cached_primary_index
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @cached_primary_index ||= Cached::PrimaryIndex.new(self)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def primary_cache_index_enabled
         
     | 
| 
      
 30 
     | 
    
         
            +
                    true
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # Declares a new index in the cache for the class where IdentityCache was
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # included.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # IdentityCache will add a fetch_by_field1_and_field2_and_...field for every
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # index.
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # == Example:
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #  class Product
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #    include IdentityCache
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #    cache_index :name, :vendor
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # Will add Product.fetch_by_name_and_vendor
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # == Parameters
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # +fields+ Array of symbols or strings representing the fields in the index
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # == Options
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # * unique: if the index would only have unique values. Default is false
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def cache_index(*fields, unique: false)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    attribute_proc = -> { primary_key }
         
     | 
| 
      
 57 
     | 
    
         
            +
                    cache_attribute_by_alias(attribute_proc, alias_name: :id, by: fields, unique: unique)
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    field_list = fields.join("_and_")
         
     | 
| 
      
 60 
     | 
    
         
            +
                    arg_list = (0...fields.size).collect { |i| "arg#{i}" }.join(',')
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    if unique
         
     | 
| 
      
 63 
     | 
    
         
            +
                      instance_eval(<<-CODE, __FILE__, __LINE__ + 1)
         
     | 
| 
      
 64 
     | 
    
         
            +
                        def fetch_by_#{field_list}(#{arg_list}, includes: nil)
         
     | 
| 
      
 65 
     | 
    
         
            +
                          id = fetch_id_by_#{field_list}(#{arg_list})
         
     | 
| 
      
 66 
     | 
    
         
            +
                          id && fetch_by_id(id, includes: includes)
         
     | 
| 
      
 67 
     | 
    
         
            +
                        end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                        # exception throwing variant
         
     | 
| 
      
 70 
     | 
    
         
            +
                        def fetch_by_#{field_list}!(#{arg_list}, includes: nil)
         
     | 
| 
      
 71 
     | 
    
         
            +
                          fetch_by_#{field_list}(#{arg_list}, includes: includes) or raise IdentityCache::RecordNotFound
         
     | 
| 
      
 72 
     | 
    
         
            +
                        end
         
     | 
| 
      
 73 
     | 
    
         
            +
                      CODE
         
     | 
| 
      
 74 
     | 
    
         
            +
                    else
         
     | 
| 
      
 75 
     | 
    
         
            +
                      instance_eval(<<-CODE, __FILE__, __LINE__ + 1)
         
     | 
| 
      
 76 
     | 
    
         
            +
                        def fetch_by_#{field_list}(#{arg_list}, includes: nil)
         
     | 
| 
      
 77 
     | 
    
         
            +
                          ids = fetch_id_by_#{field_list}(#{arg_list})
         
     | 
| 
      
 78 
     | 
    
         
            +
                          ids.empty? ? ids : fetch_multi(ids, includes: includes)
         
     | 
| 
      
 79 
     | 
    
         
            +
                        end
         
     | 
| 
      
 80 
     | 
    
         
            +
                      CODE
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    if fields.length == 1
         
     | 
| 
      
 84 
     | 
    
         
            +
                      instance_eval(<<-CODE, __FILE__, __LINE__ + 1)
         
     | 
| 
      
 85 
     | 
    
         
            +
                        def fetch_multi_by_#{field_list}(index_values, includes: nil)
         
     | 
| 
      
 86 
     | 
    
         
            +
                          ids = fetch_multi_id_by_#{field_list}(index_values).values.flatten(1)
         
     | 
| 
      
 87 
     | 
    
         
            +
                          return ids if ids.empty?
         
     | 
| 
      
 88 
     | 
    
         
            +
                          fetch_multi(ids, includes: includes)
         
     | 
| 
      
 89 
     | 
    
         
            +
                        end
         
     | 
| 
      
 90 
     | 
    
         
            +
                      CODE
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  # Similar to ActiveRecord::Base#exists? will return true if the id can be
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # found in the cache or in the DB.
         
     | 
| 
      
 96 
     | 
    
         
            +
                  def exists_with_identity_cache?(id)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    !!fetch_by_id(id)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  # Default fetcher added to the model on inclusion, it behaves like
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # ActiveRecord::Base.where(id: id).first
         
     | 
| 
      
 102 
     | 
    
         
            +
                  def fetch_by_id(id, includes: nil)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    ensure_base_model
         
     | 
| 
      
 104 
     | 
    
         
            +
                    raise_if_scoped
         
     | 
| 
      
 105 
     | 
    
         
            +
                    record = cached_primary_index.fetch(id)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    prefetch_associations(includes, [record]) if record && includes
         
     | 
| 
      
 107 
     | 
    
         
            +
                    record
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  # Default fetcher added to the model on inclusion, it behaves like
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # ActiveRecord::Base.find, but will raise IdentityCache::RecordNotFound
         
     | 
| 
      
 112 
     | 
    
         
            +
                  # if the id is not in the cache.
         
     | 
| 
      
 113 
     | 
    
         
            +
                  def fetch(id, includes: nil)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    fetch_by_id(id, includes: includes) || raise(
         
     | 
| 
      
 115 
     | 
    
         
            +
                      IdentityCache::RecordNotFound, "Couldn't find #{name} with ID=#{id}"
         
     | 
| 
      
 116 
     | 
    
         
            +
                    )
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  # Default fetcher added to the model on inclusion, if behaves like
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # ActiveRecord::Base.find_all_by_id
         
     | 
| 
      
 121 
     | 
    
         
            +
                  def fetch_multi(*ids, includes: nil)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    ensure_base_model
         
     | 
| 
      
 123 
     | 
    
         
            +
                    raise_if_scoped
         
     | 
| 
      
 124 
     | 
    
         
            +
                    ids.flatten!(1)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    records = cached_primary_index.fetch_multi(ids)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    prefetch_associations(includes, records) if includes
         
     | 
| 
      
 127 
     | 
    
         
            +
                    records
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  # Invalidates the primary cache index for the associated record. Will not invalidate cached attributes.
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def expire_primary_key_cache_index(id)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    cached_primary_index.expire(id)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,10 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       1 
2 
     | 
    
         
             
            module IdentityCache
         
     | 
| 
       2 
3 
     | 
    
         
             
              module WithoutPrimaryIndex
         
     | 
| 
       3 
4 
     | 
    
         
             
                extend ActiveSupport::Concern
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
                 
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
                include ArTransactionChanges
         
     | 
| 
      
 7 
     | 
    
         
            +
                include IdentityCache::BelongsToCaching
         
     | 
| 
      
 8 
     | 
    
         
            +
                include IdentityCache::CacheKeyGeneration
         
     | 
| 
      
 9 
     | 
    
         
            +
                include IdentityCache::ConfigurationDSL
         
     | 
| 
      
 10 
     | 
    
         
            +
                include IdentityCache::QueryAPI
         
     | 
| 
      
 11 
     | 
    
         
            +
                include IdentityCache::CacheInvalidation
         
     | 
| 
      
 12 
     | 
    
         
            +
                include IdentityCache::ShouldUseCache
         
     | 
| 
      
 13 
     | 
    
         
            +
                include ParentModelExpiration
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def self.append_features(base) #:nodoc:
         
     | 
| 
      
 16 
     | 
    
         
            +
                  raise AlreadyIncludedError if base.include?(WithoutPrimaryIndex)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  super
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                included do
         
     | 
| 
      
 21 
     | 
    
         
            +
                  class_attribute(:cached_model)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  self.cached_model = self
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def primary_cache_index_enabled
         
     | 
| 
      
 27 
     | 
    
         
            +
                    false
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
       8 
29 
     | 
    
         
             
                end
         
     | 
| 
       9 
30 
     | 
    
         
             
              end
         
     | 
| 
       10 
31 
     | 
    
         
             
            end
         
     | 
    
        data/performance/cache_runner.rb
    CHANGED
    
    | 
         @@ -1,4 +1,5 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
         
     | 
| 
       2 
3 
     | 
    
         
             
            require 'active_record'
         
     | 
| 
       3 
4 
     | 
    
         
             
            require 'active_support/core_ext'
         
     | 
| 
       4 
5 
     | 
    
         
             
            require 'active_support/cache'
         
     | 
| 
         @@ -6,25 +7,19 @@ require 'identity_cache' 
     | 
|
| 
       6 
7 
     | 
    
         
             
            require 'memcached_store'
         
     | 
| 
       7 
8 
     | 
    
         
             
            require 'active_support/cache/memcached_store'
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            $memcached_port = 11211
         
     | 
| 
       10 
     | 
    
         
            -
            $mysql_port = 3306
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
10 
     | 
    
         
             
            require File.dirname(__FILE__) + '/../test/helpers/active_record_objects'
         
     | 
| 
       13 
11 
     | 
    
         
             
            require File.dirname(__FILE__) + '/../test/helpers/database_connection'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/../test/helpers/cache_connection'
         
     | 
| 
       14 
13 
     | 
    
         | 
| 
       15 
14 
     | 
    
         
             
            IdentityCache.logger = Logger.new(nil)
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
            if ActiveRecord.gem_version < Gem::Version.new('5') && ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks=)
         
     | 
| 
       19 
     | 
    
         
            -
              ActiveRecord::Base.raise_in_transactional_callbacks = true
         
     | 
| 
       20 
     | 
    
         
            -
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
            CacheConnection.setup
         
     | 
| 
       21 
16 
     | 
    
         | 
| 
       22 
17 
     | 
    
         
             
            def create_record(id)
         
     | 
| 
       23 
18 
     | 
    
         
             
              Item.new(id)
         
     | 
| 
       24 
19 
     | 
    
         
             
            end
         
     | 
| 
       25 
20 
     | 
    
         | 
| 
       26 
21 
     | 
    
         
             
            def database_ready(count)
         
     | 
| 
       27 
     | 
    
         
            -
              Item.where(: 
     | 
| 
      
 22 
     | 
    
         
            +
              Item.where(id: (1..count)).count == count
         
     | 
| 
       28 
23 
     | 
    
         
             
            rescue
         
     | 
| 
       29 
24 
     | 
    
         
             
              false
         
     | 
| 
       30 
25 
     | 
    
         
             
            end
         
     | 
| 
         @@ -42,26 +37,25 @@ def create_database(count) 
     | 
|
| 
       42 
37 
     | 
    
         
             
              DatabaseConnection.create_tables
         
     | 
| 
       43 
38 
     | 
    
         
             
              existing = Item.all
         
     | 
| 
       44 
39 
     | 
    
         
             
              (1..count).to_a.each do |i|
         
     | 
| 
       45 
     | 
    
         
            -
                 
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                  end
         
     | 
| 
       54 
     | 
    
         
            -
                  a.save
         
     | 
| 
      
 40 
     | 
    
         
            +
                next if existing.any? { |e| e.id == i }
         
     | 
| 
      
 41 
     | 
    
         
            +
                a = Item.new
         
     | 
| 
      
 42 
     | 
    
         
            +
                a.id = i
         
     | 
| 
      
 43 
     | 
    
         
            +
                a.associated = AssociatedRecord.new(name: "Associated for #{i}")
         
     | 
| 
      
 44 
     | 
    
         
            +
                a.associated_records
         
     | 
| 
      
 45 
     | 
    
         
            +
                (1..5).each do |j|
         
     | 
| 
      
 46 
     | 
    
         
            +
                  a.associated_records << AssociatedRecord.new(name: "Has Many #{j} for #{i}")
         
     | 
| 
      
 47 
     | 
    
         
            +
                  a.normalized_associated_records << NormalizedAssociatedRecord.new(name: "Normalized Has Many #{j} for #{i}")
         
     | 
| 
       55 
48 
     | 
    
         
             
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                a.save
         
     | 
| 
       56 
50 
     | 
    
         
             
              end
         
     | 
| 
       57 
51 
     | 
    
         
             
            ensure
         
     | 
| 
       58 
52 
     | 
    
         
             
              helper.teardown_models
         
     | 
| 
       59 
53 
     | 
    
         
             
            end
         
     | 
| 
       60 
54 
     | 
    
         | 
| 
       61 
55 
     | 
    
         
             
            def setup_embedded_associations
         
     | 
| 
       62 
     | 
    
         
            -
              Item.cache_has_one : 
     | 
| 
       63 
     | 
    
         
            -
              Item.cache_has_many 
     | 
| 
       64 
     | 
    
         
            -
              AssociatedRecord.cache_has_many 
     | 
| 
      
 56 
     | 
    
         
            +
              Item.cache_has_one(:associated, embed: true)
         
     | 
| 
      
 57 
     | 
    
         
            +
              Item.cache_has_many(:associated_records, embed: true)
         
     | 
| 
      
 58 
     | 
    
         
            +
              AssociatedRecord.cache_has_many(:deeply_associated_records, embed: true)
         
     | 
| 
       65 
59 
     | 
    
         
             
            end
         
     | 
| 
       66 
60 
     | 
    
         | 
| 
       67 
61 
     | 
    
         
             
            class CacheRunner
         
     | 
| 
         @@ -86,7 +80,7 @@ CACHE_RUNNERS = [] 
     | 
|
| 
       86 
80 
     | 
    
         
             
            class FindRunner < CacheRunner
         
     | 
| 
       87 
81 
     | 
    
         
             
              def run
         
     | 
| 
       88 
82 
     | 
    
         
             
                (1..@count).each do |i|
         
     | 
| 
       89 
     | 
    
         
            -
                  ::Item.includes(:associated,  
     | 
| 
      
 83 
     | 
    
         
            +
                  ::Item.includes(:associated, associated_records: :deeply_associated_records).find(i)
         
     | 
| 
       90 
84 
     | 
    
         
             
                end
         
     | 
| 
       91 
85 
     | 
    
         
             
              end
         
     | 
| 
       92 
86 
     | 
    
         
             
            end
         
     | 
| 
         @@ -109,18 +103,18 @@ end 
     | 
|
| 
       109 
103 
     | 
    
         
             
            module DeletedRunner
         
     | 
| 
       110 
104 
     | 
    
         
             
              def prepare
         
     | 
| 
       111 
105 
     | 
    
         
             
                super
         
     | 
| 
       112 
     | 
    
         
            -
                (1..@count).each {|i| ::Item.find(i). 
     | 
| 
      
 106 
     | 
    
         
            +
                (1..@count).each { |i| ::Item.find(i).expire_cache }
         
     | 
| 
       113 
107 
     | 
    
         
             
              end
         
     | 
| 
       114 
108 
     | 
    
         
             
            end
         
     | 
| 
       115 
109 
     | 
    
         | 
| 
       116 
110 
     | 
    
         
             
            module ConflictRunner
         
     | 
| 
       117 
111 
     | 
    
         
             
              def prepare
         
     | 
| 
       118 
112 
     | 
    
         
             
                super
         
     | 
| 
       119 
     | 
    
         
            -
                records = (1..@count).map {|id| ::Item.find(id) }
         
     | 
| 
      
 113 
     | 
    
         
            +
                records = (1..@count).map { |id| ::Item.find(id) }
         
     | 
| 
       120 
114 
     | 
    
         
             
                orig_resolve_cache_miss = ::Item.method(:resolve_cache_miss)
         
     | 
| 
       121 
115 
     | 
    
         | 
| 
       122 
116 
     | 
    
         
             
                ::Item.define_singleton_method(:resolve_cache_miss) do |id|
         
     | 
| 
       123 
     | 
    
         
            -
                  records[id-1]. 
     | 
| 
      
 117 
     | 
    
         
            +
                  records[id - 1].expire_cache
         
     | 
| 
       124 
118 
     | 
    
         
             
                  orig_resolve_cache_miss.call(id)
         
     | 
| 
       125 
119 
     | 
    
         
             
                end
         
     | 
| 
       126 
120 
     | 
    
         
             
                IdentityCache.cache.clear
         
     | 
| 
         @@ -131,16 +125,16 @@ module DeletedConflictRunner 
     | 
|
| 
       131 
125 
     | 
    
         
             
              include ConflictRunner
         
     | 
| 
       132 
126 
     | 
    
         
             
              def prepare
         
     | 
| 
       133 
127 
     | 
    
         
             
                super
         
     | 
| 
       134 
     | 
    
         
            -
                (1..@count).each {|i| ::Item.find(i). 
     | 
| 
      
 128 
     | 
    
         
            +
                (1..@count).each { |i| ::Item.find(i).expire_cache }
         
     | 
| 
       135 
129 
     | 
    
         
             
              end
         
     | 
| 
       136 
130 
     | 
    
         
             
            end
         
     | 
| 
       137 
131 
     | 
    
         | 
| 
       138 
132 
     | 
    
         
             
            class EmbedRunner < CacheRunner
         
     | 
| 
       139 
133 
     | 
    
         
             
              def setup_models
         
     | 
| 
       140 
134 
     | 
    
         
             
                super
         
     | 
| 
       141 
     | 
    
         
            -
                Item.cache_has_one : 
     | 
| 
       142 
     | 
    
         
            -
                Item.cache_has_many 
     | 
| 
       143 
     | 
    
         
            -
                AssociatedRecord.cache_has_many 
     | 
| 
      
 135 
     | 
    
         
            +
                Item.cache_has_one(:associated, embed: true)
         
     | 
| 
      
 136 
     | 
    
         
            +
                Item.cache_has_many(:associated_records, embed: true)
         
     | 
| 
      
 137 
     | 
    
         
            +
                AssociatedRecord.cache_has_many(:deeply_associated_records, embed: true)
         
     | 
| 
       144 
138 
     | 
    
         
             
              end
         
     | 
| 
       145 
139 
     | 
    
         | 
| 
       146 
140 
     | 
    
         
             
              def run
         
     | 
| 
         @@ -180,9 +174,9 @@ CACHE_RUNNERS << FetchEmbedDeletedConflictRunner 
     | 
|
| 
       180 
174 
     | 
    
         
             
            class NormalizedRunner < CacheRunner
         
     | 
| 
       181 
175 
     | 
    
         
             
              def setup_models
         
     | 
| 
       182 
176 
     | 
    
         
             
                super
         
     | 
| 
       183 
     | 
    
         
            -
                Item.cache_has_one 
     | 
| 
       184 
     | 
    
         
            -
                Item.cache_has_many 
     | 
| 
       185 
     | 
    
         
            -
                AssociatedRecord.cache_has_many 
     | 
| 
      
 177 
     | 
    
         
            +
                Item.cache_has_one(:associated, embed: :id)
         
     | 
| 
      
 178 
     | 
    
         
            +
                Item.cache_has_many(:associated_records, embed: :ids)
         
     | 
| 
      
 179 
     | 
    
         
            +
                AssociatedRecord.cache_has_many(:deeply_associated_records, embed: :ids)
         
     | 
| 
       186 
180 
     | 
    
         
             
              end
         
     | 
| 
       187 
181 
     | 
    
         | 
| 
       188 
182 
     | 
    
         
             
              def run
         
     | 
    
        data/performance/cpu.rb
    CHANGED
    
    | 
         @@ -1,3 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       1 
2 
     | 
    
         
             
            require 'rubygems'
         
     | 
| 
       2 
3 
     | 
    
         
             
            require 'benchmark'
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
         @@ -19,7 +20,7 @@ ensure 
     | 
|
| 
       19 
20 
     | 
    
         
             
              obj.cleanup
         
     | 
| 
       20 
21 
     | 
    
         
             
            end
         
     | 
| 
       21 
22 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
            def benchmark(runners, label_width=0)
         
     | 
| 
      
 23 
     | 
    
         
            +
            def benchmark(runners, label_width = 0)
         
     | 
| 
       23 
24 
     | 
    
         
             
              IdentityCache.cache.clear
         
     | 
| 
       24 
25 
     | 
    
         
             
              runners.each do |runner|
         
     | 
| 
       25 
26 
     | 
    
         
             
                print "#{runner.name}: ".ljust(label_width)
         
     | 
| 
         @@ -28,7 +29,7 @@ def benchmark(runners, label_width=0) 
     | 
|
| 
       28 
29 
     | 
    
         
             
            end
         
     | 
| 
       29 
30 
     | 
    
         | 
| 
       30 
31 
     | 
    
         
             
            def bmbm(runners)
         
     | 
| 
       31 
     | 
    
         
            -
              label_width = runners.map{ |r| r.name.size }.max + 2
         
     | 
| 
      
 32 
     | 
    
         
            +
              label_width = runners.map { |r| r.name.size }.max + 2
         
     | 
| 
       32 
33 
     | 
    
         
             
              width = label_width + Benchmark::CAPTION.size
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
       34 
35 
     | 
    
         
             
              puts 'Rehearsal: '.ljust(width, '-')
         
     | 
    
        data/performance/externals.rb
    CHANGED
    
    | 
         @@ -1,3 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       1 
2 
     | 
    
         
             
            require 'rubygems'
         
     | 
| 
       2 
3 
     | 
    
         
             
            require 'benchmark'
         
     | 
| 
       3 
4 
     | 
    
         
             
            require 'ruby-prof'
         
     | 
| 
         @@ -7,8 +8,8 @@ require_relative 'cache_runner' 
     | 
|
| 
       7 
8 
     | 
    
         
             
            RUNS = 1000
         
     | 
| 
       8 
9 
     | 
    
         
             
            RubyProf.measure_mode = RubyProf::CPU_TIME
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            EXTERNALS = {"Memcache" => ["MemCache#set", "MemCache#get"],
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
            EXTERNALS = { "Memcache" => ["MemCache#set", "MemCache#get"],
         
     | 
| 
      
 12 
     | 
    
         
            +
                          "Database" => ["Mysql2::Client#query"] }
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
14 
     | 
    
         
             
            def run(obj)
         
     | 
| 
       14 
15 
     | 
    
         
             
              obj.prepare
         
     | 
| 
         @@ -26,7 +27,7 @@ def count_externals(results) 
     | 
|
| 
       26 
27 
     | 
    
         
             
              count = {}
         
     | 
| 
       27 
28 
     | 
    
         
             
              results.split(/\n/).each do |line|
         
     | 
| 
       28 
29 
     | 
    
         
             
                fields = line.split
         
     | 
| 
       29 
     | 
    
         
            -
                if ext = EXTERNALS.detect { |e| e[1].any? { |method| method == fields[-1] } }
         
     | 
| 
      
 30 
     | 
    
         
            +
                if (ext = EXTERNALS.detect { |e| e[1].any? { |method| method == fields[-1] } })
         
     | 
| 
       30 
31 
     | 
    
         
             
                  count[ext[0]] ||= 0
         
     | 
| 
       31 
32 
     | 
    
         
             
                  count[ext[0]] += fields[-2].to_i
         
     | 
| 
       32 
33 
     | 
    
         
             
                end
         
     | 
    
        data/performance/profile.rb
    CHANGED
    
    | 
         @@ -1,3 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       1 
2 
     | 
    
         
             
            require 'rubygems'
         
     | 
| 
       2 
3 
     | 
    
         
             
            require 'benchmark'
         
     | 
| 
       3 
4 
     | 
    
         
             
            require 'stackprof'
         
     | 
| 
         @@ -21,15 +22,15 @@ end 
     | 
|
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         
             
            create_database(RUNS)
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
            if runner_name = ENV['RUNNER']
         
     | 
| 
       25 
     | 
    
         
            -
              if runner = CACHE_RUNNERS.find{ |r| r.name == runner_name }
         
     | 
| 
      
 25 
     | 
    
         
            +
            if (runner_name = ENV['RUNNER'])
         
     | 
| 
      
 26 
     | 
    
         
            +
              if (runner = CACHE_RUNNERS.find { |r| r.name == runner_name })
         
     | 
| 
       26 
27 
     | 
    
         
             
                run(runner.new(RUNS), filename: ENV['FILENAME'])
         
     | 
| 
       27 
28 
     | 
    
         
             
              else
         
     | 
| 
       28 
29 
     | 
    
         
             
                puts "Couldn't find cache runner #{runner_name.inspect}"
         
     | 
| 
       29 
     | 
    
         
            -
                exit 
     | 
| 
      
 30 
     | 
    
         
            +
                exit(1)
         
     | 
| 
       30 
31 
     | 
    
         
             
              end
         
     | 
| 
       31 
32 
     | 
    
         
             
            else
         
     | 
| 
       32 
     | 
    
         
            -
              CACHE_RUNNERS.each do | 
     | 
| 
       33 
     | 
    
         
            -
                run( 
     | 
| 
      
 33 
     | 
    
         
            +
              CACHE_RUNNERS.each do |cache_runner|
         
     | 
| 
      
 34 
     | 
    
         
            +
                run(cache_runner.new(RUNS))
         
     | 
| 
       34 
35 
     | 
    
         
             
              end
         
     | 
| 
       35 
36 
     | 
    
         
             
            end
         
     | 
    
        data/railgun.yml
    ADDED
    
    | 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # https://dev-accel.shopify.io/dev/railgun/Railgun-Config
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: identity-cache
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            vm:
         
     | 
| 
      
 5 
     | 
    
         
            +
              image:      /opt/dev/misc/railgun-images/default
         
     | 
| 
      
 6 
     | 
    
         
            +
              ip_address: 192.168.64.98
         
     | 
| 
      
 7 
     | 
    
         
            +
              memory:     1G
         
     | 
| 
      
 8 
     | 
    
         
            +
              cores:      2
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            volumes:
         
     | 
| 
      
 11 
     | 
    
         
            +
              root: 1G
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            services:
         
     | 
| 
      
 14 
     | 
    
         
            +
              - mysql
         
     | 
| 
      
 15 
     | 
    
         
            +
              - postgresql
         
     | 
| 
      
 16 
     | 
    
         
            +
              - memcached
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: identity_cache
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Camilo Lopez
         
     | 
| 
         @@ -14,7 +14,7 @@ authors: 
     | 
|
| 
       14 
14 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       15 
15 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       16 
16 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       17 
     | 
    
         
            -
            date:  
     | 
| 
      
 17 
     | 
    
         
            +
            date: 2020-02-13 00:00:00.000000000 Z
         
     | 
| 
       18 
18 
     | 
    
         
             
            dependencies:
         
     | 
| 
       19 
19 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       20 
20 
     | 
    
         
             
              name: ar_transaction_changes
         
     | 
| 
         @@ -36,14 +36,14 @@ dependencies: 
     | 
|
| 
       36 
36 
     | 
    
         
             
                requirements:
         
     | 
| 
       37 
37 
     | 
    
         
             
                - - ">="
         
     | 
| 
       38 
38 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       39 
     | 
    
         
            -
                    version:  
     | 
| 
      
 39 
     | 
    
         
            +
                    version: '5.2'
         
     | 
| 
       40 
40 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       41 
41 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       42 
42 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       43 
43 
     | 
    
         
             
                requirements:
         
     | 
| 
       44 
44 
     | 
    
         
             
                - - ">="
         
     | 
| 
       45 
45 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       46 
     | 
    
         
            -
                    version:  
     | 
| 
      
 46 
     | 
    
         
            +
                    version: '5.2'
         
     | 
| 
       47 
47 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       48 
48 
     | 
    
         
             
              name: memcached
         
     | 
| 
       49 
49 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -160,16 +160,16 @@ dependencies: 
     | 
|
| 
       160 
160 
     | 
    
         
             
              name: pg
         
     | 
| 
       161 
161 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       162 
162 
     | 
    
         
             
                requirements:
         
     | 
| 
       163 
     | 
    
         
            -
                - - " 
     | 
| 
      
 163 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       164 
164 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       165 
     | 
    
         
            -
                    version: '0'
         
     | 
| 
      
 165 
     | 
    
         
            +
                    version: '0.18'
         
     | 
| 
       166 
166 
     | 
    
         
             
              type: :development
         
     | 
| 
       167 
167 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       168 
168 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       169 
169 
     | 
    
         
             
                requirements:
         
     | 
| 
       170 
     | 
    
         
            -
                - - " 
     | 
| 
      
 170 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       171 
171 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       172 
     | 
    
         
            -
                    version: '0'
         
     | 
| 
      
 172 
     | 
    
         
            +
                    version: '0.18'
         
     | 
| 
       173 
173 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       174 
174 
     | 
    
         
             
              name: stackprof
         
     | 
| 
       175 
175 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -184,24 +184,28 @@ dependencies: 
     | 
|
| 
       184 
184 
     | 
    
         
             
                - - ">="
         
     | 
| 
       185 
185 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       186 
186 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       187 
     | 
    
         
            -
            description: Opt 
     | 
| 
      
 187 
     | 
    
         
            +
            description: Opt-in read through Active Record caching.
         
     | 
| 
       188 
188 
     | 
    
         
             
            email:
         
     | 
| 
       189 
189 
     | 
    
         
             
            - gems@shopify.com
         
     | 
| 
       190 
190 
     | 
    
         
             
            executables: []
         
     | 
| 
       191 
191 
     | 
    
         
             
            extensions: []
         
     | 
| 
       192 
192 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       193 
193 
     | 
    
         
             
            files:
         
     | 
| 
      
 194 
     | 
    
         
            +
            - ".github/probots.yml"
         
     | 
| 
      
 195 
     | 
    
         
            +
            - ".github/workflows/ci.yml"
         
     | 
| 
       194 
196 
     | 
    
         
             
            - ".gitignore"
         
     | 
| 
      
 197 
     | 
    
         
            +
            - ".rubocop.yml"
         
     | 
| 
       195 
198 
     | 
    
         
             
            - ".travis.yml"
         
     | 
| 
       196 
199 
     | 
    
         
             
            - CHANGELOG.md
         
     | 
| 
       197 
200 
     | 
    
         
             
            - CONTRIBUTING.md
         
     | 
| 
       198 
201 
     | 
    
         
             
            - Gemfile
         
     | 
| 
       199 
     | 
    
         
            -
            - Gemfile.rails42
         
     | 
| 
       200 
     | 
    
         
            -
            - Gemfile.rails50
         
     | 
| 
       201 
202 
     | 
    
         
             
            - LICENSE
         
     | 
| 
       202 
203 
     | 
    
         
             
            - README.md
         
     | 
| 
       203 
204 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       204 
205 
     | 
    
         
             
            - dev.yml
         
     | 
| 
      
 206 
     | 
    
         
            +
            - gemfiles/Gemfile.latest-release
         
     | 
| 
      
 207 
     | 
    
         
            +
            - gemfiles/Gemfile.rails-edge
         
     | 
| 
      
 208 
     | 
    
         
            +
            - gemfiles/Gemfile.rails52
         
     | 
| 
       205 
209 
     | 
    
         
             
            - identity_cache.gemspec
         
     | 
| 
       206 
210 
     | 
    
         
             
            - lib/identity_cache.rb
         
     | 
| 
       207 
211 
     | 
    
         
             
            - lib/identity_cache/belongs_to_caching.rb
         
     | 
| 
         @@ -209,48 +213,45 @@ files: 
     | 
|
| 
       209 
213 
     | 
    
         
             
            - lib/identity_cache/cache_hash.rb
         
     | 
| 
       210 
214 
     | 
    
         
             
            - lib/identity_cache/cache_invalidation.rb
         
     | 
| 
       211 
215 
     | 
    
         
             
            - lib/identity_cache/cache_key_generation.rb
         
     | 
| 
      
 216 
     | 
    
         
            +
            - lib/identity_cache/cache_key_loader.rb
         
     | 
| 
      
 217 
     | 
    
         
            +
            - lib/identity_cache/cached.rb
         
     | 
| 
      
 218 
     | 
    
         
            +
            - lib/identity_cache/cached/association.rb
         
     | 
| 
      
 219 
     | 
    
         
            +
            - lib/identity_cache/cached/attribute.rb
         
     | 
| 
      
 220 
     | 
    
         
            +
            - lib/identity_cache/cached/attribute_by_multi.rb
         
     | 
| 
      
 221 
     | 
    
         
            +
            - lib/identity_cache/cached/attribute_by_one.rb
         
     | 
| 
      
 222 
     | 
    
         
            +
            - lib/identity_cache/cached/belongs_to.rb
         
     | 
| 
      
 223 
     | 
    
         
            +
            - lib/identity_cache/cached/embedded_fetching.rb
         
     | 
| 
      
 224 
     | 
    
         
            +
            - lib/identity_cache/cached/prefetcher.rb
         
     | 
| 
      
 225 
     | 
    
         
            +
            - lib/identity_cache/cached/primary_index.rb
         
     | 
| 
      
 226 
     | 
    
         
            +
            - lib/identity_cache/cached/recursive/association.rb
         
     | 
| 
      
 227 
     | 
    
         
            +
            - lib/identity_cache/cached/recursive/has_many.rb
         
     | 
| 
      
 228 
     | 
    
         
            +
            - lib/identity_cache/cached/recursive/has_one.rb
         
     | 
| 
      
 229 
     | 
    
         
            +
            - lib/identity_cache/cached/reference/association.rb
         
     | 
| 
      
 230 
     | 
    
         
            +
            - lib/identity_cache/cached/reference/has_many.rb
         
     | 
| 
      
 231 
     | 
    
         
            +
            - lib/identity_cache/cached/reference/has_one.rb
         
     | 
| 
       212 
232 
     | 
    
         
             
            - lib/identity_cache/configuration_dsl.rb
         
     | 
| 
      
 233 
     | 
    
         
            +
            - lib/identity_cache/encoder.rb
         
     | 
| 
      
 234 
     | 
    
         
            +
            - lib/identity_cache/expiry_hook.rb
         
     | 
| 
       213 
235 
     | 
    
         
             
            - lib/identity_cache/fallback_fetcher.rb
         
     | 
| 
      
 236 
     | 
    
         
            +
            - lib/identity_cache/load_strategy/eager.rb
         
     | 
| 
      
 237 
     | 
    
         
            +
            - lib/identity_cache/load_strategy/lazy.rb
         
     | 
| 
      
 238 
     | 
    
         
            +
            - lib/identity_cache/load_strategy/load_request.rb
         
     | 
| 
      
 239 
     | 
    
         
            +
            - lib/identity_cache/load_strategy/multi_load_request.rb
         
     | 
| 
       214 
240 
     | 
    
         
             
            - lib/identity_cache/memoized_cache_proxy.rb
         
     | 
| 
       215 
241 
     | 
    
         
             
            - lib/identity_cache/parent_model_expiration.rb
         
     | 
| 
       216 
242 
     | 
    
         
             
            - lib/identity_cache/query_api.rb
         
     | 
| 
      
 243 
     | 
    
         
            +
            - lib/identity_cache/railtie.rb
         
     | 
| 
      
 244 
     | 
    
         
            +
            - lib/identity_cache/record_not_found.rb
         
     | 
| 
       217 
245 
     | 
    
         
             
            - lib/identity_cache/should_use_cache.rb
         
     | 
| 
       218 
246 
     | 
    
         
             
            - lib/identity_cache/version.rb
         
     | 
| 
      
 247 
     | 
    
         
            +
            - lib/identity_cache/with_primary_index.rb
         
     | 
| 
       219 
248 
     | 
    
         
             
            - lib/identity_cache/without_primary_index.rb
         
     | 
| 
       220 
249 
     | 
    
         
             
            - performance/cache_runner.rb
         
     | 
| 
       221 
250 
     | 
    
         
             
            - performance/cpu.rb
         
     | 
| 
       222 
251 
     | 
    
         
             
            - performance/externals.rb
         
     | 
| 
       223 
252 
     | 
    
         
             
            - performance/profile.rb
         
     | 
| 
      
 253 
     | 
    
         
            +
            - railgun.yml
         
     | 
| 
       224 
254 
     | 
    
         
             
            - shipit.rubygems.yml
         
     | 
| 
       225 
     | 
    
         
            -
            - test/attribute_cache_test.rb
         
     | 
| 
       226 
     | 
    
         
            -
            - test/cache_fetch_includes_test.rb
         
     | 
| 
       227 
     | 
    
         
            -
            - test/cache_hash_test.rb
         
     | 
| 
       228 
     | 
    
         
            -
            - test/cache_invalidation_test.rb
         
     | 
| 
       229 
     | 
    
         
            -
            - test/deeply_nested_associated_record_test.rb
         
     | 
| 
       230 
     | 
    
         
            -
            - test/denormalized_has_many_test.rb
         
     | 
| 
       231 
     | 
    
         
            -
            - test/denormalized_has_one_test.rb
         
     | 
| 
       232 
     | 
    
         
            -
            - test/fetch_multi_test.rb
         
     | 
| 
       233 
     | 
    
         
            -
            - test/fetch_test.rb
         
     | 
| 
       234 
     | 
    
         
            -
            - test/fixtures/serialized_record.mysql2
         
     | 
| 
       235 
     | 
    
         
            -
            - test/fixtures/serialized_record.postgresql
         
     | 
| 
       236 
     | 
    
         
            -
            - test/helpers/active_record_objects.rb
         
     | 
| 
       237 
     | 
    
         
            -
            - test/helpers/database_connection.rb
         
     | 
| 
       238 
     | 
    
         
            -
            - test/helpers/serialization_format.rb
         
     | 
| 
       239 
     | 
    
         
            -
            - test/helpers/update_serialization_format.rb
         
     | 
| 
       240 
     | 
    
         
            -
            - test/identity_cache_test.rb
         
     | 
| 
       241 
     | 
    
         
            -
            - test/index_cache_test.rb
         
     | 
| 
       242 
     | 
    
         
            -
            - test/memoized_attributes_test.rb
         
     | 
| 
       243 
     | 
    
         
            -
            - test/memoized_cache_proxy_test.rb
         
     | 
| 
       244 
     | 
    
         
            -
            - test/normalized_belongs_to_test.rb
         
     | 
| 
       245 
     | 
    
         
            -
            - test/normalized_has_many_test.rb
         
     | 
| 
       246 
     | 
    
         
            -
            - test/normalized_has_one_test.rb
         
     | 
| 
       247 
     | 
    
         
            -
            - test/prefetch_associations_test.rb
         
     | 
| 
       248 
     | 
    
         
            -
            - test/readonly_test.rb
         
     | 
| 
       249 
     | 
    
         
            -
            - test/recursive_denormalized_has_many_test.rb
         
     | 
| 
       250 
     | 
    
         
            -
            - test/save_test.rb
         
     | 
| 
       251 
     | 
    
         
            -
            - test/schema_change_test.rb
         
     | 
| 
       252 
     | 
    
         
            -
            - test/serialization_format_change_test.rb
         
     | 
| 
       253 
     | 
    
         
            -
            - test/test_helper.rb
         
     | 
| 
       254 
255 
     | 
    
         
             
            homepage: https://github.com/Shopify/identity_cache
         
     | 
| 
       255 
256 
     | 
    
         
             
            licenses: []
         
     | 
| 
       256 
257 
     | 
    
         
             
            metadata: {}
         
     | 
| 
         @@ -262,48 +263,18 @@ required_ruby_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       262 
263 
     | 
    
         
             
              requirements:
         
     | 
| 
       263 
264 
     | 
    
         
             
              - - ">="
         
     | 
| 
       264 
265 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       265 
     | 
    
         
            -
                  version: 2. 
     | 
| 
      
 266 
     | 
    
         
            +
                  version: 2.4.0
         
     | 
| 
       266 
267 
     | 
    
         
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
       267 
268 
     | 
    
         
             
              requirements:
         
     | 
| 
       268 
269 
     | 
    
         
             
              - - ">="
         
     | 
| 
       269 
270 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       270 
271 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       271 
272 
     | 
    
         
             
            requirements: []
         
     | 
| 
       272 
     | 
    
         
            -
             
     | 
| 
       273 
     | 
    
         
            -
            rubygems_version: 2.5.2
         
     | 
| 
      
 273 
     | 
    
         
            +
            rubygems_version: 3.0.3
         
     | 
| 
       274 
274 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       275 
275 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       276 
276 
     | 
    
         
             
            summary: IdentityCache lets you specify how you want to cache your model objects,
         
     | 
| 
       277 
277 
     | 
    
         
             
              at the model level, and adds a number of convenience methods for accessing those
         
     | 
| 
       278 
278 
     | 
    
         
             
              objects through the cache. Memcached is used as the backend cache store, and the
         
     | 
| 
       279 
279 
     | 
    
         
             
              database is only hit when a copy of the object cannot be found in Memcached.
         
     | 
| 
       280 
     | 
    
         
            -
            test_files:
         
     | 
| 
       281 
     | 
    
         
            -
            - test/attribute_cache_test.rb
         
     | 
| 
       282 
     | 
    
         
            -
            - test/cache_fetch_includes_test.rb
         
     | 
| 
       283 
     | 
    
         
            -
            - test/cache_hash_test.rb
         
     | 
| 
       284 
     | 
    
         
            -
            - test/cache_invalidation_test.rb
         
     | 
| 
       285 
     | 
    
         
            -
            - test/deeply_nested_associated_record_test.rb
         
     | 
| 
       286 
     | 
    
         
            -
            - test/denormalized_has_many_test.rb
         
     | 
| 
       287 
     | 
    
         
            -
            - test/denormalized_has_one_test.rb
         
     | 
| 
       288 
     | 
    
         
            -
            - test/fetch_multi_test.rb
         
     | 
| 
       289 
     | 
    
         
            -
            - test/fetch_test.rb
         
     | 
| 
       290 
     | 
    
         
            -
            - test/fixtures/serialized_record.mysql2
         
     | 
| 
       291 
     | 
    
         
            -
            - test/fixtures/serialized_record.postgresql
         
     | 
| 
       292 
     | 
    
         
            -
            - test/helpers/active_record_objects.rb
         
     | 
| 
       293 
     | 
    
         
            -
            - test/helpers/database_connection.rb
         
     | 
| 
       294 
     | 
    
         
            -
            - test/helpers/serialization_format.rb
         
     | 
| 
       295 
     | 
    
         
            -
            - test/helpers/update_serialization_format.rb
         
     | 
| 
       296 
     | 
    
         
            -
            - test/identity_cache_test.rb
         
     | 
| 
       297 
     | 
    
         
            -
            - test/index_cache_test.rb
         
     | 
| 
       298 
     | 
    
         
            -
            - test/memoized_attributes_test.rb
         
     | 
| 
       299 
     | 
    
         
            -
            - test/memoized_cache_proxy_test.rb
         
     | 
| 
       300 
     | 
    
         
            -
            - test/normalized_belongs_to_test.rb
         
     | 
| 
       301 
     | 
    
         
            -
            - test/normalized_has_many_test.rb
         
     | 
| 
       302 
     | 
    
         
            -
            - test/normalized_has_one_test.rb
         
     | 
| 
       303 
     | 
    
         
            -
            - test/prefetch_associations_test.rb
         
     | 
| 
       304 
     | 
    
         
            -
            - test/readonly_test.rb
         
     | 
| 
       305 
     | 
    
         
            -
            - test/recursive_denormalized_has_many_test.rb
         
     | 
| 
       306 
     | 
    
         
            -
            - test/save_test.rb
         
     | 
| 
       307 
     | 
    
         
            -
            - test/schema_change_test.rb
         
     | 
| 
       308 
     | 
    
         
            -
            - test/serialization_format_change_test.rb
         
     | 
| 
       309 
     | 
    
         
            -
            - test/test_helper.rb
         
     | 
| 
      
 280 
     | 
    
         
            +
            test_files: []
         
     |