identity_cache 1.1.0 → 1.2.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 +4 -4
- data/.github/workflows/ci.yml +5 -4
- data/.github/workflows/cla.yml +22 -0
- data/.rubocop.yml +7 -3
- data/.spin/bootstrap +7 -0
- data/.spin/svc.yml +2 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +15 -5
- data/LICENSE +1 -1
- data/README.md +5 -6
- data/Rakefile +13 -12
- data/dev.yml +2 -4
- data/gemfiles/Gemfile.latest-release +12 -7
- data/gemfiles/Gemfile.min-supported +11 -6
- data/gemfiles/Gemfile.rails-edge +8 -5
- data/identity_cache.gemspec +15 -27
- data/{railgun.yml → isogun.yml} +0 -5
- data/lib/identity_cache/belongs_to_caching.rb +1 -0
- data/lib/identity_cache/cache_fetcher.rb +241 -16
- data/lib/identity_cache/cache_hash.rb +7 -6
- data/lib/identity_cache/cache_invalidation.rb +1 -0
- data/lib/identity_cache/cache_key_generation.rb +22 -19
- data/lib/identity_cache/cache_key_loader.rb +2 -2
- data/lib/identity_cache/cached/association.rb +2 -4
- data/lib/identity_cache/cached/attribute.rb +3 -3
- data/lib/identity_cache/cached/attribute_by_multi.rb +1 -1
- data/lib/identity_cache/cached/belongs_to.rb +3 -0
- data/lib/identity_cache/cached/embedded_fetching.rb +2 -0
- data/lib/identity_cache/cached/primary_index.rb +3 -2
- data/lib/identity_cache/cached/recursive/association.rb +2 -0
- data/lib/identity_cache/cached/recursive/has_many.rb +1 -0
- data/lib/identity_cache/cached/recursive/has_one.rb +1 -0
- data/lib/identity_cache/cached/reference/association.rb +1 -0
- data/lib/identity_cache/cached/reference/has_many.rb +1 -0
- data/lib/identity_cache/cached/reference/has_one.rb +1 -0
- data/lib/identity_cache/cached.rb +1 -0
- data/lib/identity_cache/configuration_dsl.rb +1 -0
- data/lib/identity_cache/encoder.rb +2 -1
- data/lib/identity_cache/expiry_hook.rb +1 -0
- data/lib/identity_cache/fallback_fetcher.rb +6 -1
- data/lib/identity_cache/mem_cache_store_cas.rb +15 -5
- data/lib/identity_cache/memoized_cache_proxy.rb +15 -15
- data/lib/identity_cache/parent_model_expiration.rb +3 -1
- data/lib/identity_cache/query_api.rb +3 -0
- data/lib/identity_cache/railtie.rb +1 -0
- data/lib/identity_cache/should_use_cache.rb +1 -0
- data/lib/identity_cache/version.rb +2 -1
- data/lib/identity_cache/with_primary_index.rb +37 -10
- data/lib/identity_cache/without_primary_index.rb +7 -3
- data/lib/identity_cache.rb +38 -24
- data/performance/cache_runner.rb +12 -9
- data/performance/cpu.rb +6 -5
- data/performance/externals.rb +6 -5
- data/performance/profile.rb +7 -6
- metadata +27 -123
- data/.github/probots.yml +0 -2
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require "monitor"
|
4
|
+
require "benchmark"
|
4
5
|
|
5
6
|
module IdentityCache
|
6
7
|
class MemoizedCacheProxy
|
@@ -12,7 +13,7 @@ module IdentityCache
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def cache_backend=(cache_adaptor)
|
15
|
-
if cache_adaptor.class.name ==
|
16
|
+
if cache_adaptor.class.name == "ActiveSupport::Cache::MemCacheStore"
|
16
17
|
if cache_adaptor.respond_to?(:cas) || cache_adaptor.respond_to?(:cas_multi)
|
17
18
|
unless cache_adaptor.is_a?(MemCacheStoreCAS)
|
18
19
|
raise "#{cache_adaptor} respond to :cas or :cas_multi, that's unexpected"
|
@@ -30,7 +31,7 @@ module IdentityCache
|
|
30
31
|
# no need for CAS support
|
31
32
|
else
|
32
33
|
warn("[IdentityCache] Missing CAS support in cache backend #{cache_adaptor.class} "\
|
33
|
-
|
34
|
+
"which is needed for cache consistency")
|
34
35
|
end
|
35
36
|
@cache_fetcher = FallbackFetcher.new(cache_adaptor)
|
36
37
|
end
|
@@ -50,7 +51,7 @@ module IdentityCache
|
|
50
51
|
|
51
52
|
def write(key, value)
|
52
53
|
memoizing = memoizing?
|
53
|
-
ActiveSupport::Notifications.instrument(
|
54
|
+
ActiveSupport::Notifications.instrument("cache_write.identity_cache", memoizing: memoizing) do
|
54
55
|
memoized_key_values[key] = value if memoizing
|
55
56
|
@cache_fetcher.write(key, value)
|
56
57
|
end
|
@@ -58,7 +59,7 @@ module IdentityCache
|
|
58
59
|
|
59
60
|
def delete(key)
|
60
61
|
memoizing = memoizing?
|
61
|
-
ActiveSupport::Notifications.instrument(
|
62
|
+
ActiveSupport::Notifications.instrument("cache_delete.identity_cache", memoizing: memoizing) do
|
62
63
|
memoized_key_values.delete(key) if memoizing
|
63
64
|
if (result = @cache_fetcher.delete(key))
|
64
65
|
IdentityCache.logger.debug { "[IdentityCache] delete recorded for #{key}" }
|
@@ -69,20 +70,18 @@ module IdentityCache
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
72
|
-
def fetch(key)
|
73
|
+
def fetch(key, cache_fetcher_options = {}, &block)
|
73
74
|
memo_misses = 0
|
74
75
|
cache_misses = 0
|
75
76
|
|
76
|
-
value = ActiveSupport::Notifications.instrument(
|
77
|
+
value = ActiveSupport::Notifications.instrument("cache_fetch.identity_cache") do |payload|
|
77
78
|
payload[:resolve_miss_time] = 0.0
|
78
79
|
|
79
80
|
value = fetch_memoized(key) do
|
80
81
|
memo_misses = 1
|
81
|
-
@cache_fetcher.fetch(key) do
|
82
|
+
@cache_fetcher.fetch(key, **cache_fetcher_options) do
|
82
83
|
cache_misses = 1
|
83
|
-
instrument_duration(payload, :resolve_miss_time)
|
84
|
-
yield
|
85
|
-
end
|
84
|
+
instrument_duration(payload, :resolve_miss_time, &block)
|
86
85
|
end
|
87
86
|
end
|
88
87
|
set_instrumentation_payload(payload, num_keys: 1, memo_misses: memo_misses, cache_misses: cache_misses)
|
@@ -93,7 +92,7 @@ module IdentityCache
|
|
93
92
|
IdentityCache.logger.debug { "[IdentityCache] cache miss for #{key}" }
|
94
93
|
else
|
95
94
|
IdentityCache.logger.debug do
|
96
|
-
"[IdentityCache] #{memo_misses > 0 ?
|
95
|
+
"[IdentityCache] #{memo_misses > 0 ? "(cache_backend)" : "(memoized)"} cache hit for #{key}"
|
97
96
|
end
|
98
97
|
end
|
99
98
|
|
@@ -104,7 +103,7 @@ module IdentityCache
|
|
104
103
|
memo_miss_keys = EMPTY_ARRAY
|
105
104
|
cache_miss_keys = EMPTY_ARRAY
|
106
105
|
|
107
|
-
result = ActiveSupport::Notifications.instrument(
|
106
|
+
result = ActiveSupport::Notifications.instrument("cache_fetch_multi.identity_cache") do |payload|
|
108
107
|
payload[:resolve_miss_time] = 0.0
|
109
108
|
|
110
109
|
result = fetch_multi_memoized(keys) do |non_memoized_keys|
|
@@ -132,7 +131,7 @@ module IdentityCache
|
|
132
131
|
end
|
133
132
|
|
134
133
|
def clear
|
135
|
-
ActiveSupport::Notifications.instrument(
|
134
|
+
ActiveSupport::Notifications.instrument("cache_clear.identity_cache") do
|
136
135
|
clear_memoization
|
137
136
|
@cache_fetcher.clear
|
138
137
|
end
|
@@ -155,6 +154,7 @@ module IdentityCache
|
|
155
154
|
if memoized_key_values.key?(key)
|
156
155
|
return memoized_key_values[key]
|
157
156
|
end
|
157
|
+
|
158
158
|
memoized_key_values[key] = yield
|
159
159
|
end
|
160
160
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module IdentityCache
|
3
4
|
module ParentModelExpiration # :nodoc:
|
4
5
|
extend ActiveSupport::Concern
|
@@ -22,6 +23,7 @@ module IdentityCache
|
|
22
23
|
|
23
24
|
def install_pending_parent_expiry_hooks(model)
|
24
25
|
return if lazy_hooks.empty?
|
26
|
+
|
25
27
|
name = model.name.demodulize
|
26
28
|
if (hooks = lazy_hooks.delete(name))
|
27
29
|
hooks.each(&:install)
|
@@ -85,7 +87,7 @@ module IdentityCache
|
|
85
87
|
|
86
88
|
cached_associations.each do |parent_class, only_on_foreign_key_change|
|
87
89
|
if new_parent&.is_a?(parent_class) &&
|
88
|
-
|
90
|
+
should_expire_identity_cache_parent?(foreign_key, only_on_foreign_key_change)
|
89
91
|
add_record_to_cache_expiry_set(parents_to_expire, new_parent)
|
90
92
|
end
|
91
93
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module IdentityCache
|
3
4
|
module QueryAPI
|
4
5
|
extend ActiveSupport::Concern
|
@@ -68,6 +69,7 @@ module IdentityCache
|
|
68
69
|
def setup_embedded_associations_on_miss(records,
|
69
70
|
readonly: IdentityCache.fetch_read_only_records && should_use_cache?)
|
70
71
|
return if records.empty?
|
72
|
+
|
71
73
|
records.each(&:readonly!) if readonly
|
72
74
|
each_id_embedded_association do |cached_association|
|
73
75
|
preload_id_embedded_association(records, cached_association)
|
@@ -86,6 +88,7 @@ module IdentityCache
|
|
86
88
|
association.reset
|
87
89
|
# reset inverse associations
|
88
90
|
next unless target && association_reflection.has_inverse?
|
91
|
+
|
89
92
|
inverse_name = association_reflection.inverse_of.name
|
90
93
|
if target.is_a?(Array)
|
91
94
|
target.each { |child_record| child_record.association(inverse_name).reset }
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module IdentityCache
|
3
4
|
module WithPrimaryIndex
|
4
5
|
extend ActiveSupport::Concern
|
@@ -57,7 +58,7 @@ module IdentityCache
|
|
57
58
|
cache_attribute_by_alias(attribute_proc, alias_name: :id, by: fields, unique: unique)
|
58
59
|
|
59
60
|
field_list = fields.join("_and_")
|
60
|
-
arg_list = (0...fields.size).collect { |i| "arg#{i}" }.join(
|
61
|
+
arg_list = (0...fields.size).collect { |i| "arg#{i}" }.join(",")
|
61
62
|
|
62
63
|
if unique
|
63
64
|
instance_eval(<<-CODE, __FILE__, __LINE__ + 1)
|
@@ -97,21 +98,47 @@ module IdentityCache
|
|
97
98
|
!!fetch_by_id(id)
|
98
99
|
end
|
99
100
|
|
100
|
-
#
|
101
|
-
#
|
102
|
-
|
101
|
+
# Fetch the record by its primary key from the cache or read from
|
102
|
+
# the database and fill the cache on a cache miss. This behaves like
|
103
|
+
# `where(id: id).readonly.first` being called on the model.
|
104
|
+
#
|
105
|
+
# @param id Primary key value for the record to fetch.
|
106
|
+
# @param includes [Hash|Array|Symbol] Cached associations to prefetch from
|
107
|
+
# the cache on the returned record
|
108
|
+
# @param fill_lock_duration [Float] If provided, take a fill lock around cache fills
|
109
|
+
# and wait for this duration for cache to be filled when reading a lock provided
|
110
|
+
# by another client. Defaults to not setting the fill lock and trying to fill the
|
111
|
+
# cache from the database regardless of the presence of another client's fill lock.
|
112
|
+
# Set this to just above the typical amount of time it takes to do a cache fill.
|
113
|
+
# @param lock_wait_tries [Integer] Only applicable if fill_lock_duration is provided,
|
114
|
+
# in which case it specifies the number of times to do a lock wait. After the first
|
115
|
+
# lock wait it will try to take the lock, so will only do following lock waits due
|
116
|
+
# to another client taking the lock first. If another lock wait would be needed after
|
117
|
+
# reaching the limit, then a `LockWaitTimeout` exception is raised. Default is 2. Use
|
118
|
+
# this to control the maximum total lock wait duration
|
119
|
+
# (`lock_wait_tries * fill_lock_duration`).
|
120
|
+
# @raise [LockWaitTimeout] Timeout after waiting `lock_wait_tries * fill_lock_duration`
|
121
|
+
# seconds for `lock_wait_tries` other clients to fill the cache.
|
122
|
+
# @return [self|nil] An instance of this model for the record with the specified id or
|
123
|
+
# `nil` if no record with this `id` exists in the database
|
124
|
+
def fetch_by_id(id, includes: nil, **cache_fetcher_options)
|
103
125
|
ensure_base_model
|
104
126
|
raise_if_scoped
|
105
|
-
record = cached_primary_index.fetch(id)
|
127
|
+
record = cached_primary_index.fetch(id, cache_fetcher_options)
|
106
128
|
prefetch_associations(includes, [record]) if record && includes
|
107
129
|
record
|
108
130
|
end
|
109
131
|
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
|
114
|
-
|
132
|
+
# Fetch the record by its primary key from the cache or read from
|
133
|
+
# the database and fill the cache on a cache miss. This behaves like
|
134
|
+
# `readonly.find(id)` being called on the model.
|
135
|
+
#
|
136
|
+
# @param (see #fetch_by_id)
|
137
|
+
# @raise (see #fetch_by_id)
|
138
|
+
# @raise [ActiveRecord::RecordNotFound] if the record isn't found
|
139
|
+
# @return [self] An instance of this model for the record with the specified id
|
140
|
+
def fetch(id, **options)
|
141
|
+
fetch_by_id(id, **options) || raise(
|
115
142
|
IdentityCache::RecordNotFound, "Couldn't find #{name} with ID=#{id}"
|
116
143
|
)
|
117
144
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module IdentityCache
|
3
4
|
module WithoutPrimaryIndex
|
4
5
|
extend ActiveSupport::Concern
|
@@ -12,9 +13,12 @@ module IdentityCache
|
|
12
13
|
include IdentityCache::ShouldUseCache
|
13
14
|
include ParentModelExpiration
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
class << self
|
17
|
+
def append_features(base) # :nodoc:
|
18
|
+
raise AlreadyIncludedError if base.include?(WithoutPrimaryIndex)
|
19
|
+
|
20
|
+
super
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
included do
|
data/lib/identity_cache.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "active_support/core_ext/module/attribute_accessors"
|
5
|
+
require "ar_transaction_changes"
|
5
6
|
|
6
7
|
require "identity_cache/version"
|
7
8
|
require "identity_cache/record_not_found"
|
@@ -27,24 +28,24 @@ require "identity_cache/cached/reference/association"
|
|
27
28
|
require "identity_cache/cached/reference/has_one"
|
28
29
|
require "identity_cache/cached/reference/has_many"
|
29
30
|
require "identity_cache/expiry_hook"
|
30
|
-
require
|
31
|
-
require
|
32
|
-
require
|
33
|
-
require
|
34
|
-
require
|
35
|
-
require
|
36
|
-
require
|
31
|
+
require "identity_cache/memoized_cache_proxy"
|
32
|
+
require "identity_cache/belongs_to_caching"
|
33
|
+
require "identity_cache/cache_key_generation"
|
34
|
+
require "identity_cache/configuration_dsl"
|
35
|
+
require "identity_cache/should_use_cache"
|
36
|
+
require "identity_cache/parent_model_expiration"
|
37
|
+
require "identity_cache/query_api"
|
37
38
|
require "identity_cache/cache_hash"
|
38
39
|
require "identity_cache/cache_invalidation"
|
39
40
|
require "identity_cache/cache_fetcher"
|
40
41
|
require "identity_cache/fallback_fetcher"
|
41
|
-
require
|
42
|
-
require
|
42
|
+
require "identity_cache/without_primary_index"
|
43
|
+
require "identity_cache/with_primary_index"
|
43
44
|
|
44
45
|
module IdentityCache
|
45
46
|
extend ActiveSupport::Concern
|
46
47
|
|
47
|
-
autoload :MemCacheStoreCAS,
|
48
|
+
autoload :MemCacheStoreCAS, "identity_cache/mem_cache_store_cas"
|
48
49
|
|
49
50
|
include WithPrimaryIndex
|
50
51
|
|
@@ -65,23 +66,26 @@ module IdentityCache
|
|
65
66
|
|
66
67
|
class DerivedModelError < StandardError; end
|
67
68
|
|
69
|
+
class LockWaitTimeout < StandardError; end
|
70
|
+
|
68
71
|
mattr_accessor :cache_namespace
|
69
72
|
self.cache_namespace = "IDC:#{CACHE_VERSION}:"
|
70
73
|
|
71
74
|
# Fetched records are not read-only and this could sometimes prevent IDC from
|
72
75
|
# reflecting what's truly in the database when fetch_read_only_records is false.
|
73
76
|
# When set to true, it will only return read-only records when cache is used.
|
74
|
-
|
75
|
-
self.fetch_read_only_records = true
|
77
|
+
@fetch_read_only_records = true
|
76
78
|
|
77
79
|
class << self
|
78
80
|
include IdentityCache::CacheHash
|
79
81
|
|
82
|
+
attr_writer :fetch_read_only_records
|
80
83
|
attr_accessor :readonly
|
81
84
|
attr_writer :logger
|
82
85
|
|
83
|
-
def append_features(base)
|
86
|
+
def append_features(base) # :nodoc:
|
84
87
|
raise AlreadyIncludedError if base.include?(IdentityCache)
|
88
|
+
|
85
89
|
super
|
86
90
|
end
|
87
91
|
|
@@ -141,10 +145,13 @@ module IdentityCache
|
|
141
145
|
#
|
142
146
|
# == Parameters
|
143
147
|
# +key+ A cache key string
|
148
|
+
# +cache_fetcher_options+ A hash of options to pass to the cache backend
|
144
149
|
#
|
145
|
-
def fetch(key)
|
150
|
+
def fetch(key, cache_fetcher_options = {})
|
146
151
|
if should_use_cache?
|
147
|
-
unmap_cached_nil_for(cache.fetch(key
|
152
|
+
unmap_cached_nil_for(cache.fetch(key, cache_fetcher_options) do
|
153
|
+
map_cached_nil_for(yield)
|
154
|
+
end)
|
148
155
|
else
|
149
156
|
yield
|
150
157
|
end
|
@@ -185,11 +192,18 @@ module IdentityCache
|
|
185
192
|
end
|
186
193
|
|
187
194
|
def with_fetch_read_only_records(value = true)
|
188
|
-
old_value =
|
189
|
-
|
195
|
+
old_value = Thread.current[:identity_cache_fetch_read_only_records]
|
196
|
+
Thread.current[:identity_cache_fetch_read_only_records] = value
|
190
197
|
yield
|
191
198
|
ensure
|
192
|
-
|
199
|
+
Thread.current[:identity_cache_fetch_read_only_records] = old_value
|
200
|
+
end
|
201
|
+
|
202
|
+
def fetch_read_only_records
|
203
|
+
v = Thread.current[:identity_cache_fetch_read_only_records]
|
204
|
+
return v unless v.nil?
|
205
|
+
|
206
|
+
@fetch_read_only_records
|
193
207
|
end
|
194
208
|
|
195
209
|
def eager_load!
|
@@ -198,12 +212,12 @@ module IdentityCache
|
|
198
212
|
|
199
213
|
private
|
200
214
|
|
201
|
-
def fetch_in_batches(keys)
|
215
|
+
def fetch_in_batches(keys, &block)
|
202
216
|
keys.each_slice(BATCH_SIZE).each_with_object({}) do |slice, result|
|
203
|
-
result.merge!(cache.fetch_multi(*slice
|
217
|
+
result.merge!(cache.fetch_multi(*slice, &block))
|
204
218
|
end
|
205
219
|
end
|
206
220
|
end
|
207
221
|
end
|
208
222
|
|
209
|
-
require
|
223
|
+
require "identity_cache/railtie" if defined?(Rails)
|
data/performance/cache_runner.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
4
|
+
require "active_record"
|
5
|
+
require "active_support/core_ext"
|
6
|
+
require "active_support/cache"
|
7
|
+
require "identity_cache"
|
8
|
+
require "memcached_store"
|
9
|
+
require "active_support/cache/memcached_store"
|
9
10
|
|
10
|
-
require File.dirname(__FILE__) +
|
11
|
-
require File.dirname(__FILE__) +
|
12
|
-
require File.dirname(__FILE__) +
|
11
|
+
require File.dirname(__FILE__) + "/../test/helpers/active_record_objects"
|
12
|
+
require File.dirname(__FILE__) + "/../test/helpers/database_connection"
|
13
|
+
require File.dirname(__FILE__) + "/../test/helpers/cache_connection"
|
13
14
|
|
14
15
|
IdentityCache.logger = Logger.new(nil)
|
15
16
|
CacheConnection.setup
|
@@ -31,6 +32,7 @@ def create_database(count)
|
|
31
32
|
helper.setup_models
|
32
33
|
|
33
34
|
return if database_ready(count)
|
35
|
+
|
34
36
|
puts "Database not ready for performance testing, generating records"
|
35
37
|
|
36
38
|
DatabaseConnection.drop_tables
|
@@ -38,6 +40,7 @@ def create_database(count)
|
|
38
40
|
existing = Item.all
|
39
41
|
(1..count).to_a.each do |i|
|
40
42
|
next if existing.any? { |e| e.id == i }
|
43
|
+
|
41
44
|
a = Item.new
|
42
45
|
a.id = i
|
43
46
|
a.associated = AssociatedRecord.new(name: "Associated for #{i}")
|
data/performance/cpu.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'rubygems'
|
3
|
-
require 'benchmark'
|
4
2
|
|
5
|
-
|
3
|
+
require "rubygems"
|
4
|
+
require "benchmark"
|
5
|
+
|
6
|
+
require_relative "cache_runner"
|
6
7
|
|
7
8
|
RUNS = 400
|
8
9
|
|
@@ -32,9 +33,9 @@ def bmbm(runners)
|
|
32
33
|
label_width = runners.map { |r| r.name.size }.max + 2
|
33
34
|
width = label_width + Benchmark::CAPTION.size
|
34
35
|
|
35
|
-
puts
|
36
|
+
puts "Rehearsal: ".ljust(width, "-")
|
36
37
|
benchmark(runners, label_width)
|
37
|
-
puts
|
38
|
+
puts "-" * width
|
38
39
|
|
39
40
|
benchmark(runners, label_width)
|
40
41
|
end
|
data/performance/externals.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'rubygems'
|
3
|
-
require 'benchmark'
|
4
|
-
require 'ruby-prof'
|
5
2
|
|
6
|
-
|
3
|
+
require "rubygems"
|
4
|
+
require "benchmark"
|
5
|
+
require "ruby-prof"
|
6
|
+
|
7
|
+
require_relative "cache_runner"
|
7
8
|
|
8
9
|
RUNS = 1000
|
9
10
|
RubyProf.measure_mode = RubyProf::CPU_TIME
|
10
11
|
|
11
12
|
EXTERNALS = { "Memcache" => ["MemCache#set", "MemCache#get"],
|
12
|
-
"Database" => ["Mysql2::Client#query"] }
|
13
|
+
"Database" => ["Mysql2::Client#query"], }
|
13
14
|
|
14
15
|
def run(obj)
|
15
16
|
obj.prepare
|
data/performance/profile.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'rubygems'
|
3
|
-
require 'benchmark'
|
4
|
-
require 'stackprof'
|
5
2
|
|
6
|
-
|
3
|
+
require "rubygems"
|
4
|
+
require "benchmark"
|
5
|
+
require "stackprof"
|
6
|
+
|
7
|
+
require_relative "cache_runner"
|
7
8
|
|
8
9
|
RUNS = 1000
|
9
10
|
|
@@ -22,9 +23,9 @@ end
|
|
22
23
|
|
23
24
|
create_database(RUNS)
|
24
25
|
|
25
|
-
if (runner_name = ENV[
|
26
|
+
if (runner_name = ENV["RUNNER"])
|
26
27
|
if (runner = CACHE_RUNNERS.find { |r| r.name == runner_name })
|
27
|
-
run(runner.new(RUNS), filename: ENV[
|
28
|
+
run(runner.new(RUNS), filename: ENV["FILENAME"])
|
28
29
|
else
|
29
30
|
puts "Couldn't find cache runner #{runner_name.inspect}"
|
30
31
|
exit(1)
|