File watcher: Reduce touch ignore duration

On Linux and Windows the file watcher can't distinguish between changes
that were caused by the process itself, like during a sync operation,
and external changes. To work around that the client keeps a list of
files it has touched and blocks notifications on these files for a bit.

The duration of this block was originally and arbitrarily set at 15
seconds. During manual tests I regularly thought there was a bug when
syncs didn't trigger, when the only problem was that my changes happened
too close to a previous sync operation.

This change reduces the duration to three seconds. I imagine that this
is still enough.

Also use std::chrono while at it.
This commit is contained in:
Christian Kamm 2019-02-08 09:57:11 +01:00 committed by Kevin Ottens
parent 539cef345e
commit 4d58208676
No known key found for this signature in database
GPG key ID: 074BBBCB8DECC9E2
5 changed files with 35 additions and 13 deletions

View file

@ -497,7 +497,7 @@ int main(int argc, char **argv)
}
// much lower age than the default since this utility is usually made to be run right after a change in the tests
SyncEngine::minimumFileAgeForUpload = 0;
SyncEngine::minimumFileAgeForUpload = std::chrono::milliseconds(0);
int restartCount = 0;
restart_sync:

View file

@ -61,7 +61,7 @@ static bool fileIsStillChanging(const SyncFileItem &item)
const QDateTime modtime = Utility::qDateTimeFromTime_t(item._modtime);
const qint64 msSinceMod = modtime.msecsTo(QDateTime::currentDateTimeUtc());
return msSinceMod < SyncEngine::minimumFileAgeForUpload
return std::chrono::milliseconds(msSinceMod) < SyncEngine::minimumFileAgeForUpload
// if the mtime is too much in the future we *do* upload the file
&& msSinceMod > -10000;
}

View file

@ -38,6 +38,7 @@
#include <climits>
#include <cassert>
#include <chrono>
#include <QCoreApplication>
#include <QSslSocket>
@ -58,10 +59,25 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcEngine, "nextcloud.sync.engine", QtInfoMsg)
static const int s_touchedFilesMaxAgeMs = 15 * 1000;
bool SyncEngine::s_anySyncRunning = false;
qint64 SyncEngine::minimumFileAgeForUpload = 2000;
/** When the client touches a file, block change notifications for this duration (ms)
*
* On Linux and Windows the file watcher can't distinguish a change that originates
* from the client (like a download during a sync operation) and an external change.
* To work around that, all files the client touches are recorded and file change
* notifications for these are blocked for some time. This value controls for how
* long.
*
* Reasons this delay can't be very small:
* - it takes time for the change notification to arrive and to be processed by the client
* - some time could pass between the client recording that a file will be touched
* and its filesystem operation finishing, triggering the notification
*/
static const std::chrono::milliseconds s_touchedFilesMaxAgeMs(3 * 1000);
// doc in header
std::chrono::milliseconds SyncEngine::minimumFileAgeForUpload(2000);
SyncEngine::SyncEngine(AccountPtr account, const QString &localPath,
const QString &remotePath, OCC::SyncJournalDb *journal)
@ -911,8 +927,9 @@ void SyncEngine::slotAddTouchedFile(const QString &fn)
break;
// Compare to our new QElapsedTimer instead of using elapsed().
// This avoids querying the current time from the OS for every loop.
if (now.msecsSinceReference() - first.key().msecsSinceReference() <= s_touchedFilesMaxAgeMs) {
// We found the first path younger than 15 second, keep the rest.
auto elapsed = std::chrono::milliseconds(now.msecsSinceReference() - first.key().msecsSinceReference());
if (elapsed <= s_touchedFilesMaxAgeMs) {
// We found the first path younger than the maximum age, keep the rest.
break;
}
@ -934,7 +951,7 @@ bool SyncEngine::wasFileTouched(const QString &fn) const
auto begin = _touchedFiles.constBegin();
for (auto it = _touchedFiles.constEnd(); it != begin; --it) {
if ((it-1).value() == fn)
return (it-1).key().elapsed() <= s_touchedFilesMaxAgeMs;
return std::chrono::milliseconds((it-1).key().elapsed()) <= s_touchedFilesMaxAgeMs;
}
return false;
}

View file

@ -87,12 +87,17 @@ public:
SyncJournalDb *journal() const { return _journal; }
QString localPath() const { return _localPath; }
/**
* Minimum age, in milisecond, of a file that can be uploaded.
* Files more recent than that are not going to be uploaeded as they are considered
* too young and possibly still changing
/** Duration in ms that uploads should be delayed after a file change
*
* In certain situations a file can be written to very regularly over a large
* amount of time. Copying a large file could take a while. A logfile could be
* updated every second.
*
* In these cases it isn't desirable to attempt to upload the "unfinished" file.
* To avoid that, uploads of files where the distance between the mtime and the
* current time is less than this duration are skipped.
*/
static qint64 minimumFileAgeForUpload; // in ms
static std::chrono::milliseconds minimumFileAgeForUpload;
/**
* Control whether local discovery should read from filesystem or db.

View file

@ -906,7 +906,7 @@ public:
: _localModifier(_tempDir.path())
{
// Needs to be done once
OCC::SyncEngine::minimumFileAgeForUpload = 0;
OCC::SyncEngine::minimumFileAgeForUpload = std::chrono::milliseconds(0);
OCC::Logger::instance()->setLogFile("-");
QDir rootDir{_tempDir.path()};