ar_cache 2.0.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 +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
|