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

Prototype: dynamic chunkingNG #5368

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,19 +645,58 @@ void Folder::startSync(const QStringList &pathList)
}

setDirtyNetworkLimits();
setSyncOptions();

_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);

QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);

emit syncStarted();
}

void Folder::setSyncOptions()
{
SyncOptions opt;
ConfigFile cfgFile;

auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
opt._newBigFolderSizeLimit = newFolderLimit.first ? newFolderLimit.second * 1000LL * 1000LL : -1; // convert from MB to B
opt._confirmExternalStorage = cfgFile.confirmExternalStorage();
_engine->setSyncOptions(opt);

_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
QByteArray chunkSizeEnv = qgetenv("OWNCLOUD_CHUNK_SIZE");
if (!chunkSizeEnv.isEmpty()) {
opt._initialChunkSize = chunkSizeEnv.toUInt();
} else {
opt._initialChunkSize = cfgFile.chunkSize();
}
QByteArray minChunkSizeEnv = qgetenv("OWNCLOUD_MIN_CHUNK_SIZE");
if (!minChunkSizeEnv.isEmpty()) {
opt._minChunkSize = minChunkSizeEnv.toUInt();
} else {
opt._minChunkSize = cfgFile.minChunkSize();
}
QByteArray maxChunkSizeEnv = qgetenv("OWNCLOUD_MAX_CHUNK_SIZE");
if (!maxChunkSizeEnv.isEmpty()) {
opt._maxChunkSize = maxChunkSizeEnv.toUInt();
} else {
opt._maxChunkSize = cfgFile.maxChunkSize();
}

QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);
// Previously min/max chunk size values didn't exist, so users might
// have setups where the chunk size exceeds the new min/max default
// values. To cope with this, adjust min/max to always include the
// initial chunk size value.
opt._minChunkSize = qMin(opt._minChunkSize, opt._initialChunkSize);
opt._maxChunkSize = qMax(opt._maxChunkSize, opt._initialChunkSize);

emit syncStarted();
QByteArray targetChunkUploadDurationEnv = qgetenv("OWNCLOUD_TARGET_CHUNK_UPLOAD_DURATION");
if (!targetChunkUploadDurationEnv.isEmpty()) {
opt._targetChunkUploadDuration = targetChunkUploadDurationEnv.toUInt();
} else {
opt._targetChunkUploadDuration = cfgFile.targetChunkUploadDuration();
}

_engine->setSyncOptions(opt);
}

void Folder::setDirtyNetworkLimits()
Expand Down
2 changes: 2 additions & 0 deletions src/gui/folder.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ private slots:

void checkLocalPath();

void setSyncOptions();

enum LogStatus {
LogStatusRemove,
LogStatusRename,
Expand Down
21 changes: 21 additions & 0 deletions src/libsync/configfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ static const char updateCheckIntervalC[] = "updateCheckInterval";
static const char geometryC[] = "geometry";
static const char timeoutC[] = "timeout";
static const char chunkSizeC[] = "chunkSize";
static const char minChunkSizeC[] = "minChunkSize";
static const char maxChunkSizeC[] = "maxChunkSize";
static const char targetChunkUploadDurationC[] = "targetChunkUploadDuration";

static const char proxyHostC[] = "Proxy/host";
static const char proxyTypeC[] = "Proxy/type";
Expand Down Expand Up @@ -130,6 +133,24 @@ quint64 ConfigFile::chunkSize() const
return settings.value(QLatin1String(chunkSizeC), 10*1000*1000).toLongLong(); // default to 10 MB
}

quint64 ConfigFile::maxChunkSize() const
{
QSettings settings(configFile(), QSettings::IniFormat);
return settings.value(QLatin1String(maxChunkSizeC), 100*1000*1000).toLongLong(); // default to 100 MB
}

quint64 ConfigFile::minChunkSize() const
{
QSettings settings(configFile(), QSettings::IniFormat);
return settings.value(QLatin1String(minChunkSizeC), 1000*1000).toLongLong(); // default to 1 MB
}

quint64 ConfigFile::targetChunkUploadDuration() const
{
QSettings settings(configFile(), QSettings::IniFormat);
return settings.value(QLatin1String(targetChunkUploadDurationC), 60*1000).toLongLong(); // default to 1 minute
}

void ConfigFile::setOptionalDesktopNotifications(bool show)
{
QSettings settings(configFile(), QSettings::IniFormat);
Expand Down
3 changes: 3 additions & 0 deletions src/libsync/configfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ class OWNCLOUDSYNC_EXPORT ConfigFile

int timeout() const;
quint64 chunkSize() const;
quint64 maxChunkSize() const;
quint64 minChunkSize() const;
quint64 targetChunkUploadDuration() const;

void saveGeometry(QWidget *w);
void restoreGeometry(QWidget *w);
Expand Down
31 changes: 30 additions & 1 deletion src/libsync/discoveryphase.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,41 @@ class Account;
*/

struct SyncOptions {
SyncOptions() : _newBigFolderSizeLimit(-1), _confirmExternalStorage(false) {}
SyncOptions()
: _newBigFolderSizeLimit(-1)
, _confirmExternalStorage(false)
, _initialChunkSize(10 * 1000 * 1000) // 10 MB
, _minChunkSize(1 * 1000 * 1000) // 1 MB
, _maxChunkSize(100 * 1000 * 1000) // 100 MB
, _targetChunkUploadDuration(60 * 1000) // 1 minute
{}

/** Maximum size (in Bytes) a folder can have without asking for confirmation.
* -1 means infinite */
qint64 _newBigFolderSizeLimit;

/** If a confirmation should be asked for external storages */
bool _confirmExternalStorage;

/** The initial un-adjusted chunk size in bytes for chunked uploads
*
* When dynamic chunk size adjustments are done, this is the
* starting value and is then gradually adjusted within the
* minChunkSize / maxChunkSize bounds.
*/
quint64 _initialChunkSize;

/** The minimum chunk size in bytes for chunked uploads */
quint64 _minChunkSize;

/** The maximum chunk size in bytes for chunked uploads */
quint64 _maxChunkSize;

/** The target duration of chunk uploads for dynamic chunk sizing.
*
* Set to 0 it will disable dynamic chunk sizing.
*/
quint64 _targetChunkUploadDuration;
};


Expand Down
29 changes: 13 additions & 16 deletions src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItemPtr &item) {
return job;
} else {
PropagateUploadFileCommon *job = 0;
if (item->_size > chunkSize() && account()->capabilities().chunkingNg()) {
if (item->_size > _chunkSize && account()->capabilities().chunkingNg()) {
job = new PropagateUploadFileNG(this, item);
} else {
job = new PropagateUploadFileV1(this, item);
Expand Down Expand Up @@ -503,6 +503,17 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
scheduleNextJob();
}

const SyncOptions& OwncloudPropagator::syncOptions() const
{
return _syncOptions;
}

void OwncloudPropagator::setSyncOptions(const SyncOptions& syncOptions)
{
_syncOptions = syncOptions;
_chunkSize = syncOptions._initialChunkSize;
}

// ownCloud server < 7.0 did not had permissions so we need some other euristics
// to detect wrong doing in a Shared directory
bool OwncloudPropagator::isInSharedDirectory(const QString& file)
Expand All @@ -522,7 +533,7 @@ bool OwncloudPropagator::isInSharedDirectory(const QString& file)

int OwncloudPropagator::httpTimeout()
{
static int timeout;
static int timeout = 0;
if (!timeout) {
timeout = qgetenv("OWNCLOUD_TIMEOUT").toUInt();
if (timeout == 0) {
Expand All @@ -534,20 +545,6 @@ int OwncloudPropagator::httpTimeout()
return timeout;
}

quint64 OwncloudPropagator::chunkSize()
{
static uint chunkSize;
if (!chunkSize) {
chunkSize = qgetenv("OWNCLOUD_CHUNK_SIZE").toUInt();
if (chunkSize == 0) {
ConfigFile cfg;
chunkSize = cfg.chunkSize();
}
}
return chunkSize;
}


bool OwncloudPropagator::localFileNameClash( const QString& relFile )
{
bool re = false;
Expand Down
19 changes: 15 additions & 4 deletions src/libsync/owncloudpropagator.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "syncjournaldb.h"
#include "bandwidthmanager.h"
#include "accountfwd.h"
#include "discoveryphase.h"

namespace OCC {

Expand Down Expand Up @@ -279,7 +280,6 @@ class OwncloudPropagator : public QObject {
SyncJournalDb * const _journal;
bool _finishedEmited; // used to ensure that finished is only emitted once


public:
OwncloudPropagator(AccountPtr account, const QString &localDir,
const QString &remoteFolder, SyncJournalDb *progressDb)
Expand All @@ -289,13 +289,17 @@ class OwncloudPropagator : public QObject {
, _finishedEmited(false)
, _bandwidthManager(this)
, _anotherSyncNeeded(false)
, _chunkSize(10 * 1000 * 1000) // 10 MB, overridden in setSyncOptions
, _account(account)
{ }

~OwncloudPropagator();

void start(const SyncFileItemVector &_syncedItems);

const SyncOptions& syncOptions() const;
void setSyncOptions(const SyncOptions& syncOptions);

QAtomicInt _downloadLimit;
QAtomicInt _uploadLimit;
BandwidthManager _bandwidthManager;
Expand All @@ -315,6 +319,15 @@ class OwncloudPropagator : public QObject {

/* the maximum number of jobs using bandwidth (uploads or downloads, in parallel) */
int maximumActiveTransferJob();

/** The size to use for upload chunks.
*
* Will be dynamically adjusted after each chunk upload finishes
* if Capabilities::desiredChunkUploadDuration has a target
* chunk-upload duration set.
*/
quint64 _chunkSize;

/* The maximum number of active jobs in parallel */
int hardMaximumActiveJob();

Expand Down Expand Up @@ -355,9 +368,6 @@ class OwncloudPropagator : public QObject {
// timeout in seconds
static int httpTimeout();

/** returns the size of chunks in bytes */
static quint64 chunkSize();

AccountPtr account() const;

enum DiskSpaceResult
Expand Down Expand Up @@ -404,6 +414,7 @@ private slots:

AccountPtr _account;
QScopedPointer<PropagateDirectory> _rootJob;
SyncOptions _syncOptions;

#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// access to signals which are protected in Qt4
Expand Down
1 change: 1 addition & 0 deletions src/libsync/propagateupload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ void PUTFileJob::start() {
connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
#endif

_requestTimer.start();
AbstractNetworkJob::start();
}

Expand Down
14 changes: 10 additions & 4 deletions src/libsync/propagateupload.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <QBuffer>
#include <QFile>
#include <QDebug>
#include <QElapsedTimer>


namespace OCC {
Expand Down Expand Up @@ -90,6 +91,7 @@ class PUTFileJob : public AbstractNetworkJob {
QMap<QByteArray, QByteArray> _headers;
QString _errorString;
QUrl _url;
QElapsedTimer _requestTimer;

public:
// Takes ownership of the device
Expand Down Expand Up @@ -123,6 +125,10 @@ class PUTFileJob : public AbstractNetworkJob {

virtual void slotTimeout() Q_DECL_OVERRIDE;

quint64 msSinceStart() const {
return _requestTimer.elapsed();
}


signals:
void finishedSignal();
Expand Down Expand Up @@ -201,7 +207,6 @@ class PropagateUploadFileCommon : public PropagateItemJob {
QByteArray _transmissionChecksum;
QByteArray _transmissionChecksumType;


public:
PropagateUploadFileCommon(OwncloudPropagator* propagator,const SyncFileItemPtr& item)
: PropagateItemJob(propagator, item), _finished(false), _deleteExisting(false) {}
Expand Down Expand Up @@ -276,7 +281,7 @@ class PropagateUploadFileV1 : public PropagateUploadFileCommon {
int _chunkCount; /// Total number of chunks for this file
int _transferId; /// transfer id (part of the url)

quint64 chunkSize() const { return propagator()->chunkSize(); }
quint64 chunkSize() const { return propagator()->syncOptions()._initialChunkSize; }


public:
Expand All @@ -303,14 +308,14 @@ class PropagateUploadFileNG : public PropagateUploadFileCommon {
quint64 _sent; /// amount of data (bytes) that was already sent
uint _transferId; /// transfer id (part of the url)
int _currentChunk; /// Id of the next chunk that will be sent
quint64 _currentChunkSize; /// current chunk size
bool _removeJobError; /// If not null, there was an error removing the job

// Map chunk number with its size from the PROPFIND on resume.
// (Only used from slotPropfindIterate/slotPropfindFinished because the LsColJob use signals to report data.)
struct ServerChunkInfo { quint64 size; QString originalName; };
QMap<int, ServerChunkInfo> _serverChunks;

quint64 chunkSize() const { return propagator()->chunkSize(); }
/**
* Return the URL of a chunk.
* If chunk == -1, returns the URL of the parent folder containing the chunks
Expand All @@ -319,9 +324,10 @@ class PropagateUploadFileNG : public PropagateUploadFileCommon {

public:
PropagateUploadFileNG(OwncloudPropagator* propagator,const SyncFileItemPtr& item) :
PropagateUploadFileCommon(propagator,item) {}
PropagateUploadFileCommon(propagator,item), _currentChunkSize(0) {}

void doStartUpload() Q_DECL_OVERRIDE;

private:
void startNewUpload();
void startNextChunk();
Expand Down
Loading