Update cache header after revalidation without rewriting the body data
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Feb 2015 12:41:52 +0000 (12:41 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Feb 2015 12:41:52 +0000 (12:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141182

Reviewed by Andreas Kling.

Currently we just rewrite the entire entry after revalidation.

* NetworkProcess/cache/NetworkCache.cpp:
(WebKit::decodeStorageEntry):

    Include the strorage entry to the cache response so we can more easily update it.

(WebKit::NetworkCache::update):

    Call the storage update function with the new and the exisiting storage entry.

* NetworkProcess/cache/NetworkCache.h:
* NetworkProcess/cache/NetworkCacheStorage.h:
* NetworkProcess/cache/NetworkCacheStorageCocoa.mm:
(WebKit::openFileForKey):

    Added an option for opening a file for writing without creating a new one.
    Renamed for clarity.

(WebKit::encodeEntryHeader):

    Separate header encoding to a function.

(WebKit::encodeEntry):
(WebKit::NetworkCacheStorage::dispatchRetrieveOperation):
(WebKit::NetworkCacheStorage::store):
(WebKit::NetworkCacheStorage::update):

    New update function.
    If the page-rounded header size would stay unchanged we can just write the new header over the old one.
    In the unlikely event it doesn't we rewrite the whole thing.

(WebKit::createIOChannelForKey): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@179546 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/ChangeLog
Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp
Source/WebKit2/NetworkProcess/cache/NetworkCache.h
Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h
Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm

index b2ef9f7..e1691c8 100644 (file)
@@ -1,3 +1,44 @@
+2015-02-02  Antti Koivisto  <antti@apple.com>
+
+        Update cache header after revalidation without rewriting the body data
+        https://bugs.webkit.org/show_bug.cgi?id=141182
+
+        Reviewed by Andreas Kling.
+
+        Currently we just rewrite the entire entry after revalidation.
+
+        * NetworkProcess/cache/NetworkCache.cpp:
+        (WebKit::decodeStorageEntry):
+
+            Include the strorage entry to the cache response so we can more easily update it.
+
+        (WebKit::NetworkCache::update):
+
+            Call the storage update function with the new and the exisiting storage entry.
+
+        * NetworkProcess/cache/NetworkCache.h:
+        * NetworkProcess/cache/NetworkCacheStorage.h:
+        * NetworkProcess/cache/NetworkCacheStorageCocoa.mm:
+        (WebKit::openFileForKey):
+
+            Added an option for opening a file for writing without creating a new one.
+            Renamed for clarity.
+
+        (WebKit::encodeEntryHeader):
+
+            Separate header encoding to a function.
+
+        (WebKit::encodeEntry):
+        (WebKit::NetworkCacheStorage::dispatchRetrieveOperation):
+        (WebKit::NetworkCacheStorage::store):
+        (WebKit::NetworkCacheStorage::update):
+
+            New update function.
+            If the page-rounded header size would stay unchanged we can just write the new header over the old one.
+            In the unlikely event it doesn't we rewrite the whole thing.
+
+        (WebKit::createIOChannelForKey): Deleted.
+
 2015-02-02  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r179540.
index cb038e6..3235767 100644 (file)
@@ -167,6 +167,7 @@ static std::unique_ptr<NetworkCache::Entry> decodeStorageEntry(const NetworkCach
     }
 
     auto entry = std::make_unique<NetworkCache::Entry>();
+    entry->storageEntry = storageEntry;
     entry->needsRevalidation = needsRevalidation;
 
     cachedResponse.setSource(needsRevalidation ? WebCore::ResourceResponse::Source::DiskCacheAfterValidation : WebCore::ResourceResponse::Source::DiskCache);
@@ -316,11 +317,10 @@ void NetworkCache::update(const WebCore::ResourceRequest& originalRequest, const
     WebCore::ResourceResponse response = entry.response;
     WebCore::updateResponseHeadersAfterRevalidation(response, validatingResponse);
 
-    // FIXME: This rewrites the entire resource instead of just the header.
     auto key = makeCacheKey(originalRequest);
-    auto storageEntry = encodeStorageEntry(originalRequest, response, entry.buffer);
+    auto updateEntry = encodeStorageEntry(originalRequest, response, entry.buffer);
 
-    m_storage->store(key, storageEntry, [](bool success) {
+    m_storage->update(key, updateEntry, entry.storageEntry, [](bool success) {
         LOG(NetworkCache, "(NetworkProcess) updated, success=%d", success);
     });
 }
index 5aa7b51..a705751 100644 (file)
@@ -53,6 +53,7 @@ public:
     bool isEnabled() const { return !!m_storage; }
 
     struct Entry {
+        NetworkCacheStorage::Entry storageEntry;
         WebCore::ResourceResponse response;
         RefPtr<WebCore::SharedBuffer> buffer;
 #if ENABLE(SHAREABLE_RESOURCE)
index 294613c..2a1153a 100644 (file)
@@ -134,6 +134,7 @@ public:
     // This may call completion handler synchronously on failure.
     void retrieve(const NetworkCacheKey&, unsigned priority, std::function<bool (std::unique_ptr<Entry>)>);
     void store(const NetworkCacheKey&, const Entry&, std::function<void (bool success)>);
+    void update(const NetworkCacheKey&, const Entry& updateEntry, const Entry& existingEntry, std::function<void (bool success)>);
 
     void setMaximumSize(size_t);
     void clear();
index 2f2380b..ff420ea 100644 (file)
@@ -161,19 +161,23 @@ static String filePathForKey(const NetworkCacheKey& key, const String& cachePath
     return WebCore::pathByAppendingComponent(directoryPathForKey(key, cachePath), key.hashAsString());
 }
 
-enum class IOChannelType { Read, Write };
-static DispatchPtr<dispatch_io_t> createIOChannelForKey(const NetworkCacheKey& key, IOChannelType type, const String& cachePath, int& fd)
+enum class FileOpenType { Read, Write, Create };
+static DispatchPtr<dispatch_io_t> openFileForKey(const NetworkCacheKey& key, FileOpenType type, const String& cachePath, int& fd)
 {
     int oflag;
     mode_t mode;
 
     switch (type) {
-    case IOChannelType::Write:
+    case FileOpenType::Create:
         oflag = O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK;
         mode = S_IRUSR | S_IWUSR;
         WebCore::makeAllDirectories(directoryPathForKey(key, cachePath));
         break;
-    case IOChannelType::Read:
+    case FileOpenType::Write:
+        oflag = O_WRONLY | O_NONBLOCK;
+        mode = S_IRUSR | S_IWUSR;
+        break;
+    case FileOpenType::Read:
         oflag = O_RDONLY | O_NONBLOCK;
         mode = 0;
     }
@@ -311,7 +315,7 @@ static DispatchPtr<dispatch_data_t> encodeEntryMetaData(const EntryMetaData& ent
     return adoptDispatch(dispatch_data_create(encoder.buffer(), encoder.bufferSize(), nullptr, DISPATCH_DATA_DESTRUCTOR_DEFAULT));
 }
 
-static DispatchPtr<dispatch_data_t> encodeEntry(const NetworkCacheKey& key, const NetworkCacheStorage::Entry& entry)
+static DispatchPtr<dispatch_data_t> encodeEntryHeader(const NetworkCacheKey& key, const NetworkCacheStorage::Entry& entry)
 {
     EntryMetaData metaData(key);
     metaData.timeStamp = entry.timeStamp;
@@ -329,10 +333,13 @@ static DispatchPtr<dispatch_data_t> encodeEntry(const NetworkCacheKey& key, cons
     size_t dataOffset = round_page(headerSize);
     Vector<uint8_t, 4096> filler(dataOffset - headerSize, 0);
     auto alignmentData = adoptDispatch(dispatch_data_create(filler.data(), filler.size(), nullptr, DISPATCH_DATA_DESTRUCTOR_DEFAULT));
+    return adoptDispatch(dispatch_data_create_concat(headerData.get(), alignmentData.get()));
+}
 
-    auto headerWithAlignmentData = adoptDispatch(dispatch_data_create_concat(headerData.get(), alignmentData.get()));
-
-    return adoptDispatch(dispatch_data_create_concat(headerWithAlignmentData.get(), entry.body.dispatchData()));
+static DispatchPtr<dispatch_data_t> encodeEntry(const NetworkCacheKey& key, const NetworkCacheStorage::Entry& entry)
+{
+    auto encodedHeader = encodeEntryHeader(key, entry);
+    return adoptDispatch(dispatch_data_create_concat(encodedHeader.get(), entry.body.dispatchData()));
 }
 
 void NetworkCacheStorage::removeEntry(const NetworkCacheKey& key)
@@ -360,7 +367,7 @@ void NetworkCacheStorage::dispatchRetrieveOperation(const RetrieveOperation& ret
     StringCapture cachePathCapture(m_directoryPath);
     dispatch_async(m_ioQueue.get(), [this, retrieve, cachePathCapture] {
         int fd;
-        auto channel = createIOChannelForKey(retrieve.key, IOChannelType::Read, cachePathCapture.string(), fd);
+        auto channel = openFileForKey(retrieve.key, FileOpenType::Read, cachePathCapture.string(), fd);
 
         bool didCallCompletionHandler = false;
         dispatch_io_read(channel.get(), 0, std::numeric_limits<size_t>::max(), dispatch_get_main_queue(), [this, fd, retrieve, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable {
@@ -428,8 +435,9 @@ void NetworkCacheStorage::store(const NetworkCacheKey& key, const Entry& entry,
     StringCapture cachePathCapture(m_directoryPath);
     dispatch_async(m_backgroundIOQueue.get(), [this, key, entry, cachePathCapture, completionHandler] {
         auto data = encodeEntry(key, entry);
+
         int fd;
-        auto channel = createIOChannelForKey(key, IOChannelType::Write, cachePathCapture.string(), fd);
+        auto channel = openFileForKey(key, FileOpenType::Create, cachePathCapture.string(), fd);
         dispatch_io_write(channel.get(), 0, data.get(), dispatch_get_main_queue(), [this, key, completionHandler](bool done, dispatch_data_t, int error) {
             ASSERT_UNUSED(done, done);
             LOG(NetworkCacheStorage, "(NetworkProcess) write complete error=%d", error);
@@ -443,6 +451,45 @@ void NetworkCacheStorage::store(const NetworkCacheKey& key, const Entry& entry,
     });
 }
 
+void NetworkCacheStorage::update(const NetworkCacheKey& key, const Entry& updateEntry, const Entry& existingEntry, std::function<void (bool success)> completionHandler)
+{
+    ASSERT(RunLoop::isMain());
+
+    if (!m_keyFilter.mayContain(key.hash())) {
+        LOG(NetworkCacheStorage, "(NetworkProcess) existing entry not found, storing full entry");
+        store(key, updateEntry, completionHandler);
+        return;
+    }
+
+    // Try to update the header of an existing entry.
+    StringCapture cachePathCapture(m_directoryPath);
+    dispatch_async(m_backgroundIOQueue.get(), [this, key, updateEntry, existingEntry, cachePathCapture, completionHandler] {
+        auto headerData = encodeEntryHeader(key, updateEntry);
+        auto existingHeaderData = encodeEntryHeader(key, existingEntry);
+
+        bool pageRoundedHeaderSizeChanged = dispatch_data_get_size(headerData.get()) != dispatch_data_get_size(existingHeaderData.get());
+        if (pageRoundedHeaderSizeChanged) {
+            LOG(NetworkCacheStorage, "(NetworkProcess) page-rounded header size changed, storing full entry");
+            dispatch_async(dispatch_get_main_queue(), [this, key, updateEntry, completionHandler] {
+                store(key, updateEntry, completionHandler);
+            });
+            return;
+        }
+
+        int fd;
+        auto channel = openFileForKey(key, FileOpenType::Write, cachePathCapture.string(), fd);
+        dispatch_io_write(channel.get(), 0, headerData.get(), dispatch_get_main_queue(), [this, key, completionHandler](bool done, dispatch_data_t, int error) {
+            ASSERT_UNUSED(done, done);
+            LOG(NetworkCacheStorage, "(NetworkProcess) update complete error=%d", error);
+
+            if (error)
+                removeEntry(key);
+
+            completionHandler(!error);
+        });
+    });
+}
+
 void NetworkCacheStorage::setMaximumSize(size_t size)
 {
     ASSERT(RunLoop::isMain());