record-cache 0.1.2 → 0.1.3
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 +15 -0
- data/lib/record_cache.rb +2 -1
- data/lib/record_cache/base.rb +63 -22
- data/lib/record_cache/datastore/active_record.rb +5 -3
- data/lib/record_cache/datastore/active_record_30.rb +95 -38
- data/lib/record_cache/datastore/active_record_31.rb +157 -54
- data/lib/record_cache/datastore/active_record_32.rb +444 -0
- data/lib/record_cache/dispatcher.rb +47 -47
- data/lib/record_cache/multi_read.rb +14 -1
- data/lib/record_cache/query.rb +36 -25
- data/lib/record_cache/statistics.rb +5 -5
- data/lib/record_cache/strategy/base.rb +49 -19
- data/lib/record_cache/strategy/full_table_cache.rb +81 -0
- data/lib/record_cache/strategy/index_cache.rb +38 -36
- data/lib/record_cache/strategy/unique_index_cache.rb +130 -0
- data/lib/record_cache/strategy/util.rb +12 -12
- data/lib/record_cache/test/resettable_version_store.rb +2 -9
- data/lib/record_cache/version.rb +1 -1
- data/lib/record_cache/version_store.rb +23 -16
- data/spec/db/schema.rb +12 -0
- data/spec/db/seeds.rb +10 -0
- data/spec/lib/active_record/visitor_spec.rb +22 -0
- data/spec/lib/base_spec.rb +21 -0
- data/spec/lib/dispatcher_spec.rb +24 -46
- data/spec/lib/multi_read_spec.rb +6 -6
- data/spec/lib/query_spec.rb +43 -43
- data/spec/lib/statistics_spec.rb +28 -28
- data/spec/lib/strategy/base_spec.rb +98 -87
- data/spec/lib/strategy/full_table_cache_spec.rb +68 -0
- data/spec/lib/strategy/index_cache_spec.rb +112 -69
- data/spec/lib/strategy/query_cache_spec.rb +83 -0
- data/spec/lib/strategy/unique_index_on_id_cache_spec.rb +317 -0
- data/spec/lib/strategy/unique_index_on_string_cache_spec.rb +168 -0
- data/spec/lib/strategy/util_spec.rb +67 -49
- data/spec/lib/version_store_spec.rb +22 -41
- data/spec/models/address.rb +9 -0
- data/spec/models/apple.rb +1 -1
- data/spec/models/banana.rb +21 -2
- data/spec/models/language.rb +5 -0
- data/spec/models/person.rb +1 -1
- data/spec/models/store.rb +2 -1
- data/spec/spec_helper.rb +7 -4
- data/spec/support/after_commit.rb +2 -0
- data/spec/support/matchers/hit_cache_matcher.rb +10 -6
- data/spec/support/matchers/log.rb +45 -0
- data/spec/support/matchers/miss_cache_matcher.rb +10 -6
- data/spec/support/matchers/use_cache_matcher.rb +10 -6
- metadata +156 -161
- data/lib/record_cache/strategy/id_cache.rb +0 -93
- data/lib/record_cache/strategy/request_cache.rb +0 -49
- data/spec/lib/strategy/id_cache_spec.rb +0 -168
- data/spec/lib/strategy/request_cache_spec.rb +0 -85
@@ -6,6 +6,8 @@
|
|
6
6
|
#
|
7
7
|
# Important: Because of a bug in Dalli, read_multi is quite slow on some machines.
|
8
8
|
# @see https://github.com/mperham/dalli/issues/106
|
9
|
+
require "set"
|
10
|
+
|
9
11
|
module RecordCache
|
10
12
|
module MultiRead
|
11
13
|
@tested = Set.new
|
@@ -48,4 +50,15 @@ module RecordCache
|
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
51
|
-
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Exposes Dalli's +multi+ functionality in ActiveSupport::Cache implementation of Dalli
|
56
|
+
module ActiveSupport
|
57
|
+
module Cache
|
58
|
+
class DalliStore
|
59
|
+
def multi(&block)
|
60
|
+
dalli.multi(&block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end if defined?(::ActiveSupport::Cache::DalliStore)
|
data/lib/record_cache/query.rb
CHANGED
@@ -8,7 +8,7 @@ module RecordCache
|
|
8
8
|
@wheres = equality || {}
|
9
9
|
@sort_orders = []
|
10
10
|
@limit = nil
|
11
|
-
@
|
11
|
+
@where_values = {}
|
12
12
|
end
|
13
13
|
|
14
14
|
# Set equality of an attribute (usually found in where clause)
|
@@ -16,19 +16,23 @@ module RecordCache
|
|
16
16
|
@wheres[attribute.to_sym] = values if attribute
|
17
17
|
end
|
18
18
|
|
19
|
-
# Retrieve the
|
19
|
+
# Retrieve the values for the given attribute from the where statements
|
20
20
|
# Returns nil if no the attribute is not present
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
# @param attribute: the attribute name
|
22
|
+
# @param type: the type to be retrieved, :integer or :string (defaults to :integer)
|
23
|
+
def where_values(attribute, type = :integer)
|
24
|
+
return @where_values[attribute] if @where_values.key?(attribute)
|
25
|
+
@where_values[attribute] ||= array_of_values(@wheres[attribute], type)
|
24
26
|
end
|
25
27
|
|
26
|
-
# Retrieve the single
|
27
|
-
# Returns nil if
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
# Retrieve the single value for the given attribute from the where statements
|
29
|
+
# Returns nil if the attribute is not present, or if it contains multiple values
|
30
|
+
# @param attribute: the attribute name
|
31
|
+
# @param type: the type to be retrieved, :integer or :string (defaults to :integer)
|
32
|
+
def where_value(attribute, type = :integer)
|
33
|
+
values = where_values(attribute, type)
|
34
|
+
return nil unless values && values.size == 1
|
35
|
+
values.first
|
32
36
|
end
|
33
37
|
|
34
38
|
# Add a sort order to the query
|
@@ -44,17 +48,18 @@ module RecordCache
|
|
44
48
|
@limit = limit.to_i
|
45
49
|
end
|
46
50
|
|
47
|
-
# retrieve a unique key for this Query (used in RequestCache)
|
51
|
+
# DEPRECATED: retrieve a unique key for this Query (used in RequestCache)
|
48
52
|
def cache_key
|
49
53
|
@cache_key ||= generate_key
|
50
54
|
end
|
51
55
|
|
56
|
+
# DEPRECATED
|
52
57
|
def to_s
|
53
58
|
s = "SELECT "
|
54
59
|
s << @wheres.map{|k,v| "#{k} = #{v.inspect}"}.join(" AND ")
|
55
|
-
if
|
56
|
-
|
57
|
-
s << " ORDER_BY #{
|
60
|
+
if sorted?
|
61
|
+
order_by_clause = @sort_orders.map{|attr,asc| "#{attr} #{asc ? 'ASC' : 'DESC'}"}.join(', ')
|
62
|
+
s << " ORDER_BY #{order_by_clause}"
|
58
63
|
end
|
59
64
|
s << " LIMIT #{@limit}" if @limit
|
60
65
|
s
|
@@ -63,20 +68,26 @@ module RecordCache
|
|
63
68
|
private
|
64
69
|
|
65
70
|
def generate_key
|
66
|
-
key =
|
67
|
-
if @
|
68
|
-
|
69
|
-
|
71
|
+
key = ""
|
72
|
+
key << @limit.to_s if @limit
|
73
|
+
key << @sort_orders.map{|attr,asc| "#{asc ? '+' : '-'}#{attr}"}.join if sorted?
|
74
|
+
if @wheres.any?
|
75
|
+
key << "?"
|
76
|
+
key << @wheres.map{|k,v| "#{k}=#{v.inspect}"}.join("&")
|
70
77
|
end
|
71
|
-
key << "L#{@limit}" if @limit
|
72
78
|
key
|
73
79
|
end
|
74
|
-
|
75
|
-
def
|
80
|
+
|
81
|
+
def array_of_values(values, type)
|
76
82
|
return nil unless values
|
77
|
-
values =
|
78
|
-
values
|
79
|
-
|
83
|
+
values = values.is_a?(Array) ? values.dup : [values]
|
84
|
+
values.compact!
|
85
|
+
if type == :integer
|
86
|
+
values = values.map{|value| value.to_i} unless values.first.is_a?(Fixnum)
|
87
|
+
return nil unless values.all?{ |value| value && value > 0 } # all values must be positive integers
|
88
|
+
elsif type == :string
|
89
|
+
values = values.map{|value| value.to_s} unless values.first.is_a?(String)
|
90
|
+
end
|
80
91
|
values
|
81
92
|
end
|
82
93
|
|
@@ -32,13 +32,13 @@ module RecordCache
|
|
32
32
|
stats.each{ |s| s.reset! }
|
33
33
|
end
|
34
34
|
|
35
|
-
# Retrieve the statistics for the given base and
|
36
|
-
# Returns a hash {<
|
37
|
-
# Returns a hash of hashes { <model_name> => {<
|
38
|
-
def find(base = nil,
|
35
|
+
# Retrieve the statistics for the given base and attribute
|
36
|
+
# Returns a hash {<attribute> => <statistics} for a model if no strategy is provided
|
37
|
+
# Returns a hash of hashes { <model_name> => {<attribute> => <statistics} } if no parameter is provided
|
38
|
+
def find(base = nil, attribute = nil)
|
39
39
|
stats = (@stats ||= {})
|
40
40
|
stats = (stats[base.name] ||= {}) if base
|
41
|
-
stats = (stats[
|
41
|
+
stats = (stats[attribute] ||= Counter.new) if attribute
|
42
42
|
stats
|
43
43
|
end
|
44
44
|
end
|
@@ -2,11 +2,23 @@ module RecordCache
|
|
2
2
|
module Strategy
|
3
3
|
class Base
|
4
4
|
|
5
|
-
|
5
|
+
# Parse the options and return (an array of) instances of this strategy.
|
6
|
+
def self.parse(base, record_store, options)
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(base, attribute, record_store, options)
|
6
11
|
@base = base
|
7
|
-
@
|
8
|
-
@record_store = record_store
|
9
|
-
@cache_key_prefix = "rc/#{options[:key] || @base.name}/"
|
12
|
+
@attribute = attribute
|
13
|
+
@record_store = with_multi_support(record_store)
|
14
|
+
@cache_key_prefix = "rc/#{options[:key] || @base.name}/"
|
15
|
+
@version_opts = options[:ttl] ? { :ttl => options[:ttl] } : {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Retrieve the +attribute+ for this strategy (unique per model).
|
19
|
+
# May be a non-existing attribute in case a cache is not based on a single attribute.
|
20
|
+
def attribute
|
21
|
+
@attribute
|
10
22
|
end
|
11
23
|
|
12
24
|
# Fetch all records and sort and filter locally
|
@@ -14,57 +26,75 @@ module RecordCache
|
|
14
26
|
records = fetch_records(query)
|
15
27
|
Util.filter!(records, query.wheres) if query.wheres.size > 0
|
16
28
|
Util.sort!(records, query.sort_orders) if query.sorted?
|
29
|
+
records = records[0..query.limit-1] if query.limit
|
17
30
|
records
|
18
31
|
end
|
19
32
|
|
20
|
-
#
|
21
|
-
def
|
33
|
+
# Can the cache retrieve the records based on this query?
|
34
|
+
def cacheable?(query)
|
22
35
|
raise NotImplementedError
|
23
36
|
end
|
24
37
|
|
25
|
-
#
|
26
|
-
def
|
38
|
+
# Handle create/update/destroy (use record.previous_changes to find the old values in case of an update)
|
39
|
+
def record_change(record, action)
|
27
40
|
raise NotImplementedError
|
28
41
|
end
|
29
42
|
|
30
43
|
# Handle invalidation call
|
31
44
|
def invalidate(id)
|
32
|
-
|
45
|
+
version_store.renew(cache_key(id), version_opts)
|
33
46
|
end
|
34
47
|
|
35
48
|
protected
|
36
|
-
|
49
|
+
|
37
50
|
def fetch_records(query)
|
38
51
|
raise NotImplementedError
|
39
52
|
end
|
40
|
-
|
53
|
+
|
41
54
|
# ------------------------- Utility methods ----------------------------
|
42
|
-
|
55
|
+
|
43
56
|
# retrieve the version store (unique store for the whole application)
|
44
57
|
def version_store
|
45
58
|
RecordCache::Base.version_store
|
46
59
|
end
|
47
|
-
|
60
|
+
|
61
|
+
# should be used when calling version_store.renew(..., version_opts)
|
62
|
+
def version_opts
|
63
|
+
@version_opts
|
64
|
+
end
|
65
|
+
|
48
66
|
# retrieve the record store (store for records for this cache strategy)
|
49
67
|
def record_store
|
50
68
|
@record_store
|
51
69
|
end
|
52
|
-
|
70
|
+
|
53
71
|
# find the statistics for this cache strategy
|
54
72
|
def statistics
|
55
|
-
@statistics ||= RecordCache::Statistics.find(@base, @
|
73
|
+
@statistics ||= RecordCache::Statistics.find(@base, @attribute)
|
56
74
|
end
|
57
75
|
|
58
76
|
# retrieve the cache key for the given id, e.g. rc/person/14
|
59
77
|
def cache_key(id)
|
60
|
-
"#{@cache_key_prefix}#{id}"
|
78
|
+
"#{@cache_key_prefix}#{id}"
|
61
79
|
end
|
62
|
-
|
80
|
+
|
63
81
|
# retrieve the versioned record key, e.g. rc/person/14v1
|
64
82
|
def versioned_key(cache_key, version)
|
65
|
-
"#{cache_key}v#{version.to_s}"
|
83
|
+
"#{cache_key}v#{version.to_s}"
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# add default implementation for multi support to perform multiple cache calls in a pipelined fashion
|
89
|
+
def with_multi_support(cache_store)
|
90
|
+
unless cache_store.respond_to?(:multi)
|
91
|
+
cache_store.send(:define_singleton_method, :multi) do |&block|
|
92
|
+
block.call
|
93
|
+
end
|
94
|
+
end
|
95
|
+
cache_store
|
66
96
|
end
|
67
|
-
|
97
|
+
|
68
98
|
end
|
69
99
|
end
|
70
100
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module RecordCache
|
2
|
+
module Strategy
|
3
|
+
class FullTableCache < Base
|
4
|
+
FULL_TABLE = 'full-table'
|
5
|
+
|
6
|
+
# parse the options and return (an array of) instances of this strategy
|
7
|
+
def self.parse(base, record_store, options)
|
8
|
+
return nil unless options[:full_table]
|
9
|
+
return nil unless base.table_exists?
|
10
|
+
|
11
|
+
FullTableCache.new(base, :full_table, record_store, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Can the cache retrieve the records based on this query?
|
15
|
+
def cacheable?(query)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
# Clear the cache on any record change
|
20
|
+
def record_change(record, action)
|
21
|
+
version_store.delete(cache_key(FULL_TABLE))
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# retrieve the record(s) with the given id(s) as an array
|
27
|
+
def fetch_records(query)
|
28
|
+
key = cache_key(FULL_TABLE)
|
29
|
+
# retrieve the current version of the records
|
30
|
+
current_version = version_store.current(key)
|
31
|
+
# get the records from the cache if there is a current version
|
32
|
+
records = current_version ? from_cache(key, current_version) : nil
|
33
|
+
# logging (only in debug mode!) and statistics
|
34
|
+
log_full_table_cache_hit(key, records) if RecordCache::Base.logger.debug?
|
35
|
+
statistics.add(1, records ? 1 : 0) if statistics.active?
|
36
|
+
# no records found?
|
37
|
+
unless records
|
38
|
+
# renew the version in case the version was not known
|
39
|
+
current_version ||= version_store.renew(key, version_opts)
|
40
|
+
# retrieve all records from the DB
|
41
|
+
records = from_db(key, current_version)
|
42
|
+
end
|
43
|
+
# return the array
|
44
|
+
records
|
45
|
+
end
|
46
|
+
|
47
|
+
def cache_key(id)
|
48
|
+
super(FULL_TABLE)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# ---------------------------- Querying ------------------------------------
|
54
|
+
|
55
|
+
# retrieve the records from the cache with the given keys
|
56
|
+
def from_cache(key, version)
|
57
|
+
records = record_store.read(versioned_key(key, version))
|
58
|
+
records.map{ |record| Util.deserialize(record) } if records
|
59
|
+
end
|
60
|
+
|
61
|
+
# retrieve the records with the given ids from the database
|
62
|
+
def from_db(key, version)
|
63
|
+
RecordCache::Base.without_record_cache do
|
64
|
+
# retrieve the records from the database
|
65
|
+
records = @base.all.to_a
|
66
|
+
# write all records to the cache
|
67
|
+
record_store.write(versioned_key(key, version), records.map{ |record| Util.serialize(record) })
|
68
|
+
records
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# ------------------------- Utility methods ----------------------------
|
73
|
+
|
74
|
+
# log cache hit/miss to debug log
|
75
|
+
def log_full_table_cache_hit(key, records)
|
76
|
+
RecordCache::Base.logger.debug{ "FullTableCache #{records ? 'hit' : 'miss'} for model #{@base.name}" }
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -2,53 +2,59 @@ module RecordCache
|
|
2
2
|
module Strategy
|
3
3
|
class IndexCache < Base
|
4
4
|
|
5
|
-
|
5
|
+
# parse the options and return (an array of) instances of this strategy
|
6
|
+
def self.parse(base, record_store, options)
|
7
|
+
return nil unless options[:index]
|
8
|
+
return nil unless base.table_exists?
|
9
|
+
|
10
|
+
raise "Index cache '#{options[:index].inspect}' on #{base.name} is redundant as index cache queries are handled by the full table cache." if options[:full_table]
|
11
|
+
raise ":index => #{options[:index].inspect} option cannot be used unless 'id' is present on #{base.name}" unless base.columns_hash['id']
|
12
|
+
[options[:index]].flatten.compact.map do |attribute|
|
13
|
+
type = base.columns_hash[attribute.to_s].try(:type)
|
14
|
+
raise "No column found for index '#{attribute}' on #{base.name}." unless type
|
15
|
+
raise "Incorrect type (expected integer, found #{type}) for index '#{attribute}' on #{base.name}." unless type == :integer
|
16
|
+
IndexCache.new(base, attribute, record_store, options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(base, attribute, record_store, options)
|
6
21
|
super
|
7
|
-
@
|
8
|
-
# check the index
|
9
|
-
type = @base.columns_hash[@index.to_s].try(:type)
|
10
|
-
raise "No column found for index '#{@index}' on #{@base.name}." unless type
|
11
|
-
raise "Incorrect type (expected integer, found #{type}) for index '#{@index}' on #{@base.name}." unless type == :integer
|
12
|
-
@index_cache_key_prefix = cache_key(@index) # "/rc/<model>/<index>"
|
22
|
+
@cache_key_prefix << "#{attribute}="
|
13
23
|
end
|
14
24
|
|
15
25
|
# Can the cache retrieve the records based on this query?
|
16
26
|
def cacheable?(query)
|
17
|
-
|
27
|
+
# allow limit of 1 for has_one
|
28
|
+
query.where_value(@attribute) && (query.limit.nil? || (query.limit == 1 && !query.sorted?))
|
18
29
|
end
|
19
30
|
|
20
31
|
# Handle create/update/destroy (use record.previous_changes to find the old values in case of an update)
|
21
32
|
def record_change(record, action)
|
22
33
|
if action == :destroy
|
23
|
-
remove_from_index(record.send(@
|
34
|
+
remove_from_index(record.send(@attribute), record.id)
|
24
35
|
elsif action == :create
|
25
|
-
add_to_index(record.send(@
|
36
|
+
add_to_index(record.send(@attribute), record.id)
|
26
37
|
else
|
27
|
-
index_change = record.previous_changes[@
|
38
|
+
index_change = record.previous_changes[@attribute.to_s] || record.previous_changes[@attribute]
|
28
39
|
return unless index_change
|
29
40
|
remove_from_index(index_change[0], record.id)
|
30
41
|
add_to_index(index_change[1], record.id)
|
31
42
|
end
|
32
43
|
end
|
33
44
|
|
34
|
-
# Explicitly invalidate the record cache for the given value
|
35
|
-
def invalidate(value)
|
36
|
-
version_store.increment(index_cache_key(value))
|
37
|
-
end
|
38
|
-
|
39
45
|
protected
|
40
46
|
|
41
47
|
# retrieve the record(s) based on the given query
|
42
48
|
def fetch_records(query)
|
43
|
-
value = query.
|
49
|
+
value = query.where_value(@attribute)
|
44
50
|
# make sure CacheCase.filter! does not see this where clause anymore
|
45
|
-
query.wheres.delete(@
|
51
|
+
query.wheres.delete(@attribute)
|
46
52
|
# retrieve the cache key for this index and value
|
47
|
-
key =
|
53
|
+
key = cache_key(value)
|
48
54
|
# retrieve the current version of the ids list
|
49
55
|
current_version = version_store.current(key)
|
50
56
|
# create the versioned key, renew the version in case it was missing in the version store
|
51
|
-
versioned_key = versioned_key(key, current_version || version_store.renew(key))
|
57
|
+
versioned_key = versioned_key(key, current_version || version_store.renew(key, version_opts))
|
52
58
|
# retrieve the ids from the local cache based on the current version from the version store
|
53
59
|
ids = current_version ? fetch_ids_from_cache(versioned_key) : nil
|
54
60
|
# logging (only in debug mode!) and statistics
|
@@ -59,16 +65,11 @@ module RecordCache
|
|
59
65
|
# use the IdCache to retrieve the records based on the ids
|
60
66
|
@base.record_cache[:id].send(:fetch_records, ::RecordCache::Query.new({:id => ids}))
|
61
67
|
end
|
62
|
-
|
68
|
+
|
63
69
|
private
|
64
70
|
|
65
71
|
# ---------------------------- Querying ------------------------------------
|
66
72
|
|
67
|
-
# key to retrieve the ids for a given value
|
68
|
-
def index_cache_key(value)
|
69
|
-
"#{@index_cache_key_prefix}=#{value}"
|
70
|
-
end
|
71
|
-
|
72
73
|
# Retrieve the ids from the local cache
|
73
74
|
def fetch_ids_from_cache(versioned_key)
|
74
75
|
record_store.read(versioned_key)
|
@@ -78,7 +79,7 @@ module RecordCache
|
|
78
79
|
def fetch_ids_from_db(versioned_key, value)
|
79
80
|
RecordCache::Base.without_record_cache do
|
80
81
|
# go straight to SQL result for optimal performance
|
81
|
-
sql = @base.select('id').where(@
|
82
|
+
sql = @base.select('id').where(@attribute => value).to_sql
|
82
83
|
ids = []; @base.connection.execute(sql).each{ |row| ids << (row.is_a?(Hash) ? row['id'] : row.first).to_i }
|
83
84
|
record_store.write(versioned_key, ids)
|
84
85
|
ids
|
@@ -89,25 +90,26 @@ module RecordCache
|
|
89
90
|
|
90
91
|
# add one record(id) to the index with the given value
|
91
92
|
def add_to_index(value, id)
|
92
|
-
|
93
|
+
renew_version(value.to_i) { |ids| ids << id } if value
|
93
94
|
end
|
94
95
|
|
95
96
|
# remove one record(id) from the index with the given value
|
96
97
|
def remove_from_index(value, id)
|
97
|
-
|
98
|
+
renew_version(value.to_i) { |ids| ids.delete(id) } if value
|
98
99
|
end
|
99
100
|
|
100
|
-
#
|
101
|
-
def
|
101
|
+
# renew the version store and update the local store
|
102
|
+
def renew_version(value, &block)
|
102
103
|
# retrieve local version and increment version store
|
103
|
-
key =
|
104
|
-
|
104
|
+
key = cache_key(value)
|
105
|
+
old_version = version_store.current(key)
|
106
|
+
new_version = version_store.renew(key, version_opts)
|
105
107
|
# try to update the ids list based on the last version
|
106
|
-
ids = fetch_ids_from_cache(versioned_key(key,
|
108
|
+
ids = fetch_ids_from_cache(versioned_key(key, old_version))
|
107
109
|
if ids
|
108
110
|
ids = Array.new(ids)
|
109
111
|
yield ids
|
110
|
-
record_store.write(versioned_key(key,
|
112
|
+
record_store.write(versioned_key(key, new_version), ids)
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
@@ -115,7 +117,7 @@ module RecordCache
|
|
115
117
|
|
116
118
|
# log cache hit/miss to debug log
|
117
119
|
def log_cache_hit(key, ids)
|
118
|
-
RecordCache::Base.logger.debug
|
120
|
+
RecordCache::Base.logger.debug{ "IndexCache #{ids ? 'hit' : 'miss'} for #{key}: found #{ids ? ids.size : 'no'} ids" }
|
119
121
|
end
|
120
122
|
end
|
121
123
|
end
|