mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-02 10:55:30 -04:00
Implement sorted entries cache
sorted_entries cached
This commit is contained in:
parent
4a09aee177
commit
bf81a4e48b
@ -157,3 +157,90 @@ class InfoCache
|
||||
@@cached_sort_opt_previous = {} of String => Hash(String, SortOptions)
|
||||
end
|
||||
end
|
||||
|
||||
private class SortedEntriesCacheEntry
|
||||
getter key : String, atime : Time
|
||||
|
||||
def initialize(@ctime : Time, @key : String, @value : Array(String))
|
||||
@atime = @ctime
|
||||
end
|
||||
|
||||
def value
|
||||
@atime = Time.utc
|
||||
@value
|
||||
end
|
||||
|
||||
def instance_size
|
||||
@value.size * (instance_sizeof(String) + sizeof(String)) +
|
||||
@value.sum(&.size) + instance_sizeof(SortedEntriesCacheEntry)
|
||||
end
|
||||
end
|
||||
|
||||
# LRU Cache
|
||||
class SortedEntriesCache
|
||||
@@limit : Int128 = Int128.new 1024 * 1024 * 50 # 50MB
|
||||
# key => entry
|
||||
@@cache = {} of String => SortedEntriesCacheEntry
|
||||
|
||||
def self.gen_key(book_id : String, username : String,
|
||||
entries : Array(Entry), opt : SortOptions?)
|
||||
sig = Digest::SHA1.hexdigest (entries.map &.id).to_s
|
||||
user_context = opt && opt.method == SortMethod::Progress ? username : ""
|
||||
Digest::SHA1.hexdigest (book_id + sig + user_context +
|
||||
(opt ? opt.to_tuple.to_s : "nil"))
|
||||
end
|
||||
|
||||
def self.get(key : String)
|
||||
entry = @@cache[key]?
|
||||
Logger.debug "SortedEntries Cache Hit! #{key}" unless entry.nil?
|
||||
Logger.debug "SortedEntries Cache Miss #{key}" if entry.nil?
|
||||
return ids2entries entry.value unless entry.nil?
|
||||
end
|
||||
|
||||
def self.set(key : String, value : Array(Entry))
|
||||
@@cache[key] = SortedEntriesCacheEntry.new Time.utc, key, value.map &.id
|
||||
Logger.debug "SortedEntries Cached #{key}"
|
||||
remove_victim_cache
|
||||
end
|
||||
|
||||
def self.invalidate(key : String)
|
||||
@@cache.delete key
|
||||
end
|
||||
|
||||
def self.print
|
||||
sum = @@cache.sum { |_, entry| entry.instance_size }
|
||||
Logger.debug "---- Sorted Entries Cache ----"
|
||||
Logger.debug "Size: #{sum} Bytes"
|
||||
Logger.debug "List:"
|
||||
@@cache.each { |k, v| Logger.debug "#{k} | #{v.atime}" }
|
||||
Logger.debug "------------------------------"
|
||||
end
|
||||
|
||||
private def self.ids2entries(ids : Array(String))
|
||||
e_map = Library.default.deep_entries.to_h { |entry| {entry.id, entry} }
|
||||
entries = [] of Entry
|
||||
begin
|
||||
ids.each do |id|
|
||||
entries << e_map[id]
|
||||
end
|
||||
return entries if ids.size == entries.size
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
private def self.is_cache_full
|
||||
sum = @@cache.sum { |_, entry| entry.instance_size }
|
||||
sum > @@limit
|
||||
end
|
||||
|
||||
private def self.remove_victim_cache
|
||||
while is_cache_full && @@cache.size > 0
|
||||
Logger.debug "SortedEntries Cache Full! Remove LRU"
|
||||
min = @@cache.min_by? { |_, entry| entry.atime }
|
||||
Logger.debug "Target: #{min[0]}, Last Access Time: #{min[1].atime}" if min
|
||||
invalidate min[0] if min
|
||||
|
||||
print if Logger.get_severity == Log::Severity::Debug
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -187,6 +187,11 @@ class Entry
|
||||
@book.parents.each do |parent|
|
||||
InfoCache.invalidate_progress_cache parent.id, username
|
||||
end
|
||||
[false, true].each do |ascend|
|
||||
sorted_entries_cache_key = SortedEntriesCache.gen_key @book.id, username,
|
||||
@book.entries, SortOptions.new(SortMethod::Progress, ascend)
|
||||
SortedEntriesCache.invalidate sorted_entries_cache_key
|
||||
end
|
||||
|
||||
TitleInfo.new @book.dir do |info|
|
||||
if info.progress[username]?.nil?
|
||||
|
@ -61,6 +61,10 @@ class Library
|
||||
titles + titles.flat_map &.deep_titles
|
||||
end
|
||||
|
||||
def deep_entries
|
||||
titles.flat_map &.deep_entries
|
||||
end
|
||||
|
||||
def to_slim_json : String
|
||||
JSON.build do |json|
|
||||
json.object do
|
||||
|
@ -344,6 +344,10 @@ class Title
|
||||
# use the default (auto, ascending)
|
||||
# When `opt` is not nil, it saves the options to info.json
|
||||
def sorted_entries(username, opt : SortOptions? = nil)
|
||||
cache_key = SortedEntriesCache.gen_key @id, username, @entries, opt
|
||||
cached_entries = SortedEntriesCache.get cache_key
|
||||
return cached_entries if cached_entries
|
||||
|
||||
if opt.nil?
|
||||
opt = SortOptions.from_info_json @dir, username
|
||||
end
|
||||
@ -377,6 +381,7 @@ class Title
|
||||
|
||||
ary.reverse! unless opt.not_nil!.ascend
|
||||
|
||||
SortedEntriesCache.set cache_key, ary
|
||||
ary
|
||||
end
|
||||
|
||||
@ -442,6 +447,12 @@ class Title
|
||||
parents.each do |parent|
|
||||
InfoCache.invalidate_progress_cache parent.id, username
|
||||
end
|
||||
[false, true].each do |ascend|
|
||||
sorted_entries_cache_key =
|
||||
SortedEntriesCache.gen_key @id, username, @entries,
|
||||
SortOptions.new(SortMethod::Progress, ascend)
|
||||
SortedEntriesCache.invalidate sorted_entries_cache_key
|
||||
end
|
||||
|
||||
selected_entries = ids
|
||||
.map { |id|
|
||||
|
Loading…
x
Reference in New Issue
Block a user