Implementation of delta-sync support on client-side. - #6297
Conversation
7f97a0e to
d4011ab
Compare
ogoffart
left a comment
There was a problem hiding this comment.
I still haven't reviewed all of it.
There is quite a lot of jenkins faillure.
| ws2_32 | ||
| ) | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FILE_OFFSET_BITS=64") | ||
| set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64") |
There was a problem hiding this comment.
Why is this required?
It would be nice also if everything that is related to zsync lib was together so we could later split it in its own file.
There was a problem hiding this comment.
Because of size_t usage and assumptions by zsync. See webarchive comments.
There was a problem hiding this comment.
Regarding putting all the changes together in one place, I can do that, no problem.
| int metaHandle = zsyncmeta.handle(); | ||
| int tfHandle = zsynctf.handle(); | ||
|
|
||
| zsync_unique_ptr<FILE> meta(fdopen(dup(metaHandle), "w"), [](FILE *f) { |
There was a problem hiding this comment.
Why are you not using QFile here, and everywhere you use raw file operation?
QFile has a constructor that opens a file descriptor as well.
I'd think this would lead to more readable code.
There was a problem hiding this comment.
These functions were copied from zsync source as-is since they are not part of the exported api. Needed when generating a metadata file (part of their make cli tool). My main task was to integrate zsync, not re-write it.
There was a problem hiding this comment.
Also, please see the archive of all the comments I linked, none of this is new.
| extern "C" { | ||
| #include "librcksum/rcksum.h" | ||
| #include "libzsync/zmap.h" | ||
| #include "libzsync/sha1.h" |
There was a problem hiding this comment.
btw, Qt also has a way to compute the sha1.
There was a problem hiding this comment.
Again, see previous comment.
| </widget> | ||
| </item> | ||
| <item row="2" column="0"> | ||
| <widget class="QGroupBox" name="advancedGroupBox"> |
There was a problem hiding this comment.
I still think there should be no UI addition.
There was a problem hiding this comment.
Then you can feel free to remove it before the merge.
|
|
||
| namespace OCC { | ||
|
|
||
| class GETJob : public AbstractNetworkJob |
There was a problem hiding this comment.
If you've got time, you could try tomake a separate commit that refactor GETJob and GETFileJob.
That'd make reviewer slightly easier.
There was a problem hiding this comment.
My time is all up on this work now, it's taken way too long to get this far ...
|
Note: Jenkins failures were resolved after rebase on latest master ... |
This commit adds client-side support for delta-sync, this adds a new 3rdparty submodule `gh:ahmedammar/zsync`. This zsync tree is a modified version of upstream, adding some needed support for the upload path and other requirements. If the server does not announce the required zsync capability then a full upload/download is fallen back to. Delta synchronization can be enabled/disabled using command line, config, or gui options. On both upload and download paths, a check is made for the existance of a zsync metadata file on the server for a given path. This is provided by a dav property called `zsync`, found during discovery phase. If it doesn't exist the code reverts back to a complete upload or download, i.e. previous implementations. In the case of upload, a new zsync metadata file will be uploaded as part of the chunked upload and future synchronizations will be delta-sync capable. Chunked uploads no longer use sequential file names for each chunk id, instead, they are named as the byte offset into the remote file, this is a minimally intrusive modification to allow fo delta-sync and legacy code paths to run seamlessly. A new http header OC-Total-File-Length is sent, which informs the server of the final expected size of the file not just the total transmitted bytes as reported by OC-Total-Length. The seeding and generation of the zsync metadata file is done in a separate thread since this is a cpu intensive task, ensuring main thread is not blocked. This commit closes owncloud#179.
|
@mrow4a Not ready in my opinion. See below. @ahmedammar I understand your frustration. I genuinely think you've done an amazing job responding and fixing things based on the hundreds of review comments so far. The old PR ended up being too much for github because you were extraordinarily willing to discuss, adjust and fix all of the issues and nitpicks that came up. 👍 Unfortunately this is a large change to a critical component of the sync client and thus everyone seems a bit extra picky and there are worries about reliability. In addition it seems that, with you busy and probably not interested in continuing with this patch after it lands, the existing maintainers will have to fix whatever issues pop up. That makes it necessary for at least two people to understand what's going on well enough to be able to work on it efficiently. My main issues at this point are: reliability on WindowsWith this patch zsync will be responsible for a lot of the filesystem operations. One wouldn't expect it, but basic stuff for dealing with files can be surprisingly hard to get right (I'm thinking of mtime timezone issues, locked files, shared access permissions, etc). And I'm convinced we'll have several regressions in this area. reliability generallyWhile there are now two basic tests (yay!), there will be ample of bugs left to find. Since this patch mostly deals with uploads and downloads these are more likely than usual to be corruption/data loss kind of bugs. zsync integration generallyThe current PR copies some of zsync's code into the client, other code we link against. In any case, the version we link against is a custom modified version of zsync. Essentially zsync becomes part of the client project, but resides in a different repository while being mutually dependent. If there's no strong reason to keep these separate I'd just fold zsync into the client repository to keep things easy. review ageMy review was months ago and there have been many changes since (which is great and has improved things!). But still, I'll need to go over things again to get an up-to-date impression of what's going on. Path forward?I'd suggest something like this:
And then we need to stabilize. That means adding lots of tests, enabling it on dev machines, pinging interested users whether they're up for testing in low-risk scenarios. Eventually enable it by default. |
|
First, thanks for the detailed response and sharing your views so succinctly. All the points made by @ckamm are valid but I just wanted to respond to a few things.
|
|
From a user's perspective, this would be really a cool new feature. We've never got so close to this feature. There has been such a demand that there will be some people ready to test it. It will certainly require some development costs as well but what better feature to invest in for a sync solution? |
|
@tflidd it is indeed; we're thinking about generating some testpilotcloud installers as well as instructions for the server patch to get community to start trying this out as soon as possible to get feedback. We'll announce soon in https://central.owncloud.org. |
|
I don't think a server patch will be needed. We'll merge the server PR and release it in the upcoming 10.1 pre-alpha/alpha so people can use that for testing |
|
@ahmedammar Have you had any contact with the zsync maintainer? Why did you not create pull requests there? (In the case we cannot upstream the change, we will make a fork of zsync on github in the owncloud organisation) Edit: I actually see that you made a pull request. ( cph6/zsync#2 ) But why is it closed? |
|
I reached out to him, and got no response. |
|
@ahmedammar There was a meeting where we talked about this PR. Upcoming steps are:
At the same time there'll be another attempt to reach out to the author of zsync. Depending on how this goes we might upstream patches or create a fork in the owncloud organization. |
ckamm
left a comment
There was a problem hiding this comment.
I have gone through the diff again, looking primarily at the zsync-disabled branches.
There's one inline comment about progress updating where something seems off.
Besides that the only big change to existing paths is in the upload chunking. The chunk numbering the servers receive will be very different. I'll need to go through the updated logic again tomorrow.
| @@ -229,7 +245,7 @@ void ProgressInfo::setProgressComplete(const SyncFileItem &item) | |||
| _currentItems.remove(item._file); | |||
There was a problem hiding this comment.
needs to move below the if or _totalSizeOfCompletedJobs won't update correctly. edit: done
| * Example: With delta-sync, the actual size of the download will only | ||
| * be known during propagation - this function adjusts the total size | ||
| * to account for it. | ||
| */ |
There was a problem hiding this comment.
for later: This might need even more documentation: item._size must be the old size when this is called, different from newSize which isn't obvious to me. edit: done
| bool _removeJobError = false; /// if not null, there was an error removing the job | ||
| bool _zsyncSupported = false; /// if zsync is supported this will be set to true | ||
| bool _isZsyncMetadataUploadRunning = false; // flag to ensure that zsync metadata upload is complete before job is | ||
| quint64 _bytesToUpload; // in case of zsync upload this will hold the actual bytes to upload, normal upload will be file size |
There was a problem hiding this comment.
later: I was wondering what would happen to _bytesToUpload for the case of resumes. It turns out it works fine since it's always used in the context of "total bytes that need to arrive on the server before we're done".
So if the file size is 10 and a previous job uploaded 6, this member would be 10 even though the actual number of bytes to upload for this job is just 4. Rename / documentation change probably.
edit: done
| quint64 _sent = 0; /// amount of data (bytes) that was already sent | ||
| uint _transferId = 0; /// transfer id (part of the url) | ||
| int _currentChunk = 0; /// Id of the next chunk that will be sent | ||
| int _currentChunk = 0; /// id of the next chunk that will be sent |
There was a problem hiding this comment.
later: _currentChunk -> _currentChunkStart ? edit: done
|
|
||
| if (_sent > _item->_size) { | ||
| if (!_rangesToUpload.isEmpty()) | ||
| _currentChunk = _rangesToUpload.first().start; |
There was a problem hiding this comment.
later: Potentially redundant, _currentChunk is also set in startNextChunk() - only used in logging, it seems. edit: done
| propagator()->_activeJobList.append(this); | ||
|
|
||
| _zsyncSupported = isZsyncPropagationEnabled(propagator(), _item); | ||
| if (_zsyncSupported && _item->_remotePerm.hasPermission(RemotePermissions::HasZSyncMetadata)) { |
There was a problem hiding this comment.
later: In the case where this is false, one can still have _zsyncSupported be true and use the corresponding code paths below. I haven't looked in detail whether that's correct.
|
I've merged this PR manually into the |
|
@ckamm thanks for the review and pulling into a branch but how should I address the things mentioned above after you've already merged into a branch? |
|
@ahmedammar Let's continue working on it collaboratively in the branch by making small PRs against it. I've committed a couple of minor fixes directly but my larger changes will also go through pull requests for review. |
|
@ahmedammar @ckamm Hi, I've been testing this code a bit. I've set up two testing environments and I've found some bigger and smaller problems. From progress bar not being updated correctly to total crash of synchronization after synchronizing many times a group of files from a delta-sync enabled and regular client one after another. There was also a bug when delta-sync was not triggered when adding just the last byte to a file. How do I go on with reporting those? Otherwise it's bringing great speed improvements in some cases. |
|
I will check on this new branch and get back to you soon |
|
This feature will be in the upcoming 2.6 alpha release. |
This commit adds client-side support for delta-sync, this adds a new
3rdparty submodule gh:ahmedammar/zsync. This zsync tree is a modified
version of upstream, adding some needed support for the upload path and
other requirements.
If the server does not announce the required zsync capability then a
full upload/download is fallen back to. Delta synchronization can be
enabled/disabled using command line, config, or gui options.
On both upload and download paths, a check is made for the existance of
a zsync metadata file on the server for a given path. This is provided
by a dav property called
zsync, found during discovery phase. If itdoesn't exist the code reverts back to a complete upload or download,
i.e. previous implementations. In the case of upload, a new zsync
metadata file will be uploaded as part of the chunked upload and future
synchronizations will be delta-sync capable.
Chunked uploads no longer use sequential file names for each chunk id,
instead, they are named as the byte offset into the remote file, this is
a minimally intrusive modification to allow fo delta-sync and legacy
code paths to run seamlessly. A new http header OC-Total-File-Length is
sent, which informs the server of the final expected size of the file
not just the total transmitted bytes as reported by OC-Total-Length.
The seeding and generation of the zsync metadata file is done in a
separate thread since this is a cpu intensive task, ensuring main thread
is not blocked.
This commit closes #179.