db/update/Walk: use marker to remove deleted items
This eliminates all `Storage::GetInfo()` calls from `UpdateWalk::PurgeDeletedFromDirectory()` and instead uses a "marker" field to mark items that have been visited; later, all unmarked items can be deleted. This eliminates a lot of redundant I/O which is noticable with the `curl` storage plugin (i.e. WebDAV).
This commit is contained in:
parent
9027e5c5bb
commit
7bb251dad8
2
NEWS
2
NEWS
@ -14,6 +14,8 @@ ver 0.24 (not yet released)
|
|||||||
- proxy: require libmpdclient 2.15 or later
|
- proxy: require libmpdclient 2.15 or later
|
||||||
* archive
|
* archive
|
||||||
- add option to disable archive plugins in mpd.conf
|
- add option to disable archive plugins in mpd.conf
|
||||||
|
* storage
|
||||||
|
- curl: optimize database update
|
||||||
* input
|
* input
|
||||||
- curl: add "connect_timeout" configuration
|
- curl: add "connect_timeout" configuration
|
||||||
* decoder
|
* decoder
|
||||||
|
@ -24,6 +24,12 @@ struct PlaylistInfo {
|
|||||||
std::chrono::system_clock::time_point mtime =
|
std::chrono::system_clock::time_point mtime =
|
||||||
std::chrono::system_clock::time_point::min();
|
std::chrono::system_clock::time_point::min();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is used by the database update to check whether
|
||||||
|
* an item has disappeared.
|
||||||
|
*/
|
||||||
|
bool mark;
|
||||||
|
|
||||||
class CompareName {
|
class CompareName {
|
||||||
const std::string_view name;
|
const std::string_view name;
|
||||||
|
|
||||||
|
@ -27,8 +27,11 @@ PlaylistVector::UpdateOrInsert(PlaylistInfo &&pi) noexcept
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
i->mtime = pi.mtime;
|
i->mtime = pi.mtime;
|
||||||
} else
|
i->mark = true;
|
||||||
|
} else {
|
||||||
|
pi.mark = true;
|
||||||
push_back(std::move(pi));
|
push_back(std::move(pi));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,12 @@ struct Directory : IntrusiveListHook<> {
|
|||||||
*/
|
*/
|
||||||
DatabasePtr mounted_database;
|
DatabasePtr mounted_database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is used by the database update to check whether
|
||||||
|
* an item has disappeared.
|
||||||
|
*/
|
||||||
|
bool mark;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Directory(std::string &&_path_utf8, Directory *_parent) noexcept;
|
Directory(std::string &&_path_utf8, Directory *_parent) noexcept;
|
||||||
~Directory() noexcept;
|
~Directory() noexcept;
|
||||||
|
@ -79,6 +79,12 @@ struct Song : IntrusiveListHook<> {
|
|||||||
*/
|
*/
|
||||||
bool in_playlist = false;
|
bool in_playlist = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is used by the database update to check whether
|
||||||
|
* an item has disappeared.
|
||||||
|
*/
|
||||||
|
bool mark;
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
Song(F &&_filename, Directory &_parent) noexcept
|
Song(F &&_filename, Directory &_parent) noexcept
|
||||||
:parent(_parent), filename(std::forward<F>(_filename)) {}
|
:parent(_parent), filename(std::forward<F>(_filename)) {}
|
||||||
|
@ -137,7 +137,6 @@ UpdateWalk::UpdateArchiveFile(Directory &parent, std::string_view name,
|
|||||||
file = archive_file_open(&plugin, path_fs);
|
file = archive_file_open(&plugin, path_fs);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
LogError(std::current_exception());
|
LogError(std::current_exception());
|
||||||
editor.LockDeleteDirectory(directory);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +144,8 @@ UpdateWalk::UpdateArchiveFile(Directory &parent, std::string_view name,
|
|||||||
|
|
||||||
UpdateArchiveVisitor visitor(*this, *file, *directory);
|
UpdateArchiveVisitor visitor(*this, *file, *directory);
|
||||||
file->Visit(visitor);
|
file->Visit(visitor);
|
||||||
|
|
||||||
|
directory->mark = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -29,17 +29,11 @@ try {
|
|||||||
FmtError(update_domain,
|
FmtError(update_domain,
|
||||||
"no read permissions on {}/{}",
|
"no read permissions on {}/{}",
|
||||||
directory.GetPath(), name);
|
directory.GetPath(), name);
|
||||||
if (song != nullptr)
|
|
||||||
editor.LockDeleteSong(directory, song);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(song != nullptr && info.mtime == song->mtime && !walk_discard) &&
|
if (!(song != nullptr && info.mtime == song->mtime && !walk_discard) &&
|
||||||
UpdateContainerFile(directory, name, suffix, info)) {
|
UpdateContainerFile(directory, name, suffix, info)) {
|
||||||
if (song != nullptr)
|
|
||||||
editor.LockDeleteSong(directory, song);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +49,8 @@ try {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_song->mark = true;
|
||||||
|
|
||||||
{
|
{
|
||||||
const ScopeDatabaseLock protect;
|
const ScopeDatabaseLock protect;
|
||||||
directory.AddSong(std::move(new_song));
|
directory.AddSong(std::move(new_song));
|
||||||
@ -66,14 +62,17 @@ try {
|
|||||||
} else if (info.mtime != song->mtime || walk_discard) {
|
} else if (info.mtime != song->mtime || walk_discard) {
|
||||||
FmtNotice(update_domain, "updating {}/{}",
|
FmtNotice(update_domain, "updating {}/{}",
|
||||||
directory.GetPath(), name);
|
directory.GetPath(), name);
|
||||||
if (!song->UpdateFile(storage)) {
|
if (song->UpdateFile(storage))
|
||||||
|
song->mark = true;
|
||||||
|
else
|
||||||
FmtDebug(update_domain,
|
FmtDebug(update_domain,
|
||||||
"deleting unrecognized file {}/{}",
|
"deleting unrecognized file {}/{}",
|
||||||
directory.GetPath(), name);
|
directory.GetPath(), name);
|
||||||
editor.LockDeleteSong(directory, song);
|
|
||||||
}
|
|
||||||
|
|
||||||
modified = true;
|
modified = true;
|
||||||
|
} else {
|
||||||
|
/* not modified */
|
||||||
|
song->mark = true;
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
FmtError(update_domain,
|
FmtError(update_domain,
|
||||||
|
@ -22,6 +22,7 @@ UpdateWalk::MakeVirtualDirectoryIfModified(Directory &parent, std::string_view n
|
|||||||
directory->device == virtual_device &&
|
directory->device == virtual_device &&
|
||||||
!walk_discard) {
|
!walk_discard) {
|
||||||
/* not modified */
|
/* not modified */
|
||||||
|
directory->mark = true;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ UpdateWalk::MakeVirtualDirectoryIfModified(Directory &parent, std::string_view n
|
|||||||
directory = parent.MakeChild(name);
|
directory = parent.MakeChild(name);
|
||||||
directory->mtime = info.mtime;
|
directory->mtime = info.mtime;
|
||||||
directory->device = virtual_device;
|
directory->device = virtual_device;
|
||||||
|
directory->mark = true;
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,19 @@ UpdateWalk::RemoveExcludedFromDirectory(Directory &directory,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
UnmarkAllIn(Directory &directory) noexcept
|
||||||
|
{
|
||||||
|
for (auto &i : directory.children)
|
||||||
|
i.mark = false;
|
||||||
|
|
||||||
|
for (auto &i : directory.songs)
|
||||||
|
i.mark = false;
|
||||||
|
|
||||||
|
for (auto &i : directory.playlists)
|
||||||
|
i.mark = false;
|
||||||
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept
|
UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept
|
||||||
{
|
{
|
||||||
@ -81,8 +94,7 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept
|
|||||||
/* mount points are always preserved */
|
/* mount points are always preserved */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (DirectoryExists(storage, child) &&
|
if (child.mark)
|
||||||
child.IsPluginAvailable())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* the directory was deleted (or the plugin which
|
/* the directory was deleted (or the plugin which
|
||||||
@ -94,9 +106,7 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept
|
|||||||
});
|
});
|
||||||
|
|
||||||
directory.ForEachSongSafe([&](Song &song){
|
directory.ForEachSongSafe([&](Song &song){
|
||||||
if (!directory_child_is_regular(storage, directory,
|
if (!song.mark) {
|
||||||
song.filename) ||
|
|
||||||
!song.IsPluginAvailable()) {
|
|
||||||
/* the song file was deleted (or the decoder
|
/* the song file was deleted (or the decoder
|
||||||
plugin is unavailable) */
|
plugin is unavailable) */
|
||||||
|
|
||||||
@ -109,7 +119,7 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept
|
|||||||
for (auto i = directory.playlists.begin(),
|
for (auto i = directory.playlists.begin(),
|
||||||
end = directory.playlists.end();
|
end = directory.playlists.end();
|
||||||
i != end;) {
|
i != end;) {
|
||||||
if (!directory_child_is_regular(storage, directory, i->name)) {
|
if (!i->mark) {
|
||||||
const ScopeDatabaseLock protect;
|
const ScopeDatabaseLock protect;
|
||||||
i = directory.playlists.erase(i);
|
i = directory.playlists.erase(i);
|
||||||
} else
|
} else
|
||||||
@ -343,7 +353,7 @@ UpdateWalk::UpdateDirectory(Directory &directory,
|
|||||||
if (!child_exclude_list.IsEmpty())
|
if (!child_exclude_list.IsEmpty())
|
||||||
RemoveExcludedFromDirectory(directory, child_exclude_list);
|
RemoveExcludedFromDirectory(directory, child_exclude_list);
|
||||||
|
|
||||||
PurgeDeletedFromDirectory(directory);
|
UnmarkAllIn(directory);
|
||||||
|
|
||||||
const char *name_utf8;
|
const char *name_utf8;
|
||||||
while (!cancel && (name_utf8 = reader->Read()) != nullptr) {
|
while (!cancel && (name_utf8 = reader->Read()) != nullptr) {
|
||||||
@ -370,7 +380,10 @@ UpdateWalk::UpdateDirectory(Directory &directory,
|
|||||||
UpdateDirectoryChild(directory, child_exclude_list, name_utf8, info2);
|
UpdateDirectoryChild(directory, child_exclude_list, name_utf8, info2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PurgeDeletedFromDirectory(directory);
|
||||||
|
|
||||||
directory.mtime = info.mtime;
|
directory.mtime = info.mtime;
|
||||||
|
directory.mark = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user