Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up local discovery by not doing it #6087

Merged
merged 3 commits into from
Oct 24, 2017
Merged

Speed up local discovery by not doing it #6087

merged 3 commits into from
Oct 24, 2017

Conversation

ckamm
Copy link
Contributor

@ckamm ckamm commented Oct 9, 2017

Use the data from the db instead and only check the file system when a file watcher event suggests it's necessary.

Since we're not confident that file watcher events are good enough, we still do a full local discovery every hour for now. This also doesn't get rid of the initial sync after client startup. But incremental syncs should be sped up significantly.

See #2297

@ckamm ckamm added this to the 2.5.0 milestone Oct 9, 2017
@ckamm ckamm self-assigned this Oct 9, 2017
@ckamm ckamm requested review from jturcotte, ogoffart and guruz October 9, 2017 12:58
@ckamm
Copy link
Contributor Author

ckamm commented Oct 9, 2017

This seemed to work in simple tests but clearly needs a lot more testing. I also broke the unittests - they still need fixing.

@ckamm
Copy link
Contributor Author

ckamm commented Oct 10, 2017

The existing unittests work again now. Next steps: Add new unittests that verify it works.

@@ -550,7 +550,7 @@ bool SyncJournalDb::checkConnect()
_getFilesBelowPathQuery.reset(new SqlQuery(_db));
if (_getFilesBelowPathQuery->prepare(
GET_FILE_RECORD_QUERY
" WHERE path > (?1||'/') AND path < (?1||'0') ORDER BY path||'/' ASC")) {
" WHERE ?1 == '' OR (path > (?1||'/') AND path < (?1||'0')) ORDER BY path||'/' ASC")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering: does the query uses the index?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, adding that term does indeed break index use (checked with EXPLAIN QUERY PLAN)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that our database doesn't keep paths with a initial /, so path > (''||'/') AND path < (''||'0'') matches nothing. Something like path > '' AND path < 'last-unicode-character' would work, but I think we just need a different query for clarity.

auto rowCallback = [ctx, &count, &skipbase, &files](const OCC::SyncJournalFileRecord &rec) {
if (ctx->current == REMOTE_REPLICA) {
/* When selective sync is used, the database may have subtrees with a parent
* whose etag (md5) is _invalid_. These are ignored and shall not appear in the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(unrelated: we could remove the reference to md5 here. It was there before because the _etag was called _md5, but it has gotten renamed)

return;
}

auto relativePath = path.mid(this->path().size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/mid/midRef/

@@ -783,6 +824,21 @@ void Folder::slotSyncFinished(bool success)
journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList());
}

// bug: This function uses many different criteria for "sync was successful" - investigate!
if ((_syncResult.status() == SyncResult::Success
|| _syncResult.status() == SyncResult::Problem)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it is problem even for soft errors?

// on 100% successful sync. ('Problem' syncs could have the touched
// file trigger a soft-error-and-retry scenario and wiping this list
// would mean the file isn't retried)
_locallyModifiedDirs.clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there were change hapenning during the sync!

Maybe we should clear it as we start the sync, and if the sync fails for any problem, we add them back.

Copy link
Contributor Author

@ckamm ckamm Oct 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ogoffart Yes, great catch! That means we really need to remove files from this list as the individual jobs report success even in the initial version. At least that also solves the problem with 'Problem' syncs due to soft errors at the same time.

src/gui/folder.h Outdated
*
* Created by registerFolderWatcher(), triggers slotWatchedPathChanged()
*/
FolderWatcher *_folderWatcher = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohoh! you are putting the FolderWatcher back in the Folder itself :-) cc/ @dragotin

src/gui/folder.h Outdated
*
* Created by registerFolderWatcher(), triggers slotWatchedPathChanged()
*/
FolderWatcher *_folderWatcher = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using QScopedPointer. Otherwise, nullptr instead of 0.

ckamm added 2 commits October 17, 2017 12:50
This makes it unnecessary for FolderMan to manage the list and removes
the need for some forwarders.

This is done in preparation for follow-up commits that want to add
diagnostics to FolderWatcher that shall be available from within Folder.
Add state and signal to catch the following two known problems:
* Linux: inotify user watch pool is exhausted. Folder watcher becomes
unreliable.
* Windows: buffer is too small, some notifications are lost but watching
stays reliable.
@ckamm
Copy link
Contributor Author

ckamm commented Oct 17, 2017

@ogoffart Significantly updated! The local-discovery-path list now has pretty elaborate sync-error handling.

Copy link
Contributor

@ogoffart ogoffart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7d6487e approved.

Copy link
Contributor

@ogoffart ogoffart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but please see the comments.

_getAllFilesQuery.reset(new SqlQuery(_db));
if (_getAllFilesQuery->prepare(
GET_FILE_RECORD_QUERY
" ORDER BY path||'/' ASC")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we concatenate '/' to the path while ordering?

// and we want to check whether we should read from the db. Assume "d/foo a" is
// in locally_touched_dirs. Then this check will say no, don't read from the db!
// (because "d/foo" < "d/foo a" < "d/foo/bar")
auto it = ctx->locally_touched_dirs.lower_bound(QByteArray(local_uri));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The QByteArray allocation could be avoided in C++14, there is an overload of lower_bound that can work for types for which the comparator is transparent.

But that would imply a change to the makefile to enable C++14.
Maybe it is not needed as it should not happen so often anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a note about this for C++14, but won't enable it in this PR.


if (lcFolder().isDebugEnabled()) {
QByteArrayList paths;
for (auto &path : _localDiscoveryPaths) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(minor: could be done with std::copy)

for (auto &path : _localDiscoveryPaths) {
paths.append(path);
}
qCDebug(lcFolder) << "local discovery paths: " << qUtf8Printable(paths.join(", "));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you not just qCDebug << paths? why do we need to convert it to a QByteArray first?
Also , qUtf8Priontable is for QString. When you have a QByteArray you can just use constData().

@@ -72,6 +72,9 @@ class OWNCLOUDSYNC_EXPORT ConfigFile
/* Force sync interval, in milliseconds */
quint64 forceSyncInterval(const QString &connection = QString()) const;

/** Interval within which full local discovery is required, -1 to disable */
qint64 fullLocalDiscoveryInterval() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the unit?
I know this is not yet your coding style. But in the future it would be nice to use things like std::chrono::milliseconds as a return type to make it explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

@ckamm
Copy link
Contributor Author

ckamm commented Oct 18, 2017

@ogoffart Resolved remaining issues

Copy link
Contributor

@ogoffart ogoffart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

// and we want to check whether we should read from the db. Assume "d/foo a" is
// in locally_touched_dirs. Then this check will say no, don't read from the db!
// (because "d/foo" < "d/foo a" < "d/foo/bar")
// C++17: Could skip the conversion to QByteArray here.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c++14

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes...

We mostly trust the file watchers meaning that we don't re-scan the
local tree if we have done that recently and no file watcher events
have arrived. If the file watchers invalidate a subtree, we rescan
only that subtree.

Since we're not entirely sure the file watchers are reliable, we still
do full local discoveries regularly (1h by default). There is a config
file setting as well as an environment variable to control the interval.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants