ar_cache 1.2.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -16
- data/Gemfile.common +2 -0
- data/Gemfile.lock +89 -80
- data/README.md +4 -4
- data/README.zh-CN.md +165 -0
- data/ar_cache.gemspec +1 -0
- data/lib/ar_cache.rb +51 -18
- data/lib/ar_cache/active_record/associations/association.rb +1 -1
- data/lib/ar_cache/active_record/associations/has_one_through_association.rb +11 -9
- data/lib/ar_cache/active_record/associations/singular_association.rb +2 -6
- data/lib/ar_cache/active_record/connection_adapters/abstract/database_statements.rb +20 -15
- data/lib/ar_cache/active_record/connection_adapters/abstract/transaction.rb +37 -32
- data/lib/ar_cache/active_record/core.rb +5 -4
- data/lib/ar_cache/active_record/insert_all.rb +1 -4
- data/lib/ar_cache/active_record/model_schema.rb +1 -7
- data/lib/ar_cache/active_record/persistence.rb +4 -5
- data/lib/ar_cache/active_record/relation.rb +7 -8
- data/lib/ar_cache/configuration.rb +46 -41
- data/lib/ar_cache/marshal.rb +36 -26
- data/lib/ar_cache/mock_table.rb +4 -4
- data/lib/ar_cache/query.rb +20 -15
- data/lib/ar_cache/table.rb +44 -51
- data/lib/ar_cache/version.rb +1 -1
- data/lib/ar_cache/where_clause.rb +35 -26
- data/lib/generators/ar_cache/install_generator.rb +0 -7
- data/lib/generators/ar_cache/templates/configuration.rb +29 -29
- metadata +23 -5
- data/lib/ar_cache/record.rb +0 -52
- data/lib/ar_cache/store.rb +0 -38
- data/lib/generators/ar_cache/templates/migrate/create_ar_cache_records.rb.tt +0 -16
data/lib/ar_cache/version.rb
CHANGED
@@ -8,7 +8,14 @@ module ArCache
|
|
8
8
|
@klass = klass
|
9
9
|
@table = klass.ar_cache_table
|
10
10
|
@predicates = predicates
|
11
|
-
|
11
|
+
end
|
12
|
+
|
13
|
+
def missed_values
|
14
|
+
@missed_values ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def invalid_keys
|
18
|
+
@invalid_keys ||= []
|
12
19
|
end
|
13
20
|
|
14
21
|
def cacheable?
|
@@ -24,8 +31,8 @@ module ArCache
|
|
24
31
|
count = 0
|
25
32
|
|
26
33
|
bool = index.all? do |column|
|
27
|
-
where_values_hash[column].tap do
|
28
|
-
if
|
34
|
+
(Thread.current[:ar_cache_reflection] ? where_values_hash.key?(column) : where_values_hash[column]).tap do
|
35
|
+
if where_values_hash[column].is_a?(Array)
|
29
36
|
@multi_values_key = column
|
30
37
|
count += 1
|
31
38
|
end
|
@@ -52,15 +59,15 @@ module ArCache
|
|
52
59
|
@cache_hash = {}
|
53
60
|
multi_values_key = @multi_values_key || @index.first
|
54
61
|
|
55
|
-
Array(where_values_hash[multi_values_key]).each do |v|
|
62
|
+
Array.wrap(where_values_hash[multi_values_key]).each do |v|
|
56
63
|
@cache_hash[table.cache_key(where_values_hash, @index, multi_values_key, v)] = v
|
57
64
|
end
|
58
65
|
|
59
66
|
return @cache_hash if primary_key_index?
|
60
67
|
|
61
68
|
@original_cache_hash = @cache_hash
|
62
|
-
@cache_hash = ArCache
|
63
|
-
@original_cache_hash.each { |k, v|
|
69
|
+
@cache_hash = ArCache.read_multi(*@cache_hash.keys, raw: true)
|
70
|
+
@original_cache_hash.each { |k, v| missed_values << v unless @cache_hash.key?(k) }
|
64
71
|
@cache_hash = @cache_hash.invert
|
65
72
|
|
66
73
|
@cache_hash
|
@@ -73,26 +80,28 @@ module ArCache
|
|
73
80
|
end
|
74
81
|
|
75
82
|
def missed_hash
|
76
|
-
@missed_hash ||=
|
83
|
+
@missed_hash ||= missed_values.empty? ? {} : { (@multi_values_key || @index.first) => missed_values }
|
77
84
|
end
|
78
85
|
|
79
86
|
def add_missed_values(key)
|
80
87
|
if primary_key_index?
|
81
|
-
|
88
|
+
missed_values << cache_hash[key]
|
82
89
|
else
|
83
|
-
|
90
|
+
missed_values << @original_cache_hash[cache_hash[key]]
|
84
91
|
end
|
85
92
|
end
|
86
93
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
94
|
+
def add_blank_primary_cache_key(key)
|
95
|
+
invalid_keys << key
|
96
|
+
end
|
97
|
+
|
98
|
+
def add_invalid_second_cache_key(key)
|
99
|
+
# invalid_keys << key # The primary key index is reliable.
|
100
|
+
invalid_keys << cache_hash[key] unless primary_key_index?
|
92
101
|
end
|
93
102
|
|
94
103
|
def delete_invalid_keys
|
95
|
-
ArCache
|
104
|
+
ArCache.delete_multi(invalid_keys) if invalid_keys.any?
|
96
105
|
end
|
97
106
|
|
98
107
|
# This module is based on ActiveRecord::Relation::WhereClause modified
|
@@ -100,13 +109,10 @@ module ArCache
|
|
100
109
|
def where_values_hash
|
101
110
|
@where_values_hash ||= equalities(predicates).each_with_object({}) do |node, hash|
|
102
111
|
# Don't support Arel::Nodes::NamedFunction.
|
103
|
-
# But we don't judge it, because it will raise exception if it is Arel::Nodes::NamedFunction object.
|
104
112
|
next if table.name != node.left.relation.name
|
105
113
|
|
106
114
|
name = node.left.name.to_s
|
107
115
|
value = extract_node_value(node.right)
|
108
|
-
next if value.respond_to?(:size) && value.size > ArCache::Configuration.index_column_max_size
|
109
|
-
|
110
116
|
hash[name] = value
|
111
117
|
end
|
112
118
|
rescue NoMethodError, ActiveModel::RangeError
|
@@ -133,15 +139,18 @@ module ArCache
|
|
133
139
|
end
|
134
140
|
|
135
141
|
private def extract_node_value(node)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
142
|
+
case node
|
143
|
+
when Array
|
144
|
+
node.map { |v| extract_node_value(v) }
|
145
|
+
when Arel::Nodes::BindParam
|
146
|
+
value_for_database(node.value)
|
147
|
+
when Arel::Nodes::Casted, Arel::Nodes::Quoted
|
148
|
+
value_for_database(node)
|
149
|
+
end
|
150
|
+
end
|
144
151
|
|
152
|
+
private def value_for_database(node)
|
153
|
+
value = node.value_for_database # Maybe raise ActiveModel::RangeError
|
145
154
|
value.is_a?(Date) ? value.to_s : value
|
146
155
|
end
|
147
156
|
end
|
@@ -1,21 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rails/generators'
|
4
|
-
require 'rails/generators/migration'
|
5
|
-
require 'rails/generators/active_record/migration'
|
6
4
|
|
7
5
|
module ArCache
|
8
6
|
module Generators
|
9
7
|
class InstallGenerator < Rails::Generators::Base
|
10
|
-
include Rails::Generators::Migration
|
11
|
-
include ::ActiveRecord::Generators::Migration
|
12
|
-
|
13
8
|
source_root File.expand_path('templates', __dir__)
|
14
9
|
|
15
10
|
def copy_initializer_file
|
16
11
|
copy_file 'configuration.rb', 'config/initializers/ar_cache.rb'
|
17
|
-
|
18
|
-
migration_template 'migrate/create_ar_cache_records.rb', 'db/migrate/create_ar_cache_records.rb'
|
19
12
|
end
|
20
13
|
end
|
21
14
|
end
|
@@ -2,33 +2,33 @@
|
|
2
2
|
|
3
3
|
# For more information, please see: https://github.com/OuYangJinTing/ar_cache/README.md
|
4
4
|
ArCache.configure do |config|
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
#
|
11
|
-
# config.
|
12
|
-
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
# ArCache switch.
|
20
|
-
|
21
|
-
|
22
|
-
# Whether to support
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
5
|
+
# The cache tool. It must be an instance of ActiveSupport::Cache::Store.
|
6
|
+
config.cache_store = defined?(Rails) ? Rails.cache : ActiveSupport::Cache::MemoryStore.new
|
7
|
+
|
8
|
+
# NOTE: Please use cache lock if it casue database happened dead lock.
|
9
|
+
# ArCache default use database share lock('FOR SHARE' or 'LOCK IN SHARE MODE') to ensure that the cache is correct.
|
10
|
+
# You can customize the lock statement, if your database don't support lock (eg: SQLite3), please use cache lock.
|
11
|
+
# config.lock_statement = 'custom lock statement'
|
12
|
+
|
13
|
+
# WARNING: If the cache store is not Redis nor Memcached, the cache lock may be unreliable.
|
14
|
+
config.cache_lock = false # Boolean
|
15
|
+
|
16
|
+
# The cache key valid time.
|
17
|
+
config.expires_in = 1.week # Integer
|
18
|
+
|
19
|
+
# ArCache switch (default).
|
20
|
+
config.disabled = false # Boolean
|
21
|
+
|
22
|
+
# Whether to support select column sql (default)..
|
23
|
+
config.select_disabled = true # Boolean
|
24
|
+
|
25
|
+
# WARNING: If you use database lock, you should not custom unique index, otherwise may be happen lock table.
|
26
|
+
config.tables_options = {
|
27
|
+
# table_name: { # Database's table name.
|
28
|
+
# disabled: Boolean, # sArCache switch.
|
29
|
+
# select_disabled: Boolean, # Whether to support select column sql.
|
30
|
+
# unique_indexes: Array # eg: [:id, [:name, :statue]], The default is the unique index column of the table.
|
31
|
+
# },
|
32
|
+
# ...
|
33
|
+
}
|
34
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OuYangJinTing
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -30,6 +30,26 @@ dependencies:
|
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '7'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: oj
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '4'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '3'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '4'
|
33
53
|
description: An modern cacheing library for ActiveRecord inspired by cache-money and
|
34
54
|
second_level_cache.
|
35
55
|
email:
|
@@ -48,6 +68,7 @@ files:
|
|
48
68
|
- Gemfile.lock
|
49
69
|
- LICENSE.txt
|
50
70
|
- README.md
|
71
|
+
- README.zh-CN.md
|
51
72
|
- Rakefile
|
52
73
|
- ar_cache.gemspec
|
53
74
|
- bin/activerecord-test
|
@@ -72,14 +93,11 @@ files:
|
|
72
93
|
- lib/ar_cache/marshal.rb
|
73
94
|
- lib/ar_cache/mock_table.rb
|
74
95
|
- lib/ar_cache/query.rb
|
75
|
-
- lib/ar_cache/record.rb
|
76
|
-
- lib/ar_cache/store.rb
|
77
96
|
- lib/ar_cache/table.rb
|
78
97
|
- lib/ar_cache/version.rb
|
79
98
|
- lib/ar_cache/where_clause.rb
|
80
99
|
- lib/generators/ar_cache/install_generator.rb
|
81
100
|
- lib/generators/ar_cache/templates/configuration.rb
|
82
|
-
- lib/generators/ar_cache/templates/migrate/create_ar_cache_records.rb.tt
|
83
101
|
homepage: https://github.com/OuYangJinTing/ar_cache
|
84
102
|
licenses:
|
85
103
|
- MIT
|
data/lib/ar_cache/record.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ArCache
|
4
|
-
class Record < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
|
5
|
-
self.table_name = 'ar_cache_records'
|
6
|
-
|
7
|
-
def self.get(table_name)
|
8
|
-
ArCache.skip { find_by(table_name: table_name) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.version(table)
|
12
|
-
(get(table.name) || store(table)).version
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.update_version(table)
|
16
|
-
record = get(table.name)
|
17
|
-
return store(table).version unless record
|
18
|
-
|
19
|
-
record.update_version
|
20
|
-
record.version
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.store(table)
|
24
|
-
record = get(table.name) || new(table_name: table.name)
|
25
|
-
record.store(table)
|
26
|
-
record
|
27
|
-
end
|
28
|
-
|
29
|
-
def store(table)
|
30
|
-
with_optimistic_retry do
|
31
|
-
self.version += 1 unless table_md5 == table.md5
|
32
|
-
self.table_md5 = table.md5
|
33
|
-
|
34
|
-
save! if changed?
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def update_version
|
39
|
-
with_optimistic_retry do
|
40
|
-
self.version += 1
|
41
|
-
save!
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private def with_optimistic_retry
|
46
|
-
yield
|
47
|
-
rescue ::ActiveRecord::StaleObjectError
|
48
|
-
reload
|
49
|
-
retry
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
data/lib/ar_cache/store.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ArCache
|
4
|
-
class Store
|
5
|
-
@options = { raw: true, expires_in: ArCache::Configuration.expires_in }.freeze
|
6
|
-
|
7
|
-
class << self
|
8
|
-
delegate :delete, :delete_multi, :clear, :exist?, to: 'ArCache::Configuration.cache_store'
|
9
|
-
|
10
|
-
def write(name, value)
|
11
|
-
ArCache::Configuration.cache_store.write(name, dump(value), @options)
|
12
|
-
end
|
13
|
-
|
14
|
-
def write_multi(hash)
|
15
|
-
hash.each { |k, v| hash[k] = dump(v) }
|
16
|
-
ArCache::Configuration.cache_store.write_multi(hash, @options)
|
17
|
-
end
|
18
|
-
|
19
|
-
def read(name)
|
20
|
-
load(ArCache::Configuration.cache_store.read(name, @options))
|
21
|
-
end
|
22
|
-
|
23
|
-
def read_multi(names)
|
24
|
-
entries = ArCache::Configuration.cache_store.read_multi(*names, @options)
|
25
|
-
entries.each { |k, v| entries[k] = load(v) }
|
26
|
-
entries
|
27
|
-
end
|
28
|
-
|
29
|
-
private def dump(value)
|
30
|
-
ArCache::Configuration.coder.dump(value)
|
31
|
-
end
|
32
|
-
|
33
|
-
private def load(value)
|
34
|
-
ArCache::Configuration.coder.load(value) if value
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class CreateArCacheMonitors < ActiveRecord::Migration[<%= ActiveRecord::VERSION::MAJOR %>.<%= ActiveRecord::VERSION::MINOR %>]
|
4
|
-
def change
|
5
|
-
create_table :ar_cache_records do |t|
|
6
|
-
t.string :table_name, null: false
|
7
|
-
t.string :table_md5, null: false, limit: 32, default: '0' * 32
|
8
|
-
t.integer :version, null: false, default: 0
|
9
|
-
t.integer :lock_version, null: false, default: 0
|
10
|
-
|
11
|
-
t.timestamps null: false
|
12
|
-
|
13
|
-
t.index :table_name, unique: true
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|