ar_cache 2.0.0 → 2.1.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/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/lib/ar_cache.rb +6 -0
- data/lib/ar_cache/active_record/connection_adapters/abstract/transaction.rb +1 -1
- data/lib/ar_cache/configuration.rb +14 -1
- data/lib/ar_cache/marshal.rb +13 -5
- data/lib/ar_cache/query.rb +8 -13
- data/lib/ar_cache/table.rb +1 -1
- data/lib/ar_cache/version.rb +1 -1
- data/lib/generators/ar_cache/templates/configuration.rb +15 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d9f5ff76912fe1e2276c795f619cf2e1e88780e7592e7fcb659e55a75bd8aff
|
4
|
+
data.tar.gz: 51ccc09d008c7d345dc9901c2898463ef5d2ebd40649b829b63ba801b13948da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8422672824015d7496620282919ff08b380066c8e05d8bde53bd8ef732f6b3a1c475f86bdd258f188be151fd78734b79bddbfa00bb20ce9b78be4443e96a168
|
7
|
+
data.tar.gz: 3059b104bf49417391e3edb84c5005822ba652b8c29bf8fd0de7c23080444fbd8345da5776a8bd0ba81b515845e567776c22b0a925c0235042c7383f4610cbc6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
3
|
## main
|
4
|
+
|
5
|
+
## 2.1.0 (2018-06-29 UTC)
|
6
|
+
|
7
|
+
- [PR [#4](https://github.com/OuYangJinTing/ar_cache/pull/4)] Fix high concurrency causes write dirty cache ([@OuYangJinTing](https://github.com/OuYangJinTing))
|
8
|
+
- [PR [#3](https://github.com/OuYangJinTing/ar_cache/pull/3)] Shouldn't open database transaction when use cache lock ([@OuYangJinTing](https://github.com/OuYangJinTing))
|
9
|
+
- [PR [#2](https://github.com/OuYangJinTing/ar_cache/pull/2)] Use configuration template as default values ([@OuYangJinTing](https://github.com/OuYangJinTing))
|
data/Gemfile.lock
CHANGED
data/lib/ar_cache.rb
CHANGED
@@ -71,5 +71,11 @@ module ArCache
|
|
71
71
|
def load_attributes(attributes)
|
72
72
|
memcached? || redis? ? Oj.load(attributes) : attributes
|
73
73
|
end
|
74
|
+
|
75
|
+
def lock_key(key)
|
76
|
+
ArCache.write(key, PLACEHOLDER, raw: true, expires_in: 1.hour)
|
77
|
+
end
|
74
78
|
end
|
75
79
|
end
|
80
|
+
|
81
|
+
require_relative './generators/ar_cache/templates/configuration'
|
@@ -14,7 +14,7 @@ module ArCache
|
|
14
14
|
|
15
15
|
def handle_ar_cache_primary_keys(keys)
|
16
16
|
if ArCache::Configuration.cache_lock?
|
17
|
-
keys.each { |k| ArCache.
|
17
|
+
keys.each { |k| ArCache.lock_key(k) }
|
18
18
|
else
|
19
19
|
ArCache.delete_multi(keys)
|
20
20
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ArCache
|
4
4
|
class Configuration
|
5
5
|
class << self
|
6
|
-
attr_writer :cache_lock
|
6
|
+
attr_writer :cache_lock, :lock_statement
|
7
7
|
attr_reader :cache_store, :tables_options
|
8
8
|
attr_accessor :disabled, :select_disabled, :expires_in
|
9
9
|
|
@@ -46,6 +46,19 @@ module ArCache
|
|
46
46
|
options[:unique_indexes] = Array(options[:unique_indexes]).map { |index| Array(index).map(&:to_s).uniq }.uniq
|
47
47
|
options
|
48
48
|
end
|
49
|
+
|
50
|
+
def lock_statement
|
51
|
+
@lock_statement ||= case ::ActiveRecord::Base.connection.class.name
|
52
|
+
when 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
|
53
|
+
'FOR SHARE'
|
54
|
+
when 'ActiveRecord::ConnectionAdapters::Mysql2Adapter'
|
55
|
+
'LOCK IN SHARE MODE'
|
56
|
+
when 'ActiveRecord::ConnectionAdapters::SQLite3Adapter'
|
57
|
+
raise "SQLite3 don't support lock statement, please use cache lock."
|
58
|
+
else
|
59
|
+
raise "Arcache can't identify database, please defined lock statement or use cache lock"
|
60
|
+
end
|
61
|
+
end
|
49
62
|
end
|
50
63
|
end
|
51
64
|
end
|
data/lib/ar_cache/marshal.rb
CHANGED
@@ -11,12 +11,19 @@ module ArCache
|
|
11
11
|
ArCache.delete_multi(ids.map { |id| primary_cache_key(id) })
|
12
12
|
end
|
13
13
|
|
14
|
-
def write(records)
|
14
|
+
def write(records) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
15
15
|
return -1 if disabled?
|
16
16
|
|
17
17
|
records.each do |attributes|
|
18
18
|
key = primary_cache_key(attributes[primary_key])
|
19
|
-
|
19
|
+
stringify_attributes = dump_attributes(attributes)
|
20
|
+
bool = ArCache.write(key, stringify_attributes, unless_exist: cache_lock?, raw: true, expires_in: expires_in)
|
21
|
+
if cache_lock? && !bool
|
22
|
+
value = ArCache.read(key, raw: true)
|
23
|
+
next if value == ArCache::PLACEHOLDER
|
24
|
+
next ArCache.lock_key(key) if value != stringify_attributes
|
25
|
+
end
|
26
|
+
|
20
27
|
unique_indexes.each_with_index do |index, i|
|
21
28
|
# The first index is primary key, should skip it.
|
22
29
|
ArCache.write(cache_key(attributes, index), key, raw: true, expires_in: expires_in) unless i.zero?
|
@@ -26,14 +33,15 @@ module ArCache
|
|
26
33
|
0
|
27
34
|
end
|
28
35
|
|
29
|
-
def read(where_clause, select_values = nil, &block) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/
|
36
|
+
def read(where_clause, select_values = nil, &block) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
30
37
|
entries_hash = ArCache.read_multi(*where_clause.cache_hash.keys, raw: true)
|
31
38
|
where_clause.cache_hash.each_key do |k|
|
32
39
|
v = entries_hash[k]
|
33
40
|
|
34
|
-
|
41
|
+
case v
|
42
|
+
when nil
|
35
43
|
where_clause.add_missed_values(k)
|
36
|
-
|
44
|
+
when ArCache::PLACEHOLDER
|
37
45
|
where_clause.add_missed_values(k)
|
38
46
|
where_clause.add_blank_primary_cache_key(k)
|
39
47
|
entries_hash.delete(k)
|
data/lib/ar_cache/query.rb
CHANGED
@@ -2,9 +2,7 @@
|
|
2
2
|
|
3
3
|
module ArCache
|
4
4
|
class Query
|
5
|
-
|
6
|
-
singleton_class.attr_accessor :lock_statement
|
7
|
-
delegate :lock_statement, :lock_statement=, to: 'self.class'
|
5
|
+
delegate :lock_statement, :cache_lock?, to: ArCache::Configuration
|
8
6
|
|
9
7
|
attr_reader :relation, :table, :where_clause
|
10
8
|
|
@@ -21,19 +19,16 @@ module ArCache
|
|
21
19
|
records = table.read(where_clause, @select_values, &block)
|
22
20
|
|
23
21
|
if where_clause.missed_hash.any?
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
missed_relation = relation.rewhere(where_clause.missed_hash).reselect('*')
|
23
|
+
missed_relation = missed_relation.lock(lock_statement) unless cache_lock?
|
24
|
+
missed_relation.arel.singleton_class.attr_accessor(:klass_and_select_values)
|
25
|
+
missed_relation.arel.klass_and_select_values = [relation.klass, @select_values]
|
26
|
+
if cache_lock?
|
27
|
+
records += missed_relation.find_by_sql(missed_relation.arel, &block)
|
28
|
+
else
|
28
29
|
missed_relation.connection.transaction do
|
29
30
|
records += missed_relation.find_by_sql(missed_relation.arel, &block)
|
30
31
|
end
|
31
|
-
rescue ::ActiveRecord::StatementInvalid => e
|
32
|
-
raise e if relation.connection.class.name != 'ActiveRecord::ConnectionAdapters::Mysql2Adapter'
|
33
|
-
raise e if lock_statement == 'LOCK IN SHARE MODE'
|
34
|
-
|
35
|
-
self.lock_statement = 'LOCK IN SHARE MODE'
|
36
|
-
retry
|
37
32
|
end
|
38
33
|
end
|
39
34
|
|
data/lib/ar_cache/table.rb
CHANGED
@@ -64,7 +64,7 @@ module ArCache
|
|
64
64
|
return '' if disabled?
|
65
65
|
|
66
66
|
key = "#{identity_cache_key}:#{short_sha1}:#{Time.now.to_f}"
|
67
|
-
ArCache.write(identity_cache_key, key, raw: true, expires_in:
|
67
|
+
ArCache.write(identity_cache_key, key, raw: true, expires_in: 20.years)
|
68
68
|
key
|
69
69
|
end
|
70
70
|
|
data/lib/ar_cache/version.rb
CHANGED
@@ -2,26 +2,31 @@
|
|
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
|
-
# Arcache default use database share lock to ensure that the cache is correct.
|
7
|
-
config.cache_lock = false # Boolean
|
8
|
-
|
9
|
-
# The cache tool.
|
5
|
+
# The cache tool. It must be an instance of ActiveSupport::Cache::Store.
|
10
6
|
config.cache_store = defined?(Rails) ? Rails.cache : ActiveSupport::Cache::MemoryStore.new
|
11
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
|
+
|
12
16
|
# The cache key valid time.
|
13
17
|
config.expires_in = 1.week # Integer
|
14
18
|
|
15
|
-
# ArCache switch.
|
19
|
+
# ArCache switch (default).
|
16
20
|
config.disabled = false # Boolean
|
17
21
|
|
18
|
-
# Whether to support select column sql
|
22
|
+
# Whether to support select column sql (default)..
|
19
23
|
config.select_disabled = true # Boolean
|
20
24
|
|
25
|
+
# WARNING: If you use database lock, you should not custom unique index, otherwise may be happen lock table.
|
21
26
|
config.tables_options = {
|
22
|
-
# table_name: {
|
23
|
-
# disabled: Boolean,
|
24
|
-
# select_disabled: Boolean,
|
27
|
+
# table_name: { # Database's table name.
|
28
|
+
# disabled: Boolean, # sArCache switch.
|
29
|
+
# select_disabled: Boolean, # Whether to support select column sql.
|
25
30
|
# unique_indexes: Array # eg: [:id, [:name, :statue]], The default is the unique index column of the table.
|
26
31
|
# },
|
27
32
|
# ...
|
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: 2.
|
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
|