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

[POC] Feature/introduce amd to MOVE #31851

Closed
wants to merge 5 commits into from

Conversation

DeepDiver1975
Copy link
Member

@DeepDiver1975 DeepDiver1975 commented Jun 21, 2018

Description

We want to avoid situations where the clients have to wait for the long MOVE operation on assembly to finish. The connection can be terminated by any reason before the server sends the response back to the client. And after that the client has no capability to query for the status of the assembly.

  1. AMD - Asynchronous Method Dispatch
    We return the http status code 202 right after we received the MOVE request. The client can then terminate the connection and knows that the server is working on it.
    The implementation uses php's register_callback functionality to process the real MOVE after the response is sent to the client. register_callback can not be terminated by the system - we should be pretty save on this one ....
    The 202 response will hold a header 'OC-JobStatus-Location' which will point the client to the job status endpoint

  2. Job Status Endpoint
    The job status endpoint is a DAV resource in the following format: /dav/job-status/{$userId}/{$this->jobId}. It will return a json object holding the job status information.

Property Description
status init, started, finished, error
fileId the file id of the target file - set only once the MOVE was successful
ETag the ETag of the file after MOVE on the target location - set only once the MOVE was successfull
errorCode http error code - in case there was an error
errorMessage there error message - in case of an error
  1. Server Side Event on the job status endpoint
    The client can request the job status in simple request/response manner or request an event stream following the Server Side Event protocol. To initiate SSE handling the query parameter sse=1 is appended to the GET request. As a result the server will not terminate the connection but keep it open and stream job status changes to the client. The client is asked to close the connection once the status is success or error

  2. Capabilities and LazyOps header
    The server will announce to the clients via the capabilities API if this new mode of operations is available or not. A system config value is necessary to allow the admin to turn this feature explicitly on.

Furthermore in order not to break regular WebDAV client an additional http header is to be sent from the client to the server so that the server knows if this operation mode shall be used or not.

Todo

  • add capability
  • add COPY support
  • detailed implementation to run MOVE pre flight checks before sending 202

Motivation and Context

Introduce a more generic async mechanism

How Has This Been Tested?

move

deepdiver@deepdiver:~$ curl -X MOVE  http://192.168.178.44:8080/remote.php/dav/files/admin/welcome.txt -uadmin -H 'OC-LazyOps: true' -H 'Destination: http://192.168.178.44:8080/remote.php/dav/files/admin/x/welcome.txt' -v
Enter host password for user 'admin':
*   Trying 192.168.178.44...
* TCP_NODELAY set
* Connected to 192.168.178.44 (192.168.178.44) port 8080 (#0)
* Server auth using Basic with user 'admin'
> MOVE /remote.php/dav/files/admin/welcome.txt HTTP/1.1
> Host: 192.168.178.44:8080
> Authorization: Basic YWRtaW46YWRtaW4=
> User-Agent: curl/7.60.0
> Accept: */*
> OC-LazyOps: true
> Destination: http://192.168.178.44:8080/remote.php/dav/files/admin/x/welcome.txt
> 
< HTTP/1.1 202 Accepted
< Host: 192.168.178.44:8080
< Date: Thu, 21 Jun 2018 08:44:43 +0000
< Connection: close
< X-Powered-By: PHP/7.1.18-1+ubuntu18.04.1+deb.sury.org+1
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< Set-Cookie: oc_sessionPassphrase=YABFWiAVwlohlGBpO8j3dv9zD%2FZhAesNpb482kp8Xt9iESpBU5tVGJuvrlCwvfWH1dLz23CknJcfzaFYgxzsmfZ6gMmtAkgBKpI4IlX8NKfPf9MVBShNTtzswKsaGoG%2B; path=/; HttpOnly
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Robots-Tag: none
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none
< Content-Security-Policy: default-src 'none';
< Set-Cookie: oc9fy8f2gwa8=n18gf5i45fkdkka1ual4c8mgvt; path=/; HttpOnly
< Set-Cookie: cookie_test=test; expires=Thu, 21-Jun-2018 09:44:40 GMT; Max-Age=3600
< Connection: close
< OC-Location: /remote.php/dav/admin/queue/d3c011a2-e640-4b94-bd7d-897aa99e9585
< Content-type: text/html; charset=UTF-8
< 
* Closing connection 0

Query for the status:

deepdiver@deepdiver:~$ curl -v -uadmin http://192.168.178.44:8080/remote.php/dav/queue/admin/d3c011a2-e640-4b94-bd7d-897aa99e9585
Enter host password for user 'admin':
*   Trying 192.168.178.44...
* TCP_NODELAY set
* Connected to 192.168.178.44 (192.168.178.44) port 8080 (#0)
* Server auth using Basic with user 'admin'
> GET /remote.php/dav/queue/admin/d3c011a2-e640-4b94-bd7d-897aa99e9585 HTTP/1.1
> Host: 192.168.178.44:8080
> Authorization: Basic YWRtaW46YWRtaW4=
> User-Agent: curl/7.60.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Host: 192.168.178.44:8080
< Date: Thu, 21 Jun 2018 09:00:06 +0000
< Connection: close
< X-Powered-By: PHP/7.1.18-1+ubuntu18.04.1+deb.sury.org+1
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< Set-Cookie: oc_sessionPassphrase=PFsuMhAl1oNSkKVHkAKY2vxSPKtJ%2BVBMMWx1zf6%2B6O%2FQU4PBz1%2BEssbXozILMCwXwNanx%2BnuaSSooLekzD2%2BOq46tDOCN9ZEjW412Ot2s%2FD55hKzLAAfFtGqFNVlBr7Z; path=/; HttpOnly
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Robots-Tag: none
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none
< Content-Security-Policy: default-src 'none';
< Set-Cookie: oc9fy8f2gwa8=8fbdm6ijnlah8162lc7lanf7ba; path=/; HttpOnly
< Set-Cookie: cookie_test=test; expires=Thu, 21-Jun-2018 10:00:06 GMT; Max-Age=3600
< Content-Type: application/json
< ETag: "e12309511a0155a316c6223c64511ba15b217a1a"
< Content-Length: 99
< OC-ETag: "e12309511a0155a316c6223c64511ba15b217a1a"
< 
* Closing connection 0
{"status":"finished","fileId":"00003061oc9fy8f2gwa8","ETag":"\"f3610808373e6aad584d69b4a0a068f5\""}

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@codecov
Copy link

codecov bot commented Jun 21, 2018

Codecov Report

Merging #31851 into master will decrease coverage by 0.37%.
The diff coverage is 77.71%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master   #31851      +/-   ##
============================================
- Coverage        64%   63.62%   -0.38%     
- Complexity    18578    18594      +16     
============================================
  Files          1171     1176       +5     
  Lines         69912    69772     -140     
  Branches       1267     1264       -3     
============================================
- Hits          44746    44393     -353     
- Misses        24796    25010     +214     
+ Partials        370      369       -1
Flag Coverage Δ Complexity Δ
#javascript 52.59% <100%> (-0.26%) 0 <0> (ø)
#phpunit 64.88% <77.32%> (-0.4%) 18594 <39> (+16)
Impacted Files Coverage Δ Complexity Δ
apps/dav/lib/Connector/Sabre/Server.php 100% <ø> (ø) 1 <0> (ø) ⬇️
...s/dav/appinfo/Migrations/Version20180622095921.php 0% <0%> (ø) 2 <2> (?)
apps/dav/lib/Capabilities.php 0% <0%> (ø) 3 <1> (+2) ⬆️
apps/dav/lib/JobStatus/Home.php 100% <100%> (ø) 5 <5> (?)
apps/dav/lib/RootCollection.php 100% <100%> (ø) 1 <0> (ø) ⬇️
apps/dav/lib/Server.php 52.98% <100%> (+1.55%) 25 <0> (+1) ⬆️
core/js/files/client.js 80.99% <100%> (ø) 0 <0> (ø) ⬇️
apps/dav/lib/JobStatus/Entity/JobStatusMapper.php 100% <100%> (ø) 2 <2> (?)
apps/dav/lib/JobStatus/JobStatus.php 100% <100%> (ø) 7 <7> (?)
apps/dav/lib/JobStatus/RootCollection.php 40% <40%> (ø) 2 <2> (?)
... and 116 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d743ffd...5042ce9. Read the comment docs.

@PVince81
Copy link
Contributor

We cannot re-use the Location header

try with "Content-Location" then. we did that for other DAV APIs

@PVince81
Copy link
Contributor

it's not strictly the entity's location, so maybe call it "Queue-Location" or "OC-Queue-Location" ?

}

public function httpMove(RequestInterface $request, ResponseInterface $response) {
if (!$request->getHeader('OC-LazyOps')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

we could call this "Queue" or something referring to the queue, so we have the same terminology everywhere

Copy link
Member Author

Choose a reason for hiding this comment

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

absolutly - I'm not yet fully convincent with the resource name 'queue' as well - since it is not a queue ....

Copy link
Contributor

@PVince81 PVince81 left a comment

Choose a reason for hiding this comment

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

interesting concept

The queue could also be used to track stray/stuck uploads and clean up chunks afterwards. This would require using the queue earlier when dealing with chunks. Or needs a completely separate mechanism.


$response->setStatus(202);
$response->addHeader('Connection', 'close');
$response->addHeader('OC-Location', $location);
Copy link
Contributor

Choose a reason for hiding this comment

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

OC-Queue-Location ?

]);
$this->server->emit('method:MOVE', [$request, $responseDummy]);

$this->setJobStatus([
Copy link
Contributor

Choose a reason for hiding this comment

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

if we can store the final file name in there we could solve a lot of problems, like the fact that encryption relies on the part file name and uses magic extraction to find the final file name... here it could just read from the upload queue

speaking of which... does this work with user-key encryption ? cron doesn't have the user's private key so cannot encrypt...

Copy link
Member Author

Choose a reason for hiding this comment

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

the execution is not done in cron - but in the original client request.

The difference is that we defer the execution to PHP's shutdown handler which is called after the response is sent back to the client

@ogoffart
Copy link

Good. This more or less follow the design that was already done in the client and documented in #12097 so implementation in the client can re-use that. (although that code sort of bit-rotted and was not ported to new chunking)

\sleep(1);
}

if (connection_aborted()) {
Copy link
Member

Choose a reason for hiding this comment

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

\

private $jobId;

public static function getQueueInfo(string $userId, string $jobId) {
return \OC::$server->getConfig()->getUserValue($userId, 'dav', "lazy-ops-job.$jobId", null, false);
Copy link
Member

Choose a reason for hiding this comment

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

I assume you are going to replace this with a proper queue table ...

Copy link
Member

Choose a reason for hiding this comment

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

or use a file property ... config is really not the place for this ....

Copy link
Member Author

Choose a reason for hiding this comment

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

sure - this was for the sake of fast POC

Copy link
Member

Choose a reason for hiding this comment

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

Could you use the existing oc_jobs? Insert as a 'already reserved' job, and add a status column?

Copy link
Member Author

Choose a reason for hiding this comment

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

Could you use the existing oc_jobs? Insert as a 'already reserved' job, and add a status column?

hmmmm - let me think about this ....

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so - there is no way to select the job status by it's id where the is is a uuid ....
... and also the processing logic will not work.

@DeepDiver1975
Copy link
Member Author

In order to detect that the connection was closed by the client the server needs to send something over the wire.

@DeepDiver1975
Copy link
Member Author

In order to detect that the connection was closed by the client the server needs to send something over the wire.

handled by the used library https://github.com/hhxsv5/php-sse/releases/tag/1.1.4

@DeepDiver1975 DeepDiver1975 force-pushed the feature/introduce-amd-to-chunk-move-2 branch from a6192ed to 0266157 Compare June 22, 2018 09:34
@DeepDiver1975 DeepDiver1975 force-pushed the feature/introduce-amd-to-chunk-move-2 branch 2 times, most recently from d3783cd to 805a99b Compare June 25, 2018 07:13
@DeepDiver1975 DeepDiver1975 changed the title [POC] Feature/introduce amd to chunk move 2 [POC] Feature/introduce amd to MOVE Jun 25, 2018
@DeepDiver1975 DeepDiver1975 force-pushed the feature/introduce-amd-to-chunk-move-2 branch from 805a99b to 1e3d5bb Compare June 25, 2018 10:19
ogoffart added a commit to owncloud/client that referenced this pull request Jun 25, 2018
@DeepDiver1975 DeepDiver1975 force-pushed the feature/introduce-amd-to-chunk-move-2 branch 3 times, most recently from f3bb9fd to aab4804 Compare June 27, 2018 14:32
@micbar
Copy link
Contributor

micbar commented Aug 17, 2018

@PVince81 needs backport for 10, see https://github.com/owncloud/enterprise/issues/2480

@DeepDiver1975
Copy link
Member Author

@PVince81 needs backport for 10, see owncloud/enterprise#2480

We have to analyse the impact of SSE to the system. Following the semver definitions this is nothing to be added to 10.0.x but to 10.1

@butonic butonic force-pushed the feature/introduce-amd-to-chunk-move-2 branch from c1c2671 to e2ad740 Compare August 21, 2018 13:51
@DeepDiver1975 DeepDiver1975 force-pushed the feature/introduce-amd-to-chunk-move-2 branch from e2ad740 to 5042ce9 Compare August 22, 2018 14:48
@DeepDiver1975 DeepDiver1975 force-pushed the feature/introduce-amd-to-chunk-move-2 branch from 5042ce9 to cef6bdc Compare August 22, 2018 14:52
@DeepDiver1975 DeepDiver1975 mentioned this pull request Aug 23, 2018
3 tasks
@@ -86,6 +90,17 @@ public function __construct(IRequest $request, $baseUri) {
$tree = new \OCA\DAV\Tree($root);
$this->server = new \OCA\DAV\Connector\Sabre\Server($tree);

$config = \OC::$server->getConfig();
if ($config->getSystemValue('dav.enable.async', true)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

for this backport version I think we should disable this by default ?

@PVince81
Copy link
Contributor

PVince81 commented Sep 4, 2018

the stable10 simplified version got merged. Here's its master port: #32576

ogoffart added a commit to owncloud/client that referenced this pull request Oct 17, 2018
@DeepDiver1975 DeepDiver1975 deleted the feature/introduce-amd-to-chunk-move-2 branch October 17, 2018 08:52
ogoffart added a commit to owncloud/client that referenced this pull request Oct 18, 2018
ogoffart added a commit to owncloud/client that referenced this pull request Mar 14, 2019
guruz pushed a commit to owncloud/client that referenced this pull request Mar 20, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 17, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants