Fixes issue #3336 - High system load, "StatusCode: 500 - execution expired", ActiveRecord::Deadlocked issues, and scheduler problems prevent Zammad from working well/quickly.
This commit is contained in:
parent
2a6036fb6e
commit
f6f1b37250
3 changed files with 11 additions and 34 deletions
|
@ -46,41 +46,17 @@ returns
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_and_save_to_cache_by(args)
|
def find_and_save_to_cache_by(attr)
|
||||||
attribute = args.keys.first
|
record = find_by(attr)
|
||||||
lookup_value = args.values.first.to_s
|
return nil if string_key?(attr.keys.first) && (record&.send(attr.keys.first) != attr.values.first.to_s) # enforce case-sensitivity on MySQL
|
||||||
|
return record if ActiveRecord::Base.connection.transaction_open? # rollbacks can invalidate cache entries
|
||||||
|
|
||||||
# rollbacks can invalidate cache entry
|
cache_set(attr.values.first, record)
|
||||||
# therefore we don't write it
|
record
|
||||||
if ActiveRecord::Base.connection.transaction_open?
|
|
||||||
result = find_by(attribute => lookup_value)
|
|
||||||
# enforce case-sensitivity on MySQL
|
|
||||||
result = nil if !key_sensitive_match?(result, attribute, lookup_value)
|
|
||||||
else
|
|
||||||
# get the record via an `FOR UPDATE` DB lock inside of
|
|
||||||
# a transaction to ensure that we don't write obsolete
|
|
||||||
# data into the cache
|
|
||||||
transaction do
|
|
||||||
result = lock.find_by(attribute => lookup_value)
|
|
||||||
# enforce case-sensitivity on MySQL
|
|
||||||
if key_sensitive_match?(result, attribute, lookup_value)
|
|
||||||
# cache only if we got a key-sensitive match
|
|
||||||
cache_set(lookup_value, result)
|
|
||||||
else
|
|
||||||
# no key-sensitive match - no result
|
|
||||||
result = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result
|
def string_key?(key)
|
||||||
end
|
type_for_attribute(key.to_s).type == :string
|
||||||
|
|
||||||
def key_sensitive_match?(record, attribute, lookup_value)
|
|
||||||
return false if record.blank?
|
|
||||||
return true if type_for_attribute(attribute.to_s).type != :string
|
|
||||||
|
|
||||||
record[attribute] == lookup_value
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,7 +41,8 @@ module ApplicationModel::HasCache
|
||||||
|
|
||||||
def cache_set(data_id, data)
|
def cache_set(data_id, data)
|
||||||
key = "#{self}::#{data_id}"
|
key = "#{self}::#{data_id}"
|
||||||
Cache.write(key, data)
|
# cache for 4 hours max to lower impact of race conditions
|
||||||
|
Cache.write(key, data, expires_in: 4.hours)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_get(data_id)
|
def cache_get(data_id)
|
||||||
|
|
|
@ -55,7 +55,7 @@ RSpec.shared_examples 'ApplicationModel::CanLookup' do
|
||||||
it 'saves the value to the cache' do
|
it 'saves the value to the cache' do
|
||||||
expect(Rails.cache)
|
expect(Rails.cache)
|
||||||
.to receive(:write)
|
.to receive(:write)
|
||||||
.with("#{described_class}::#{instance.send(attribute)}", instance, { expires_in: 7.days })
|
.with("#{described_class}::#{instance.send(attribute)}", instance, { expires_in: 4.hours })
|
||||||
.and_call_original
|
.and_call_original
|
||||||
|
|
||||||
expect { described_class.lookup(attribute => instance.send(attribute)) }
|
expect { described_class.lookup(attribute => instance.send(attribute)) }
|
||||||
|
|
Loading…
Reference in a new issue