|
|
Created:
5 years, 6 months ago by falken Modified:
5 years, 6 months ago CC:
chromium-reviews, jsbell+serviceworker_chromium.org, tzik, serviceworker-reviews, jam, nhiroki, darin-cc_chromium.org, horo+watch_chromium.org, kinuko+serviceworker, kinuko+watch Base URL:
https://chromium.googlesource.com/chromium/src.git@master Target Ref:
refs/pending/heads/master Project:
chromium Visibility:
Public. |
DescriptionService Worker: Don't write to disk during update until proven necessary
For updates, don't write the main script to disk until a change with the
incumbent script is detected. The incumbent script is progressively
compared with as the new script is read from network. Once a change is
detected, copy everything matched up until now to disk, and from then on
write to disk as the script continues to be read from network.
BUG=457013
Committed: https://crrev.com/672f9b44c0176a503d0f996e457e3cd8db036c96
Cr-Commit-Position: refs/heads/master@{#333031}
Patch Set 1 #Patch Set 2 : compile fix #Patch Set 3 : review comments #Patch Set 4 : more refactor #Patch Set 5 : #Patch Set 6 : patch for review #
Total comments: 8
Patch Set 7 : sync #Patch Set 8 : review comments #
Total comments: 20
Patch Set 9 : sync #Patch Set 10 : review comments #Patch Set 11 : review comments and bugs #Patch Set 12 : again #
Total comments: 17
Patch Set 13 : review comments #
Messages
Total messages: 19 (4 generated)
falken@chromium.org changed reviewers: + michaeln@chromium.org, nhiroki@chromium.org
michaeln: This is still quite WIP but could you see if this is the right direction?
The behavior described in the CL description sounds great. I like your approach doesn't involve a "read it all in" step. The code in snapshot2 to do that looks a little tricky since the job now has different modes of operating when receiving net data and it switches modes over the course of a single job. Some refactoring to manage that complexity might be nice? Maybe think of the 'job' class as mediating between the netdataRequest and an abstract netdataConsoumer, where the consumer can be swapped out midjob to go from comparing to writing. The other thing that came to mind is that if the comparing phase were willing to buffer some data, the copying step could use it to avoid some re-reading.
Thanks for the comments! It's ready for another look. I've refactored along those lines. Buffering by the comparer is a good idea. As the patch is already big, I think I'll save it for a follow-up patch.
LGTM with nits https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... File content/browser/service_worker/service_worker_context_request_handler.cc (right): https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_context_request_handler.cc:82: stored_version->script_cache_map()->LookupResourceId(request->url()); nit: Can you wrap this if-statement body with {}? https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:7: #include <algorithm> nit: Can you insert an empty line after <algorithm>? https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:772: return; nit: this return is not necessary. https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc (right): https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:275: SERVICE_WORKER_PROVIDER_FOR_WORKER, context()->AsWeakPtr(), nullptr)); Why did you change the provider type?
Updated, thanks! Michael, can you take another look? https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... File content/browser/service_worker/service_worker_context_request_handler.cc (right): https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_context_request_handler.cc:82: stored_version->script_cache_map()->LookupResourceId(request->url()); On 2015/06/03 05:36:14, nhiroki wrote: > nit: Can you wrap this if-statement body with {}? Done. https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:7: #include <algorithm> On 2015/06/03 05:36:14, nhiroki wrote: > nit: Can you insert an empty line after <algorithm>? Done. https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:772: return; On 2015/06/03 05:36:14, nhiroki wrote: > nit: this return is not necessary. Done. https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc (right): https://codereview.chromium.org/1166433003/diff/100001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:275: SERVICE_WORKER_PROVIDER_FOR_WORKER, context()->AsWeakPtr(), nullptr)); On 2015/06/03 05:36:14, nhiroki wrote: > Why did you change the provider type? I think the original was wrong. This provider host has a running_hosted_version_, which means it's a provider for a worker.
looking pretty good https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (left): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:248: http_info_.reset(info_buffer_->http_info.release()); I'm not sure how intentional it was to defer setting http_into_ until NotifyHeadersComplete time? As coded on trunk, callers sampling http_info_ backed data members (charset,mimetype,responsecode) will see <nothing> until headers complete time. In the CL, those values will be exposed prior to headers complete time. I'm not sure if that matters, but I remember it not being accidental that it was coded that way. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:203: void HandleData(net::IOBuffer* buf, int size) override { What keeps |buf| alive here? Since the comparer can outlive the job, you might need a scoped_refptr in this class. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:224: bool OnResponseEnded() override { The difference between ending syncrhously when Read immediately returns 0 vs asyncly when zero is delivered to the completion callback is a nuisance. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:257: if (result == 0) { nit: smush these two ifs together if (result <= 0) https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:264: if (memcmp(pos_, io_buffer_->data(), result) != 0) { nit: having the term net_data in the first arg might help grokability (similarly calling io_buffer read_buffer might help) https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:303: const char* pos_ = nullptr; keeping an net_data_offset_ instead of rawptrs might be safer/cleaner? https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc (right): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:360: ServiceWorkerRequestHandler::InitializeHandler( There's a lot of similarity between this dense setup code and that found in SetUp(). I'd vote to factor out a common helper method that takes a few params to handle any differences. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:458: TEST_F(ServiceWorkerWriteToCacheJobTest, Update_SameSizeScript) { maybe 3, samesize test for the edge conditions? - change at the start (first 16k block) - change in the middle (not the first or last 16k block) - change at the end (last 16k block) https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:461: response[7 * 1024 + 42] = 'x'; what about putting the difference past the 16k point so the for 16 is copied from the existing resource and the remainder comes from the net, maybe the elongated test is close enough to that https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:485: TEST_F(ServiceWorkerWriteToCacheJobTest, Update_EmptyScript) { another for when the incumbent is empty and new is not? and another for when both are empty?
Ready for another look. The diff against patchset 9 shows the changes from the reviewed version. I really appreciate your careful review especially as the patch is huge and you're also busy with Hiroki-san's patches! Your comments uncovered some bugs in the earlier patch: - (empty script) was considered != (empty script). Fixed by having comparer read info before bailing out on zero bytes read. - It was double-copying from disk and net when the comparer matched partially from net. Fixed by making the comparer only update bytes_matched_ for an entire net buffer. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (left): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:248: http_info_.reset(info_buffer_->http_info.release()); On 2015/06/04 02:57:25, michaeln wrote: > I'm not sure how intentional it was to defer setting http_into_ until > NotifyHeadersComplete time? > > As coded on trunk, callers sampling http_info_ backed data members > (charset,mimetype,responsecode) will see <nothing> until headers complete time. > In the CL, those values will be exposed prior to headers complete time. I'm not > sure if that matters, but I remember it not being accidental that it was coded > that way. Hmm.. yea. I suspect it's a good practice to set headers as an atomic commit, I see ServiceWorkerUrlRequestJob also does that. I've added CommitHeaders() which sets http_info_ then calls NotifyHeadersComplete. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:203: void HandleData(net::IOBuffer* buf, int size) override { On 2015/06/04 02:57:24, michaeln wrote: > What keeps |buf| alive here? Since the comparer can outlive the job, you might > need a scoped_refptr in this class. I think Comparer dies when the job dies since job owns it (via scoped_ptr). If Comparer dies its weak ptrs die so the callbacks don't run. So maybe it's technically ok, but perhaps it's foolproof to just use a scoped_refptr here. I'll do so. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:224: bool OnResponseEnded() override { On 2015/06/04 02:57:24, michaeln wrote: > The difference between ending syncrhously when Read immediately returns 0 vs > asyncly when zero is delivered to the completion callback is a nuisance. Agreed, I didn't like that either. I think I've been able to factor it out now. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:257: if (result == 0) { On 2015/06/04 02:57:24, michaeln wrote: > nit: smush these two ifs together if (result <= 0) Done. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:264: if (memcmp(pos_, io_buffer_->data(), result) != 0) { On 2015/06/04 02:57:24, michaeln wrote: > nit: having the term net_data in the first arg might help grokability (similarly > calling io_buffer read_buffer might help) Done. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:303: const char* pos_ = nullptr; On 2015/06/04 02:57:25, michaeln wrote: > keeping an net_data_offset_ instead of rawptrs might be safer/cleaner? Done. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc (right): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:360: ServiceWorkerRequestHandler::InitializeHandler( On 2015/06/04 02:57:25, michaeln wrote: > There's a lot of similarity between this dense setup code and that found in > SetUp(). I'd vote to factor out a common helper method that takes a few params > to handle any differences. Ah yeah that was bad copy+paste. Done. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:458: TEST_F(ServiceWorkerWriteToCacheJobTest, Update_SameSizeScript) { On 2015/06/04 02:57:25, michaeln wrote: > maybe 3, samesize test for the edge conditions? > - change at the start (first 16k block) > - change in the middle (not the first or last 16k block) > - change at the end (last 16k block) Great ideas, done. https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:485: TEST_F(ServiceWorkerWriteToCacheJobTest, Update_EmptyScript) { On 2015/06/04 02:57:25, michaeln wrote: > another for when the incumbent is empty and new is not? and another for when > both are empty? Great ideas and the empty -> empty test uncovered a bug.
lgtm! https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/140001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:224: bool OnResponseEnded() override { On 2015/06/04 14:11:11, falken wrote: > On 2015/06/04 02:57:24, michaeln wrote: > > The difference between ending syncrhously when Read immediately returns 0 vs > > asyncly when zero is delivered to the completion callback is a nuisance. > > Agreed, I didn't like that either. I think I've been able to factor it out now. Great. Fyi, this is why our ResponseReader|Writer classes always complete asyncly :) https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:278: // Completes the entire Comparer. nice comments https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:683: void ServiceWorkerWriteToCacheJob::CommitHeaders() { consider calling this method CommitHeadersAndNotifiyHeadersComplete https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:472: response[5555] = 'x'; Do you need to revert the change at [0] so only the char at [5555] is different than the incumbent? Ah... i see... UpdateScript stores the modified script so it then is the incumbent. That wasn't obvious :)
(lightweight review comments only, looking pretty good to me too & thanks to michaeln for careful review) https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:59: DCHECK_LT(0, bytes_to_copy_); super-tiny-nit: for these comparison dcheck's I prefer having constant on the second arg (i.e. DCHECK_GT(bytes_to_copy_, 0)) feels more natural when I read. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:83: if (result == 0) { nit: we do single <= check in the other similar class. should we do so here too? https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:761: // (headers + bytes matched) to disk. Do we come here too when reading net data has failed? Is continuing to pass-through consumer in that case intentional? https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.h (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.h:66: friend class Copier; nit: I don't think you need friend for these
Thanks for the review! https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:59: DCHECK_LT(0, bytes_to_copy_); On 2015/06/05 02:39:09, kinuko wrote: > super-tiny-nit: for these comparison dcheck's I prefer having constant on the > second arg (i.e. DCHECK_GT(bytes_to_copy_, 0)) feels more natural when I read. Interesting.. it feels easier for me to read LE/LT instead of GE/GT which is why I order like this. It also helps for lines like DCHECK_LE(0, net_data_offset_); DCHECK_LE(net_data_offset_, net_data_size_); to express 0 <= net_data_offset_ <= net_data_size. I think I'll just keep it this way, feels subjective. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:83: if (result == 0) { On 2015/06/05 02:39:09, kinuko wrote: > nit: we do single <= check in the other similar class. should we do so here too? Done. Yeah, I forgot this one. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:278: // Completes the entire Comparer. On 2015/06/05 02:07:04, michaeln wrote: > nice comments Acknowledged. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:683: void ServiceWorkerWriteToCacheJob::CommitHeaders() { On 2015/06/05 02:07:04, michaeln wrote: > consider calling this method CommitHeadersAndNotifiyHeadersComplete Done. A bit longer but more descriptive... https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:761: // (headers + bytes matched) to disk. On 2015/06/05 02:39:09, kinuko wrote: > Do we come here too when reading net data has failed? Is continuing to > pass-through consumer in that case intentional? No we shouldn't come here... OnCompareComplete() is only called by the Comparer, and the Comparer only runs on successful reading of net data. On net failure, the job should just quit. Let me know if you spot otherwise. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.h (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.h:66: friend class Copier; On 2015/06/05 02:39:10, kinuko wrote: > nit: I don't think you need friend for these Done. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc:472: response[5555] = 'x'; On 2015/06/05 02:07:04, michaeln wrote: > Do you need to revert the change at [0] so only the char at [5555] is different > than the incumbent? Ah... i see... UpdateScript stores the modified script so it > then is the incumbent. > > That wasn't obvious :) OK I've rearranged code to be more clear.
kinuko@chromium.org changed reviewers: + kinuko@chromium.org
(lgtm, fyi) https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:59: DCHECK_LT(0, bytes_to_copy_); On 2015/06/05 08:39:07, falken wrote: > On 2015/06/05 02:39:09, kinuko wrote: > > super-tiny-nit: for these comparison dcheck's I prefer having constant on the > > second arg (i.e. DCHECK_GT(bytes_to_copy_, 0)) feels more natural when I read. > > Interesting.. it feels easier for me to read LE/LT instead of GE/GT which is why > I order like this. It also helps for lines like > > DCHECK_LE(0, net_data_offset_); > DCHECK_LE(net_data_offset_, net_data_size_); > > to express 0 <= net_data_offset_ <= net_data_size. > > I think I'll just keep it this way, feels subjective. Sure, sorry for noise. https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:761: // (headers + bytes matched) to disk. On 2015/06/05 08:39:06, falken wrote: > On 2015/06/05 02:39:09, kinuko wrote: > > Do we come here too when reading net data has failed? Is continuing to > > pass-through consumer in that case intentional? > > No we shouldn't come here... OnCompareComplete() is only called by the Comparer, > and the Comparer only runs on successful reading of net data. On net failure, > the job should just quit. Let me know if you spot otherwise. No, my reading was wrong. It looks we come here for cache read error case though...? But in that case we'll overwrite the entry so should be ok? Hm, looks like.
Thanks! Think I'm going to CQ this. We also still call ServiceWorkerStorage::CompareScriptResources after this job finishes. I'll do a followup patch to remove that, maybe also an intermediate one checking that CompareScriptResources is almost always returning false (bc if the scripts are identical we abort, although there's some cases where it could happen, like an error when reading the incumbent). https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... File content/browser/service_worker/service_worker_write_to_cache_job.cc (right): https://codereview.chromium.org/1166433003/diff/220001/content/browser/servic... content/browser/service_worker/service_worker_write_to_cache_job.cc:761: // (headers + bytes matched) to disk. On 2015/06/05 08:56:49, kinuko wrote: > On 2015/06/05 08:39:06, falken wrote: > > On 2015/06/05 02:39:09, kinuko wrote: > > > Do we come here too when reading net data has failed? Is continuing to > > > pass-through consumer in that case intentional? > > > > No we shouldn't come here... OnCompareComplete() is only called by the > Comparer, > > and the Comparer only runs on successful reading of net data. On net failure, > > the job should just quit. Let me know if you spot otherwise. > > No, my reading was wrong. It looks we come here for cache read error case > though...? But in that case we'll overwrite the entry so should be ok? Hm, looks > like. Right, in that case the Comparer will say it wasn't able to match the bytes just read from net, so the Copier copies bytes previously matched from net, then PassThrough kicks in starting with the current net bytes. Everything's written to this cache job's own resource, not really overwriting anything.
The CQ bit was checked by falken@chromium.org
The patchset sent to the CQ was uploaded after l-g-t-m from nhiroki@chromium.org, michaeln@chromium.org Link to the patchset: https://codereview.chromium.org/1166433003/#ps240001 (title: "review comments")
CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/patch-status/1166433003/240001
Message was sent while issue was closed.
Committed patchset #13 (id:240001)
Message was sent while issue was closed.
Patchset 13 (id:??) landed as https://crrev.com/672f9b44c0176a503d0f996e457e3cd8db036c96 Cr-Commit-Position: refs/heads/master@{#333031} |