IndexedDB: update size to actual disk usage only when estimated increase is bigger than space available
https://bugs.webkit.org/show_bug.cgi?id=201957

Reviewed by Youenn Fablet.

For better performance.

Covered by existing tests.

* Modules/indexeddb/server/IDBServer.cpp:
(WebCore::IDBServer::IDBServer::QuotaUser::resetSpaceUsed): m_estimatedSpaceIncrease will be updated when task
is finished, so no need to clear it when doing reset.
(WebCore::IDBServer::IDBServer::QuotaUser::computeSpaceUsed):
* Modules/indexeddb/server/IDBServer.h:

* Modules/indexeddb/server/UniqueIDBDatabase.cpp: instead of reading file size after each task, just add the
task size to known usage and get an estimated usage.
(WebCore::IDBServer::UniqueIDBDatabase::startSpaceIncreaseTask):
(WebCore::IDBServer::UniqueIDBDatabase::finishSpaceIncreaseTask):
(WebCore::IDBServer::UniqueIDBDatabase::performCurrentOpenOperation):
(WebCore::IDBServer::UniqueIDBDatabase::deleteBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::shutdownForClose):
(WebCore::IDBServer::UniqueIDBDatabase::didShutdownForClose):
(WebCore::IDBServer::UniqueIDBDatabase::didDeleteBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::generateUniqueCallbackIdentifier):
(WebCore::IDBServer::UniqueIDBDatabase::storeCallbackOrFireError):
(WebCore::IDBServer::UniqueIDBDatabase::openBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::didOpenBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::createObjectStoreAfterQuotaCheck):
(WebCore::IDBServer::UniqueIDBDatabase::didPerformCreateObjectStore):
(WebCore::IDBServer::UniqueIDBDatabase::renameObjectStoreAfterQuotaCheck):
(WebCore::IDBServer::UniqueIDBDatabase::didPerformRenameObjectStore):
(WebCore::IDBServer::UniqueIDBDatabase::createIndexAfterQuotaCheck):
(WebCore::IDBServer::UniqueIDBDatabase::didPerformCreateIndex):
(WebCore::IDBServer::UniqueIDBDatabase::renameIndexAfterQuotaCheck):
(WebCore::IDBServer::UniqueIDBDatabase::didPerformRenameIndex):
(WebCore::IDBServer::UniqueIDBDatabase::putOrAddAfterQuotaCheck):
(WebCore::IDBServer::UniqueIDBDatabase::didPerformPutOrAdd):
(WebCore::IDBServer::UniqueIDBDatabase::postDatabaseTaskReply):
(WebCore::IDBServer::UniqueIDBDatabase::immediateCloseForUserDelete):
(WebCore::IDBServer::UniqueIDBDatabase::performErrorCallback):
(WebCore::IDBServer::UniqueIDBDatabase::performKeyDataCallback):
(WebCore::IDBServer::UniqueIDBDatabase::forgetErrorCallback):
(WebCore::IDBServer::generateUniqueCallbackIdentifier): Deleted.
(WebCore::IDBServer::UniqueIDBDatabase::updateSpaceUsedIfNeeded): Deleted.
* Modules/indexeddb/server/UniqueIDBDatabase.h:

* storage/StorageQuotaManager.cpp: use m_state to decide what action to take on next request. Previously when a
request asked for size bigger than space available, we would go to ask for more space directly. Now because IDB
can be reporting estimated usage, we first ask IDB to compute its actual disk usage and ask for more space if
actual space available is smaller than requested size.
(WebCore::StorageQuotaManager::askUserToInitialize):
(WebCore::StorageQuotaManager::removeUser):
(WebCore::StorageQuotaManager::requestSpace):
(WebCore::StorageQuotaManager::askForMoreSpace):
(WebCore::StorageQuotaManager::processPendingRequests):
* storage/StorageQuotaManager.h:
* storage/StorageQuotaUser.h:
(WebCore::StorageQuotaUser::computeSpaceUsed):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@250937 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 07e1132..76f5eb8 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,67 @@
 2019-10-09  Sihui Liu  <sihui_liu@apple.com>
 
+        IndexedDB: update size to actual disk usage only when estimated increase is bigger than space available
+        https://bugs.webkit.org/show_bug.cgi?id=201957
+
+        Reviewed by Youenn Fablet.
+
+        For better performance.
+
+        Covered by existing tests.
+
+        * Modules/indexeddb/server/IDBServer.cpp:
+        (WebCore::IDBServer::IDBServer::QuotaUser::resetSpaceUsed): m_estimatedSpaceIncrease will be updated when task
+        is finished, so no need to clear it when doing reset.
+        (WebCore::IDBServer::IDBServer::QuotaUser::computeSpaceUsed):
+        * Modules/indexeddb/server/IDBServer.h:
+
+        * Modules/indexeddb/server/UniqueIDBDatabase.cpp: instead of reading file size after each task, just add the 
+        task size to known usage and get an estimated usage.
+        (WebCore::IDBServer::UniqueIDBDatabase::startSpaceIncreaseTask):
+        (WebCore::IDBServer::UniqueIDBDatabase::finishSpaceIncreaseTask):
+        (WebCore::IDBServer::UniqueIDBDatabase::performCurrentOpenOperation):
+        (WebCore::IDBServer::UniqueIDBDatabase::deleteBackingStore):
+        (WebCore::IDBServer::UniqueIDBDatabase::shutdownForClose):
+        (WebCore::IDBServer::UniqueIDBDatabase::didShutdownForClose):
+        (WebCore::IDBServer::UniqueIDBDatabase::didDeleteBackingStore):
+        (WebCore::IDBServer::UniqueIDBDatabase::generateUniqueCallbackIdentifier):
+        (WebCore::IDBServer::UniqueIDBDatabase::storeCallbackOrFireError):
+        (WebCore::IDBServer::UniqueIDBDatabase::openBackingStore):
+        (WebCore::IDBServer::UniqueIDBDatabase::didOpenBackingStore):
+        (WebCore::IDBServer::UniqueIDBDatabase::createObjectStoreAfterQuotaCheck):
+        (WebCore::IDBServer::UniqueIDBDatabase::didPerformCreateObjectStore):
+        (WebCore::IDBServer::UniqueIDBDatabase::renameObjectStoreAfterQuotaCheck):
+        (WebCore::IDBServer::UniqueIDBDatabase::didPerformRenameObjectStore):
+        (WebCore::IDBServer::UniqueIDBDatabase::createIndexAfterQuotaCheck):
+        (WebCore::IDBServer::UniqueIDBDatabase::didPerformCreateIndex):
+        (WebCore::IDBServer::UniqueIDBDatabase::renameIndexAfterQuotaCheck):
+        (WebCore::IDBServer::UniqueIDBDatabase::didPerformRenameIndex):
+        (WebCore::IDBServer::UniqueIDBDatabase::putOrAddAfterQuotaCheck):
+        (WebCore::IDBServer::UniqueIDBDatabase::didPerformPutOrAdd):
+        (WebCore::IDBServer::UniqueIDBDatabase::postDatabaseTaskReply):
+        (WebCore::IDBServer::UniqueIDBDatabase::immediateCloseForUserDelete):
+        (WebCore::IDBServer::UniqueIDBDatabase::performErrorCallback):
+        (WebCore::IDBServer::UniqueIDBDatabase::performKeyDataCallback):
+        (WebCore::IDBServer::UniqueIDBDatabase::forgetErrorCallback):
+        (WebCore::IDBServer::generateUniqueCallbackIdentifier): Deleted.
+        (WebCore::IDBServer::UniqueIDBDatabase::updateSpaceUsedIfNeeded): Deleted.
+        * Modules/indexeddb/server/UniqueIDBDatabase.h:
+
+        * storage/StorageQuotaManager.cpp: use m_state to decide what action to take on next request. Previously when a 
+        request asked for size bigger than space available, we would go to ask for more space directly. Now because IDB
+        can be reporting estimated usage, we first ask IDB to compute its actual disk usage and ask for more space if 
+        actual space available is smaller than requested size.
+        (WebCore::StorageQuotaManager::askUserToInitialize):
+        (WebCore::StorageQuotaManager::removeUser):
+        (WebCore::StorageQuotaManager::requestSpace):
+        (WebCore::StorageQuotaManager::askForMoreSpace):
+        (WebCore::StorageQuotaManager::processPendingRequests):
+        * storage/StorageQuotaManager.h:
+        * storage/StorageQuotaUser.h:
+        (WebCore::StorageQuotaUser::computeSpaceUsed):
+
+2019-10-09  Sihui Liu  <sihui_liu@apple.com>
+
         IndexedDB: include size of index records in size estimate of put/add task
         https://bugs.webkit.org/show_bug.cgi?id=202483
 
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
index b59ed7f..428aa80 100644
--- a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
@@ -712,7 +712,6 @@
 void IDBServer::QuotaUser::resetSpaceUsed()
 {
     m_spaceUsed = 0;
-    m_estimatedSpaceIncrease = 0;
 
     if (!m_manager)
         return;
@@ -732,6 +731,11 @@
     m_manager->addUser(*this);
 }
 
+void IDBServer::QuotaUser::computeSpaceUsed()
+{
+    resetSpaceUsed();
+}
+
 void IDBServer::QuotaUser::increaseSpaceUsed(uint64_t size)
 {
     if (!m_isInitialized)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.h b/Source/WebCore/Modules/indexeddb/server/IDBServer.h
index 19e6962..5aa3db5 100644
--- a/Source/WebCore/Modules/indexeddb/server/IDBServer.h
+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.h
@@ -167,7 +167,12 @@
         void initializeSpaceUsed(uint64_t spaceUsed);
 
     private:
-        uint64_t spaceUsed() const final { return m_spaceUsed + m_estimatedSpaceIncrease; }
+        uint64_t spaceUsed() const final
+        {
+            ASSERT(m_isInitialized);
+            return m_spaceUsed + m_estimatedSpaceIncrease;
+        }
+        void computeSpaceUsed() final;
         void whenInitialized(CompletionHandler<void()>&&) final;
 
         IDBServer& m_server;
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
index 8a635bd..06e956c 100644
--- a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
@@ -219,6 +219,25 @@
     requestSpace(transaction, 0, "", WTFMove(callback));
 }
 
+void UniqueIDBDatabase::startSpaceIncreaseTask(uint64_t identifier, uint64_t taskSize)
+{
+    m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize);
+    ASSERT(!m_pendingSpaceIncreaseTasks.contains(identifier));
+    m_pendingSpaceIncreaseTasks.add(identifier, taskSize);
+}
+
+void UniqueIDBDatabase::finishSpaceIncreaseTask(uint64_t identifier, bool isTaskSuccessful)
+{
+    auto iterator = m_pendingSpaceIncreaseTasks.find(identifier);
+    ASSERT(iterator != m_pendingSpaceIncreaseTasks.end() || !isTaskSuccessful);
+    if (iterator != m_pendingSpaceIncreaseTasks.end()) {
+        m_server->decreasePotentialSpaceUsed(m_identifier.origin(), iterator->value);
+        if (isTaskSuccessful)
+            m_server->increaseSpaceUsed(m_identifier.origin(), iterator->value);
+        m_pendingSpaceIncreaseTasks.remove(iterator);
+    }
+}
+
 void UniqueIDBDatabase::performCurrentOpenOperation()
 {
     LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation (%p)", this);
@@ -247,7 +266,9 @@
                     break;
                 }
                 case StorageQuotaManager::Decision::Grant:
-                    this->postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
+                    auto callbackID = this->generateUniqueCallbackIdentifier();
+                    startSpaceIncreaseTask(callbackID, defaultWriteOperationCost);
+                    this->postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier, callbackID));
                 };
             });
         }
@@ -360,14 +381,12 @@
 
     if (m_backingStore) {
         m_backingStore->deleteBackingStore();
-        m_newDatabaseSize = m_backingStore->databaseSize();
 
         m_backingStore = nullptr;
         m_backingStoreSupportsSimultaneousTransactions = false;
         m_backingStoreIsEphemeral = false;
     } else {
         auto backingStore = m_server->createBackingStore(identifier);
-        m_currentDatabaseSize = backingStore->databaseSize();
 
         IDBDatabaseInfo databaseInfo;
         auto error = backingStore->getOrEstablishDatabaseInfo(databaseInfo);
@@ -376,7 +395,6 @@
 
         deletedVersion = databaseInfo.version();
         backingStore->deleteBackingStore();
-        m_newDatabaseSize = backingStore->databaseSize();
     }
 
     postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteBackingStore, deletedVersion));
@@ -412,10 +430,8 @@
 
     LOG(IndexedDB, "(db) UniqueIDBDatabase::shutdownForClose");
 
-    if (m_backingStore) {
+    if (m_backingStore)
         m_backingStore->close();
-        m_newDatabaseSize = m_backingStore->databaseSize();
-    }
 
     m_backingStore = nullptr;
     m_backingStoreSupportsSimultaneousTransactions = false;
@@ -434,8 +450,6 @@
 {
     ASSERT(m_databaseReplyQueue.isEmpty());
     m_databaseReplyQueue.kill();
-
-    updateSpaceUsedIfNeeded();
 }
 
 void UniqueIDBDatabase::didDeleteBackingStore(uint64_t deletedVersion)
@@ -467,8 +481,6 @@
         m_currentOpenDBRequest = nullptr;
     }
 
-    updateSpaceUsedIfNeeded();
-
     m_deleteBackingStoreInProgress = false;
 
     if (m_hardClosedForUserDelete)
@@ -549,14 +561,14 @@
     return true;
 }
 
-static uint64_t generateUniqueCallbackIdentifier()
+uint64_t UniqueIDBDatabase::generateUniqueCallbackIdentifier()
 {
     ASSERT(isMainThread());
     static uint64_t currentID = 0;
     return ++currentID;
 }
 
-uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback&& callback, uint64_t taskSize)
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback&& callback)
 {
     if (m_hardClosedForUserDelete) {
         callback(IDBError::userDeleteError());
@@ -567,16 +579,11 @@
     ASSERT(!m_errorCallbacks.contains(identifier));
     m_errorCallbacks.add(identifier, WTFMove(callback));
 
-    if (taskSize) {
-        m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize);
-        m_pendingSpaceIncreasingTasks.add(identifier, taskSize);
-    }
-
     m_callbackQueue.append(identifier);
     return identifier;
 }
 
-uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback&& callback, uint64_t taskSize)
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback&& callback)
 {
     if (m_hardClosedForUserDelete) {
         callback(IDBError::userDeleteError(), { });
@@ -587,11 +594,6 @@
     ASSERT(!m_keyDataCallbacks.contains(identifier));
     m_keyDataCallbacks.add(identifier, WTFMove(callback));
 
-    if (taskSize) {
-        m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize);
-        m_pendingSpaceIncreasingTasks.add(identifier, taskSize);
-    }
-
     m_callbackQueue.append(identifier);
     return identifier;
 }
@@ -792,7 +794,7 @@
     m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
 }
 
-void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
+void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier, uint64_t taskIdentifier)
 {
     ASSERT(!isMainThread());
     LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore (%p)", this);
@@ -802,17 +804,13 @@
     m_backingStoreSupportsSimultaneousTransactions = m_backingStore->supportsSimultaneousTransactions();
     m_backingStoreIsEphemeral = m_backingStore->isEphemeral();
 
-    // QuotaUser should have initiliazed storage usage, which contains the
-    // size of this database.
-    m_currentDatabaseSize = m_backingStore->databaseSize();
-
     IDBDatabaseInfo databaseInfo;
     auto error = m_backingStore->getOrEstablishDatabaseInfo(databaseInfo);
 
-    postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo, error));
+    postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo, error,  taskIdentifier));
 }
 
-void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info, const IDBError& error)
+void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info, const IDBError& error, uint64_t taskIdentifier)
 {
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
@@ -823,7 +821,7 @@
     ASSERT(m_isOpeningBackingStore);
     m_isOpeningBackingStore = false;
 
-    updateSpaceUsedIfNeeded();
+    finishSpaceIncreaseTask(taskIdentifier, error.isNull());
 
     if (m_hardClosedForUserDelete)
         return;
@@ -848,10 +846,11 @@
 
 void UniqueIDBDatabase::createObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback, const IDBError& quotaError)
 {
-    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
+    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
     if (!callbackID)
         return;
 
+    startSpaceIncreaseTask(callbackID, taskSize);
     postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateObjectStore, callbackID, transaction.info().identifier(), info,  quotaError));
 }
 
@@ -877,6 +876,7 @@
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateObjectStore");
 
+    finishSpaceIncreaseTask(callbackIdentifier, error.isNull());
     if (error.isNull())
         m_databaseInfo->addExistingObjectStore(info);
 
@@ -952,7 +952,7 @@
 
 void UniqueIDBDatabase::renameObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback callback, const IDBError& quotaError)
 {
-    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
+    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
     if (!callbackID)
         return;
 
@@ -961,6 +961,7 @@
     if (!info)
         error = IDBError { UnknownError, "Attempt to rename non-existant object store"_s };
 
+    startSpaceIncreaseTask(callbackID, taskSize);
     postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier, newName, error));
 }
 
@@ -985,6 +986,7 @@
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameObjectStore");
 
+    finishSpaceIncreaseTask(callbackIdentifier, error.isNull());
     if (error.isNull())
         m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName);
 
@@ -1050,9 +1052,11 @@
 
 void UniqueIDBDatabase::createIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, const IDBIndexInfo& info, ErrorCallback callback, const IDBError& quotaError)
 {
-    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
+    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
     if (!callbackID)
         return;
+
+    startSpaceIncreaseTask(callbackID, taskSize);
     postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateIndex, callbackID, transaction.info().identifier(), info, quotaError));
 }
 
@@ -1084,6 +1088,8 @@
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateIndex");
 
+    finishSpaceIncreaseTask(callbackIdentifier, error.isNull());
+
     if (error.isNull()) {
         ASSERT(m_databaseInfo);
         auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier());
@@ -1172,7 +1178,7 @@
 
 void UniqueIDBDatabase::renameIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback callback, const IDBError& quotaError)
 {
-    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
+    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
     if (!callbackID)
         return;
 
@@ -1185,6 +1191,7 @@
     if (!indexInfo)
         error = IDBError { UnknownError, "Attempt to rename non-existant index"_s };
 
+    startSpaceIncreaseTask(callbackID, taskSize);
     postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexIdentifier, newName, error));
 }
 
@@ -1209,6 +1216,7 @@
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameIndex");
 
+    finishSpaceIncreaseTask(callbackIdentifier, error.isNull());
     if (error.isNull()) {
         auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
         ASSERT(objectStoreInfo);
@@ -1244,9 +1252,11 @@
 
 void UniqueIDBDatabase::putOrAddAfterQuotaCheck(uint64_t taskSize, const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback, const IDBError& quotaError)
 {
-    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
+    uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
     if (!callbackID)
         return;
+
+    startSpaceIncreaseTask(callbackID, taskSize);
     postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPutOrAdd, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData, value, overwriteMode, quotaError));
 }
 
@@ -1339,6 +1349,7 @@
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformPutOrAdd");
 
+    finishSpaceIncreaseTask(callbackIdentifier, error.isNull());
     performKeyDataCallback(callbackIdentifier, error, resultKey);
 }
 
@@ -2133,9 +2144,6 @@
 
 void UniqueIDBDatabase::postDatabaseTaskReply(CrossThreadTask&& task)
 {
-    if (m_backingStore)
-        m_newDatabaseSize = m_backingStore->databaseSize();
-
     m_databaseReplyQueue.append(WTFMove(task));
     m_server->postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTaskReply));
 }
@@ -2200,8 +2208,9 @@
 
     ASSERT(isMainThread());
 
-    m_pendingSpaceIncreasingTasks.clear();
-    m_server->resetSpaceUsed(m_identifier.origin());
+    for (auto size : m_pendingSpaceIncreaseTasks.values())
+        m_server->decreasePotentialSpaceUsed(m_identifier.origin(), size);
+    m_pendingSpaceIncreaseTasks.clear();
 
     // Error out all transactions
     for (auto& identifier : copyToVector(m_inProgressTransactions.keys()))
@@ -2277,33 +2286,8 @@
     postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performUnconditionalDeleteBackingStore));
 }
 
-void UniqueIDBDatabase::updateSpaceUsedIfNeeded(Optional<uint64_t> optionalCallbackIdentifier)
-{
-    ASSERT(isMainThread());
-
-    if (optionalCallbackIdentifier) {
-        uint64_t callbackIdentifier = optionalCallbackIdentifier.value();
-        auto iterator = m_pendingSpaceIncreasingTasks.find(callbackIdentifier);
-        if (iterator != m_pendingSpaceIncreasingTasks.end()) {
-            m_server->decreasePotentialSpaceUsed(m_identifier.origin(), iterator->value);
-            m_pendingSpaceIncreasingTasks.remove(iterator);
-        }
-    }
-
-    uint64_t databaseSize = m_newDatabaseSize;
-    if (databaseSize != m_currentDatabaseSize) {
-        if (databaseSize > m_currentDatabaseSize)
-            m_server->increaseSpaceUsed(m_identifier.origin(), databaseSize - m_currentDatabaseSize);
-        else
-            m_server->decreaseSpaceUsed(m_identifier.origin(), m_currentDatabaseSize - databaseSize);
-        m_currentDatabaseSize = databaseSize;
-    }
-}
-
 void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
 {
-    updateSpaceUsedIfNeeded(callbackIdentifier);
-
     auto callback = m_errorCallbacks.take(callbackIdentifier);
     ASSERT(callback || m_hardClosedForUserDelete);
     if (callback) {
@@ -2315,8 +2299,6 @@
 
 void UniqueIDBDatabase::performKeyDataCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
 {
-    updateSpaceUsedIfNeeded(callbackIdentifier);
-
     auto callback = m_keyDataCallbacks.take(callbackIdentifier);
     ASSERT(callback || m_hardClosedForUserDelete);
     if (callback) {
@@ -2361,8 +2343,6 @@
 
 void UniqueIDBDatabase::forgetErrorCallback(uint64_t callbackIdentifier)
 {
-    updateSpaceUsedIfNeeded(callbackIdentifier);
-
     ASSERT(m_errorCallbacks.contains(callbackIdentifier));
     ASSERT(m_callbackQueue.last() == callbackIdentifier);
     m_callbackQueue.removeLast();
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h
index 141801d..cac2708 100644
--- a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h
@@ -116,8 +116,6 @@
 
     bool hardClosedForUserDelete() const { return m_hardClosedForUserDelete; }
 
-    uint64_t spaceUsed() const;
-
     void finishActiveTransactions();
 
 private:
@@ -160,7 +158,7 @@
 
     // Database thread operations
     void deleteBackingStore(const IDBDatabaseIdentifier&);
-    void openBackingStore(const IDBDatabaseIdentifier&);
+    void openBackingStore(const IDBDatabaseIdentifier&, uint64_t taskIdentifier);
     void performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier);
     void performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier);
     void beginTransactionInBackingStore(const IDBTransactionInfo&);
@@ -188,7 +186,7 @@
 
     // Main thread callbacks
     void didDeleteBackingStore(uint64_t deletedVersion);
-    void didOpenBackingStore(const IDBDatabaseInfo&, const IDBError&);
+    void didOpenBackingStore(const IDBDatabaseInfo&, const IDBError&, uint64_t taskIdentifier);
     void didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError&, const IDBObjectStoreInfo&);
     void didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier);
     void didPerformRenameObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, const String& newName);
@@ -211,8 +209,8 @@
     void didPerformUnconditionalDeleteBackingStore();
     void didShutdownForClose();
 
-    uint64_t storeCallbackOrFireError(ErrorCallback&&, uint64_t taskSize = 0);
-    uint64_t storeCallbackOrFireError(KeyDataCallback&&, uint64_t taskSize = 0);
+    uint64_t storeCallbackOrFireError(ErrorCallback&&);
+    uint64_t storeCallbackOrFireError(KeyDataCallback&&);
     uint64_t storeCallbackOrFireError(GetAllResultsCallback&&);
     uint64_t storeCallbackOrFireError(GetResultCallback&&);
     uint64_t storeCallbackOrFireError(CountCallback&&);
@@ -249,7 +247,10 @@
 
     void requestSpace(UniqueIDBDatabaseTransaction&, uint64_t taskSize, const char* errorMessage, CompletionHandler<void(IDBError&&)>&&);
     void waitForRequestSpaceCompletion(UniqueIDBDatabaseTransaction&, CompletionHandler<void(IDBError&&)>&&);
-    void updateSpaceUsedIfNeeded(Optional<uint64_t> optionalCallbackIdentifier = WTF::nullopt);
+    void startSpaceIncreaseTask(uint64_t identifier, uint64_t taskSize);
+    void finishSpaceIncreaseTask(uint64_t identifier, bool isTaskSuccessful);
+
+    static uint64_t generateUniqueCallbackIdentifier();
 
     Ref<IDBServer> m_server;
     IDBDatabaseIdentifier m_identifier;
@@ -302,9 +303,7 @@
 
     HashSet<IDBResourceIdentifier> m_cursorPrefetches;
 
-    HashMap<uint64_t, uint64_t> m_pendingSpaceIncreasingTasks;
-    uint64_t m_currentDatabaseSize { 0 };
-    uint64_t m_newDatabaseSize { 0 };
+    HashMap<uint64_t, uint64_t> m_pendingSpaceIncreaseTasks;
 };
 
 } // namespace IDBServer
diff --git a/Source/WebCore/storage/StorageQuotaManager.cpp b/Source/WebCore/storage/StorageQuotaManager.cpp
index 767ad2e..8525752 100644
--- a/Source/WebCore/storage/StorageQuotaManager.cpp
+++ b/Source/WebCore/storage/StorageQuotaManager.cpp
@@ -84,8 +84,13 @@
         if (!m_pendingInitializationUsers.isEmpty())
             return;
 
-        updateQuotaBasedOnSpaceUsage();
-        processPendingRequests({ }, ShouldDequeueFirstPendingRequest::No);
+        // Make sure quota is set before handling first request.
+        if (m_state == State::Uninitialized) {
+            updateQuotaBasedOnSpaceUsage();
+            m_state = State::MakingDecisionForRequest;
+        }
+
+        processPendingRequests({ });
     });
 }
 
@@ -119,7 +124,7 @@
                 return;
 
             if (m_pendingInitializationUsers.isEmpty())
-                this->processPendingRequests({ }, ShouldDequeueFirstPendingRequest::No);
+                this->processPendingRequests({ });
         });
     }
 }
@@ -145,32 +150,43 @@
 
     if (shouldAskForMoreSpace(spaceIncrease)) {
         m_pendingRequests.append({ spaceIncrease, WTFMove(callback) });
+
+        // Try processing request again after making sure usage is accurate.
+        m_state = State::ComputingSpaceUsed;
+        for (auto& user : copyToVector(m_users))
+            user->computeSpaceUsed();
+
+        if (!m_pendingInitializationUsers.isEmpty())
+            return;
+
+        m_state = State::AskingForMoreSpace;
         askForMoreSpace(spaceIncrease);
         return;
     }
 
+    m_state = State::MakingDecisionForRequest;
     callback(Decision::Grant);
 }
 
 void StorageQuotaManager::askForMoreSpace(uint64_t spaceIncrease)
 {
     ASSERT(shouldAskForMoreSpace(spaceIncrease));
-    ASSERT(!m_isWaitingForSpaceIncreaseResponse);
+    ASSERT(m_state == State::AskingForMoreSpace);
 
     RELEASE_LOG(Storage, "%p - StorageQuotaManager::askForMoreSpace %" PRIu64, this, spaceIncrease);
-    m_isWaitingForSpaceIncreaseResponse = true;
+    m_state = State::WaitingForSpaceIncreaseResponse;
     m_spaceIncreaseRequester(m_quota, spaceUsage(), spaceIncrease, [this, weakThis = makeWeakPtr(*this)](Optional<uint64_t> newQuota) {
         if (!weakThis)
             return;
 
         RELEASE_LOG(Storage, "%p - StorageQuotaManager::askForMoreSpace received response %" PRIu64, this, newQuota ? *newQuota : 0);
 
-        m_isWaitingForSpaceIncreaseResponse = false;
-        processPendingRequests(newQuota, ShouldDequeueFirstPendingRequest::Yes);
+        m_state = State::AskingForMoreSpace;
+        processPendingRequests(newQuota);
     });
 }
 
-void StorageQuotaManager::processPendingRequests(Optional<uint64_t> newQuota, ShouldDequeueFirstPendingRequest shouldDequeueFirstPendingRequest)
+void StorageQuotaManager::processPendingRequests(Optional<uint64_t> newQuota)
 {
     if (m_pendingRequests.isEmpty())
         return;
@@ -178,18 +194,19 @@
     if (newQuota)
         m_quota = *newQuota;
 
-    if (m_isWaitingForSpaceIncreaseResponse)
+    if (m_state == State::WaitingForSpaceIncreaseResponse)
         return;
 
     if (!m_pendingInitializationUsers.isEmpty())
         return;
 
-    if (shouldDequeueFirstPendingRequest == ShouldDequeueFirstPendingRequest::Yes) {
+    if (m_state == State::AskingForMoreSpace) {
         auto request = m_pendingRequests.takeFirst();
         bool shouldAllowRequest = !shouldAskForMoreSpace(request.spaceIncrease);
 
         RELEASE_LOG(Storage, "%p - StorageQuotaManager::processPendingRequests first request decision is %d", this, shouldAllowRequest);
 
+        m_state = State::MakingDecisionForRequest;
         request.callback(shouldAllowRequest ? Decision::Grant : Decision::Deny);
     }
 
@@ -197,13 +214,24 @@
         auto& request = m_pendingRequests.first();
 
         if (shouldAskForMoreSpace(request.spaceIncrease)) {
+            if (m_state == State::MakingDecisionForRequest) {
+                m_state = State::ComputingSpaceUsed;
+                for (auto& user : copyToVector(m_users))
+                    user->computeSpaceUsed();
+
+                if (!m_pendingInitializationUsers.isEmpty())
+                    return;
+            }
+
+            m_state = State::AskingForMoreSpace;
             uint64_t spaceIncrease = 0;
-            for (auto& request : m_pendingRequests)
-                spaceIncrease += request.spaceIncrease;
+            for (auto& pendingRequest : m_pendingRequests)
+                spaceIncrease += pendingRequest.spaceIncrease;
             askForMoreSpace(spaceIncrease);
             return;
         }
 
+        m_state = State::MakingDecisionForRequest;
         m_pendingRequests.takeFirst().callback(Decision::Grant);
     }
 }
diff --git a/Source/WebCore/storage/StorageQuotaManager.h b/Source/WebCore/storage/StorageQuotaManager.h
index 9f64633..5ce3a22 100644
--- a/Source/WebCore/storage/StorageQuotaManager.h
+++ b/Source/WebCore/storage/StorageQuotaManager.h
@@ -70,23 +70,30 @@
     void initializeUsersIfNeeded();
     void askUserToInitialize(StorageQuotaUser&);
 
-    enum class ShouldDequeueFirstPendingRequest { No, Yes };
-    void processPendingRequests(Optional<uint64_t>, ShouldDequeueFirstPendingRequest);
+    void processPendingRequests(Optional<uint64_t>);
 
     uint64_t m_quota { 0 };
 
-    bool m_isWaitingForSpaceIncreaseResponse { false };
     SpaceIncreaseRequester m_spaceIncreaseRequester;
 
     enum class WhenInitializedCalled { No, Yes };
     HashMap<StorageQuotaUser*, WhenInitializedCalled> m_pendingInitializationUsers;
-    HashSet<const StorageQuotaUser*> m_users;
+    HashSet<StorageQuotaUser*> m_users;
 
     struct PendingRequest {
         uint64_t spaceIncrease;
         RequestCallback callback;
     };
     Deque<PendingRequest> m_pendingRequests;
+
+    enum class State {
+        Uninitialized,
+        ComputingSpaceUsed,
+        WaitingForSpaceIncreaseResponse,
+        AskingForMoreSpace,
+        MakingDecisionForRequest,
+    };
+    State m_state { State::Uninitialized };
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/storage/StorageQuotaUser.h b/Source/WebCore/storage/StorageQuotaUser.h
index b4230c5..b710cee 100644
--- a/Source/WebCore/storage/StorageQuotaUser.h
+++ b/Source/WebCore/storage/StorageQuotaUser.h
@@ -34,6 +34,7 @@
     virtual ~StorageQuotaUser() = default;
 
     virtual uint64_t spaceUsed() const = 0;
+    virtual void computeSpaceUsed() { };
     virtual void whenInitialized(CompletionHandler<void()>&& callback) { callback(); }
 };