mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-03 03:15:31 -04:00
Implement library caching TitleInfo
* Cache sum of entry progress * Cache cover_url * Cache display_name * Cache sort_opt
This commit is contained in:
parent
00c9cc1fcd
commit
4a09aee177
159
src/library/cache.cr
Normal file
159
src/library/cache.cr
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
require "digest"
|
||||||
|
|
||||||
|
class InfoCache
|
||||||
|
alias ProgressCache = Tuple(String, Int32)
|
||||||
|
|
||||||
|
def self.clear
|
||||||
|
clear_cover_url
|
||||||
|
clear_progress_cache
|
||||||
|
clear_sort_opt
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.clean
|
||||||
|
clean_cover_url
|
||||||
|
clean_progress_cache
|
||||||
|
clean_sort_opt
|
||||||
|
end
|
||||||
|
|
||||||
|
# item id => cover_url
|
||||||
|
@@cached_cover_url = {} of String => String
|
||||||
|
@@cached_cover_url_previous = {} of String => String # item id => cover_url
|
||||||
|
|
||||||
|
def self.set_cover_url(id : String, cover_url : String)
|
||||||
|
@@cached_cover_url[id] = cover_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_cover_url(id : String)
|
||||||
|
@@cached_cover_url[id]?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.invalidate_cover_url(id : String)
|
||||||
|
@@cached_cover_url.delete id
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.move_cover_url(id : String)
|
||||||
|
if @@cached_cover_url_previous[id]?
|
||||||
|
@@cached_cover_url[id] = @@cached_cover_url_previous[id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.clear_cover_url
|
||||||
|
@@cached_cover_url_previous = @@cached_cover_url
|
||||||
|
@@cached_cover_url = {} of String => String
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.clean_cover_url
|
||||||
|
@@cached_cover_url_previous = {} of String => String
|
||||||
|
end
|
||||||
|
|
||||||
|
# book.id:username => {signature, sum}
|
||||||
|
@@progress_cache = {} of String => ProgressCache
|
||||||
|
# book.id => username => {signature, sum}
|
||||||
|
@@progress_cache_previous = {} of String => Hash(String, ProgressCache)
|
||||||
|
|
||||||
|
def self.set_progress_cache(book_id : String, username : String,
|
||||||
|
entries : Array(Entry), sum : Int32)
|
||||||
|
progress_cache_id = "#{book_id}:#{username}"
|
||||||
|
progress_cache_sig = Digest::SHA1.hexdigest (entries.map &.id).to_s
|
||||||
|
@@progress_cache[progress_cache_id] = {progress_cache_sig, sum}
|
||||||
|
Logger.debug "Progress Cached #{progress_cache_id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_progress_cache(book_id : String, username : String,
|
||||||
|
entries : Array(Entry))
|
||||||
|
progress_cache_id = "#{book_id}:#{username}"
|
||||||
|
progress_cache_sig = Digest::SHA1.hexdigest (entries.map &.id).to_s
|
||||||
|
cached = @@progress_cache[progress_cache_id]?
|
||||||
|
if cached && cached[0] == progress_cache_sig
|
||||||
|
Logger.debug "Progress Cache Hit! #{progress_cache_id}"
|
||||||
|
return cached[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.invalidate_progress_cache(book_id : String, username : String)
|
||||||
|
progress_cache_id = "#{book_id}:#{username}"
|
||||||
|
if @@progress_cache[progress_cache_id]?
|
||||||
|
@@progress_cache.delete progress_cache_id
|
||||||
|
Logger.debug "Progress Invalidate Cache #{progress_cache_id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.move_progress_cache(book_id : String)
|
||||||
|
if @@progress_cache_previous[book_id]?
|
||||||
|
@@progress_cache_previous[book_id].each do |username, cached|
|
||||||
|
id = "#{book_id}:#{username}"
|
||||||
|
unless @@progress_cache[id]?
|
||||||
|
# It would be invalidated when entries changed
|
||||||
|
@@progress_cache[id] = cached
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.clear_progress_cache
|
||||||
|
@@progress_cache_previous = {} of String => Hash(String, ProgressCache)
|
||||||
|
@@progress_cache.each do |id, cached|
|
||||||
|
splitted = id.split(':', 2)
|
||||||
|
book_id = splitted[0]
|
||||||
|
username = splitted[1]
|
||||||
|
unless @@progress_cache_previous[book_id]?
|
||||||
|
@@progress_cache_previous[book_id] = {} of String => ProgressCache
|
||||||
|
end
|
||||||
|
|
||||||
|
@@progress_cache_previous[book_id][username] = cached
|
||||||
|
end
|
||||||
|
@@progress_cache = {} of String => ProgressCache
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.clean_progress_cache
|
||||||
|
@@progress_cache_previous = {} of String => Hash(String, ProgressCache)
|
||||||
|
end
|
||||||
|
|
||||||
|
# book.dir:username => SortOptions
|
||||||
|
@@cached_sort_opt = {} of String => SortOptions
|
||||||
|
@@cached_sort_opt_previous = {} of String => Hash(String, SortOptions)
|
||||||
|
|
||||||
|
def self.set_sort_opt(dir : String, username : String, sort_opt : SortOptions)
|
||||||
|
id = "#{dir}:#{username}"
|
||||||
|
@@cached_sort_opt[id] = sort_opt
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_sort_opt(dir : String, username : String)
|
||||||
|
id = "#{dir}:#{username}"
|
||||||
|
@@cached_sort_opt[id]?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.invalidate_sort_opt(dir : String, username : String)
|
||||||
|
id = "#{dir}:#{username}"
|
||||||
|
@@cached_sort_opt.delete id
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.move_sort_opt(dir : String)
|
||||||
|
if @@cached_sort_opt_previous[dir]?
|
||||||
|
@@cached_sort_opt_previous[dir].each do |username, cached|
|
||||||
|
id = "#{dir}:#{username}"
|
||||||
|
unless @@cached_sort_opt[id]?
|
||||||
|
@@cached_sort_opt[id] = cached
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.clear_sort_opt
|
||||||
|
@@cached_sort_opt_previous = {} of String => Hash(String, SortOptions)
|
||||||
|
@@cached_sort_opt.each do |id, cached|
|
||||||
|
splitted = id.split(':', 2)
|
||||||
|
book_dir = splitted[0]
|
||||||
|
username = splitted[1]
|
||||||
|
unless @@cached_sort_opt_previous[book_dir]?
|
||||||
|
@@cached_sort_opt_previous[book_dir] = {} of String => SortOptions
|
||||||
|
end
|
||||||
|
@@cached_sort_opt_previous[book_dir][username] = cached
|
||||||
|
end
|
||||||
|
@@cached_sort_opt = {} of String => SortOptions
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.clean_sort_opt
|
||||||
|
@@cached_sort_opt_previous = {} of String => Hash(String, SortOptions)
|
||||||
|
end
|
||||||
|
end
|
@ -44,6 +44,8 @@ class Entry
|
|||||||
MIME.from_filename? e.filename
|
MIME.from_filename? e.filename
|
||||||
end
|
end
|
||||||
file.close
|
file.close
|
||||||
|
|
||||||
|
InfoCache.move_cover_url @id
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_slim_json : String
|
def to_slim_json : String
|
||||||
@ -81,6 +83,8 @@ class Entry
|
|||||||
|
|
||||||
def cover_url
|
def cover_url
|
||||||
return "#{Config.current.base_url}img/icon.png" if @err_msg
|
return "#{Config.current.base_url}img/icon.png" if @err_msg
|
||||||
|
cached_cover_url = InfoCache.get_cover_url @id
|
||||||
|
return cached_cover_url if cached_cover_url
|
||||||
|
|
||||||
unless @book.entry_cover_url_cache
|
unless @book.entry_cover_url_cache
|
||||||
TitleInfo.new @book.dir do |info|
|
TitleInfo.new @book.dir do |info|
|
||||||
@ -96,6 +100,7 @@ class Entry
|
|||||||
url = File.join Config.current.base_url, info_url
|
url = File.join Config.current.base_url, info_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
InfoCache.set_cover_url @id, url
|
||||||
url
|
url
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -178,6 +183,11 @@ class Entry
|
|||||||
# For backward backward compatibility with v0.1.0, we save entry titles
|
# For backward backward compatibility with v0.1.0, we save entry titles
|
||||||
# instead of IDs in info.json
|
# instead of IDs in info.json
|
||||||
def save_progress(username, page)
|
def save_progress(username, page)
|
||||||
|
InfoCache.invalidate_progress_cache @book.id, username
|
||||||
|
@book.parents.each do |parent|
|
||||||
|
InfoCache.invalidate_progress_cache parent.id, username
|
||||||
|
end
|
||||||
|
|
||||||
TitleInfo.new @book.dir do |info|
|
TitleInfo.new @book.dir do |info|
|
||||||
if info.progress[username]?.nil?
|
if info.progress[username]?.nil?
|
||||||
info.progress[username] = {@title => page}
|
info.progress[username] = {@title => page}
|
||||||
|
@ -102,6 +102,8 @@ class Library
|
|||||||
|
|
||||||
storage = Storage.new auto_close: false
|
storage = Storage.new auto_close: false
|
||||||
|
|
||||||
|
InfoCache.clear
|
||||||
|
|
||||||
(Dir.entries @dir)
|
(Dir.entries @dir)
|
||||||
.select { |fn| !fn.starts_with? "." }
|
.select { |fn| !fn.starts_with? "." }
|
||||||
.map { |fn| File.join @dir, fn }
|
.map { |fn| File.join @dir, fn }
|
||||||
@ -115,6 +117,8 @@ class Library
|
|||||||
@title_ids << title.id
|
@title_ids << title.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
InfoCache.clean
|
||||||
|
|
||||||
storage.bulk_insert_ids
|
storage.bulk_insert_ids
|
||||||
storage.close
|
storage.close
|
||||||
|
|
||||||
|
@ -60,6 +60,10 @@ class Title
|
|||||||
@entries.sort! do |a, b|
|
@entries.sort! do |a, b|
|
||||||
sorter.compare a.title, b.title
|
sorter.compare a.title, b.title
|
||||||
end
|
end
|
||||||
|
|
||||||
|
InfoCache.move_cover_url @id
|
||||||
|
InfoCache.move_progress_cache @id
|
||||||
|
InfoCache.move_sort_opt @dir
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_slim_json : String
|
def to_slim_json : String
|
||||||
@ -230,6 +234,9 @@ class Title
|
|||||||
end
|
end
|
||||||
|
|
||||||
def cover_url
|
def cover_url
|
||||||
|
cached_cover_url = InfoCache.get_cover_url @id
|
||||||
|
return cached_cover_url if cached_cover_url
|
||||||
|
|
||||||
url = "#{Config.current.base_url}img/icon.png"
|
url = "#{Config.current.base_url}img/icon.png"
|
||||||
readable_entries = @entries.select &.err_msg.nil?
|
readable_entries = @entries.select &.err_msg.nil?
|
||||||
if readable_entries.size > 0
|
if readable_entries.size > 0
|
||||||
@ -241,10 +248,12 @@ class Title
|
|||||||
url = File.join Config.current.base_url, info_url
|
url = File.join Config.current.base_url, info_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
InfoCache.set_cover_url @id, url
|
||||||
url
|
url
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_cover_url(url : String)
|
def set_cover_url(url : String)
|
||||||
|
InfoCache.invalidate_cover_url @id
|
||||||
TitleInfo.new @dir do |info|
|
TitleInfo.new @dir do |info|
|
||||||
info.cover_url = url
|
info.cover_url = url
|
||||||
info.save
|
info.save
|
||||||
@ -252,6 +261,8 @@ class Title
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_cover_url(entry_name : String, url : String)
|
def set_cover_url(entry_name : String, url : String)
|
||||||
|
selected_entry = @entries.find { |entry| entry.display_name == entry_name }
|
||||||
|
InfoCache.invalidate_cover_url selected_entry.id if selected_entry
|
||||||
TitleInfo.new @dir do |info|
|
TitleInfo.new @dir do |info|
|
||||||
info.entry_cover_url[entry_name] = url
|
info.entry_cover_url[entry_name] = url
|
||||||
info.save
|
info.save
|
||||||
@ -273,8 +284,13 @@ class Title
|
|||||||
end
|
end
|
||||||
|
|
||||||
def deep_read_page_count(username) : Int32
|
def deep_read_page_count(username) : Int32
|
||||||
load_progress_for_all_entries(username).sum +
|
# CACHE HERE
|
||||||
titles.flat_map(&.deep_read_page_count username).sum
|
cached_sum = InfoCache.get_progress_cache @id, username, @entries
|
||||||
|
return cached_sum unless cached_sum.nil?
|
||||||
|
sum = load_progress_for_all_entries(username).sum +
|
||||||
|
titles.flat_map(&.deep_read_page_count username).sum
|
||||||
|
InfoCache.set_progress_cache @id, username, @entries, sum
|
||||||
|
sum
|
||||||
end
|
end
|
||||||
|
|
||||||
def deep_total_page_count : Int32
|
def deep_total_page_count : Int32
|
||||||
@ -422,6 +438,11 @@ class Title
|
|||||||
end
|
end
|
||||||
|
|
||||||
def bulk_progress(action, ids : Array(String), username)
|
def bulk_progress(action, ids : Array(String), username)
|
||||||
|
InfoCache.invalidate_progress_cache @id, username
|
||||||
|
parents.each do |parent|
|
||||||
|
InfoCache.invalidate_progress_cache parent.id, username
|
||||||
|
end
|
||||||
|
|
||||||
selected_entries = ids
|
selected_entries = ids
|
||||||
.map { |id|
|
.map { |id|
|
||||||
@entries.find &.id.==(id)
|
@entries.find &.id.==(id)
|
||||||
|
@ -35,12 +35,15 @@ class SortOptions
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.from_info_json(dir, username)
|
def self.from_info_json(dir, username)
|
||||||
|
cached_opt = InfoCache.get_sort_opt dir, username
|
||||||
|
return cached_opt if cached_opt
|
||||||
opt = SortOptions.new
|
opt = SortOptions.new
|
||||||
TitleInfo.new dir do |info|
|
TitleInfo.new dir do |info|
|
||||||
if info.sort_by.has_key? username
|
if info.sort_by.has_key? username
|
||||||
opt = SortOptions.from_tuple info.sort_by[username]
|
opt = SortOptions.from_tuple info.sort_by[username]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
InfoCache.set_sort_opt dir, username, opt
|
||||||
opt
|
opt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,6 +120,7 @@ macro get_and_save_sort_opt(dir)
|
|||||||
|
|
||||||
sort_opt = SortOptions.new sort_method, is_ascending
|
sort_opt = SortOptions.new sort_method, is_ascending
|
||||||
|
|
||||||
|
InfoCache.set_sort_opt {{dir}}, username, sort_opt
|
||||||
TitleInfo.new {{dir}} do |info|
|
TitleInfo.new {{dir}} do |info|
|
||||||
info.sort_by[username] = sort_opt.to_tuple
|
info.sort_by[username] = sort_opt.to_tuple
|
||||||
info.save
|
info.save
|
||||||
|
Loading…
x
Reference in New Issue
Block a user