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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ArCache
4
- VERSION = '1.2.0'
4
+ VERSION = '2.1.0'
5
5
  end
@@ -8,7 +8,14 @@ module ArCache
8
8
  @klass = klass
9
9
  @table = klass.ar_cache_table
10
10
  @predicates = predicates
11
- @missed_values = []
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 |value|
28
- if value.is_a?(Array)
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::Store.read_multi(@cache_hash.keys)
63
- @original_cache_hash.each { |k, v| @missed_values << v unless @cache_hash.key?(k) }
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 ||= @missed_values.empty? ? {} : { (@multi_values_key || @index.first) => @missed_values }
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
- @missed_values << cache_hash[key]
88
+ missed_values << cache_hash[key]
82
89
  else
83
- @missed_values << @original_cache_hash[cache_hash[key]]
90
+ missed_values << @original_cache_hash[cache_hash[key]]
84
91
  end
85
92
  end
86
93
 
87
- def add_invalid_keys(key)
88
- @invalid_keys ||= []
89
- @invalid_keys << key
90
- @invalid_keys << cache_hash[key] unless primary_key_index?
91
- @invalid_keys
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::Store.delete_multi(@invalid_keys) if @invalid_keys
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
- value = case node
137
- when Array
138
- node.map { |v| extract_node_value(v) }
139
- when Arel::Nodes::BindParam
140
- node.value.value_for_database # Maybe raise ActiveModel::RangeError
141
- when Arel::Nodes::Casted, Arel::Nodes::Quoted
142
- node.value_for_database # Maybe raise ActiveModel::RangeError
143
- end
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
- # WARNING: The should uncomment only when your database default isolation level is "READ UNCOMMITTED"!
6
- # config.read_uncommitted = true # defaul false
7
-
8
- # config.cache_store = ActiveSupport::Cache::Store # default Rails.cache || ActiveSupport::Cache::MemoryStore.new
9
-
10
- # Cache key automatic expiration time.
11
- # config.expires_in = Numeric # default 1 week
12
-
13
- # Serialize and deserialize cached data.
14
- # config.coder = [YAML|JSON] # default YAML
15
-
16
- # Support the maximum length of index column value.
17
- # config.index_column_max_size = Integer # default 64
18
-
19
- # ArCache switch.
20
- # config.disabled = Boolean # default false
21
-
22
- # Whether to support selecct columns query
23
- # config.select_disabled = Boolean # default true
24
-
25
- # config.tables_options = {
26
- # table_name: {
27
- # disabled: Boolean,
28
- # select_disabled: Boolean,
29
- # unique_indexes: Array # eg: [:id, [:name, :statue]], The default is the unique index column of the table.
30
- # ignored_columns: Array # eg: [:created_at, :updated_at], defaule [].
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.2.0
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-03-12 00:00:00.000000000 Z
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
@@ -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
@@ -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