Improve scan performance (#79)

This commit is contained in:
Alex Ling 2020-07-01 14:01:26 +00:00
parent 1725f42698
commit eab0800376
2 changed files with 84 additions and 22 deletions

View File

@ -33,7 +33,16 @@ class Entry
MIME.from_filename? e.filename MIME.from_filename? e.filename
end end
file.close file.close
@id = storage.get_id @zip_path, false id = storage.get_id @zip_path, false
if id.nil?
id = random_str
storage.insert_id({
path: @zip_path,
id: id,
is_title: false,
})
end
@id = id
@mtime = File.info(@zip_path).modification_time @mtime = File.info(@zip_path).modification_time
end end
@ -178,7 +187,16 @@ class Title
def initialize(@dir : String, @parent_id, storage, def initialize(@dir : String, @parent_id, storage,
@library : Library) @library : Library)
@id = storage.get_id @dir, true id = storage.get_id @dir, true
if id.nil?
id = random_str
storage.insert_id({
path: @dir,
id: id,
is_title: true,
})
end
@id = id
@title = File.basename dir @title = File.basename dir
@encoded_title = URI.encode @title @encoded_title = URI.encode @title
@title_ids = [] of String @title_ids = [] of String
@ -525,7 +543,7 @@ end
class Library class Library
property dir : String, title_ids : Array(String), scan_interval : Int32, property dir : String, title_ids : Array(String), scan_interval : Int32,
storage : Storage, title_hash : Hash(String, Title) title_hash : Hash(String, Title)
def self.default : self def self.default : self
unless @@default unless @@default
@ -535,7 +553,6 @@ class Library
end end
def initialize def initialize
@storage = Storage.default
register_mime_types register_mime_types
@dir = Config.current.library_path @dir = Config.current.library_path
@ -589,17 +606,24 @@ class Library
Dir.mkdir_p @dir Dir.mkdir_p @dir
end end
@title_ids.clear @title_ids.clear
storage = Storage.new auto_close: false
(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 }
.select { |path| File.directory? path } .select { |path| File.directory? path }
.map { |path| Title.new path, "", @storage, self } .map { |path| Title.new path, "", storage, self }
.select { |title| !(title.entries.empty? && title.titles.empty?) } .select { |title| !(title.entries.empty? && title.titles.empty?) }
.sort { |a, b| a.title <=> b.title } .sort { |a, b| a.title <=> b.title }
.each do |title| .each do |title|
@title_hash[title.id] = title @title_hash[title.id] = title
@title_ids << title.id @title_ids << title.id
end end
storage.bulk_insert_ids
storage.close
Logger.debug "Scan completed" Logger.debug "Scan completed"
end end

View File

@ -14,6 +14,12 @@ end
class Storage class Storage
@path : String @path : String
@db : DB::Database?
@insert_ids = [] of IDTuple
alias IDTuple = NamedTuple(path: String,
id: String,
is_title: Bool)
def self.default : self def self.default : self
unless @@default unless @@default
@ -22,7 +28,8 @@ class Storage
@@default.not_nil! @@default.not_nil!
end end
def initialize(db_path : String? = nil, init_user = true) def initialize(db_path : String? = nil, init_user = true, *,
@auto_close = true)
@path = db_path || Config.current.db_path @path = db_path || Config.current.db_path
dir = File.dirname @path dir = File.dirname @path
unless Dir.exists? dir unless Dir.exists? dir
@ -60,6 +67,9 @@ class Storage
init_admin if init_user init_admin if init_user
end end
end end
unless @auto_close
@db = DB.open "sqlite3://#{@path}"
end
end end
macro init_admin macro init_admin
@ -71,8 +81,18 @@ class Storage
"#{{"username" => "admin", "password" => random_pw}}" "#{{"username" => "admin", "password" => random_pw}}"
end end
private def get_db(&block : DB::Database ->)
if @db.nil?
DB.open "sqlite3://#{@path}" do |db|
yield db
end
else
yield @db.not_nil!
end
end
def verify_user(username, password) def verify_user(username, password)
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
begin begin
hash, token = db.query_one "select password, token from " \ hash, token = db.query_one "select password, token from " \
"users where username = (?)", "users where username = (?)",
@ -97,7 +117,7 @@ class Storage
def verify_token(token) def verify_token(token)
username = nil username = nil
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
begin begin
username = db.query_one "select username from users where " \ username = db.query_one "select username from users where " \
"token = (?)", token, as: String "token = (?)", token, as: String
@ -110,7 +130,7 @@ class Storage
def verify_admin(token) def verify_admin(token)
is_admin = false is_admin = false
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
begin begin
is_admin = db.query_one "select admin from users where " \ is_admin = db.query_one "select admin from users where " \
"token = (?)", token, as: Bool "token = (?)", token, as: Bool
@ -123,7 +143,7 @@ class Storage
def list_users def list_users
results = Array(Tuple(String, Bool)).new results = Array(Tuple(String, Bool)).new
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
db.query "select username, admin from users" do |rs| db.query "select username, admin from users" do |rs|
rs.each do rs.each do
results << {rs.read(String), rs.read(Bool)} results << {rs.read(String), rs.read(Bool)}
@ -137,7 +157,7 @@ class Storage
validate_username username validate_username username
validate_password password validate_password password
admin = (admin ? 1 : 0) admin = (admin ? 1 : 0)
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
hash = hash_password password hash = hash_password password
db.exec "insert into users values (?, ?, ?, ?)", db.exec "insert into users values (?, ?, ?, ?)",
username, hash, nil, admin username, hash, nil, admin
@ -148,7 +168,7 @@ class Storage
admin = (admin ? 1 : 0) admin = (admin ? 1 : 0)
validate_username username validate_username username
validate_password password unless password.empty? validate_password password unless password.empty?
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
if password.empty? if password.empty?
db.exec "update users set username = (?), admin = (?) " \ db.exec "update users set username = (?), admin = (?) " \
"where username = (?)", "where username = (?)",
@ -163,13 +183,13 @@ class Storage
end end
def delete_user(username) def delete_user(username)
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
db.exec "delete from users where username = (?)", username db.exec "delete from users where username = (?)", username
end end
end end
def logout(token) def logout(token)
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
begin begin
db.exec "update users set token = (?) where token = (?)", nil, token db.exec "update users set token = (?) where token = (?)", nil, token
rescue rescue
@ -178,18 +198,36 @@ class Storage
end end
def get_id(path, is_title) def get_id(path, is_title)
id = random_str id = nil
DB.open "sqlite3://#{@path}" do |db| get_db do |db|
begin id = db.query_one? "select id from ids where path = (?)", path,
id = db.query_one "select id from ids where path = (?)", path, as: {String}
as: {String}
rescue
db.exec "insert into ids values (?, ?, ?)", path, id, is_title ? 1 : 0
end
end end
id id
end end
def insert_id(tp : IDTuple)
@insert_ids << tp
end
def bulk_insert_ids
get_db do |db|
db.transaction do |tx|
@insert_ids.each do |tp|
tx.connection.exec "insert into ids values (?, ?, ?)", tp[:path],
tp[:id], tp[:is_title] ? 1 : 0
end
end
end
@insert_ids.clear
end
def close
unless @db.nil?
@db.not_nil!.close
end
end
def to_json(json : JSON::Builder) def to_json(json : JSON::Builder)
json.string self json.string self
end end