IndexedDB: prefetch cursor records on client side
authorsihui_liu@apple.com <sihui_liu@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Feb 2020 18:08:03 +0000 (18:08 +0000)
committersihui_liu@apple.com <sihui_liu@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Feb 2020 18:08:03 +0000 (18:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=207602
<rdar://problem/58483927>

Reviewed by Brady Eidson.

Cache cursor records on client side and use those records for iterate operations.

This makes cursor continue/advance tests in PerformanceTests/IndexedDB/basics ~3x faster.

* Headers.cmake:

* Modules/indexeddb/IDBCursor.cpp:
(WebCore::IDBCursor::setGetResult): Record the ID of TransactionOperation that updates cached record.
(WebCore::IDBCursor::iterateWithPrefetchedRecords): IDBCursor uses cached records for iterate opertaions if
there is no write operation between last cached records update and current iteration operation.
(WebCore::IDBCursor::clearPrefetchedRecords):
* Modules/indexeddb/IDBCursor.h:

* Modules/indexeddb/IDBGetResult.cpp:
(WebCore::IDBGetResult::isolatedCopy):
* Modules/indexeddb/IDBGetResult.h:
(WebCore::IDBGetResult::IDBGetResult):
(WebCore::IDBGetResult::prefetchedRecords const):
(WebCore::IDBGetResult::encode const):
(WebCore::IDBGetResult::decode):

* Modules/indexeddb/IDBRequest.cpp: Record corresponding TransactionOperation ID in request.
(WebCore::IDBRequest::didOpenOrIterateCursor):
* Modules/indexeddb/IDBRequest.h:
(WebCore::IDBRequest::pendingCursor const):
(WebCore::IDBRequest::setTransactionOperationID):

* Modules/indexeddb/IDBTransaction.cpp: If a cursor iterate request can be handled with cached records,
IDBClient does not need to send request to IDBServer and wait for response. But requests before that iterate
request may need to wait server to answer, and spec requires to handle requests in order. Therefore, we now keep
all the results in m_transactionOperationResultMap and handle them according to the ordering in
m_transactionOperationsInProgressQueue.
(WebCore::IDBTransaction::abortInProgressOperations):
(WebCore::IDBTransaction::removeRequest): Because result of a cursor request can be answered sooner, it is
possible that in finishedDispatchEventForRequest, m_currentlyCompletingRequest (which is a cursor request) is
set to nullptr, and then it is set back to the same cursor request in handleOperationsCompletedOnServer right
after. This happens in dispatchEvent of the cursor request, where request would remove itself from request list
of transaction at the end.
In this case, when request list becomes empty, transaction may commit automatically. But transaction should not
because that request is still valid as m_currentlyCompletingRequest and should not be removed from list.
(WebCore::IDBTransaction::scheduleOperation):
(WebCore::IDBTransaction::operationCompletedOnServer):
(WebCore::IDBTransaction::handleOperationsCompletedOnServer): All requests were sent to IDBServer and the
response/result ordering would naturally be the same as request order. Now that results can be created in both
IDBClient and IDBServer, we no longer handle requests using m_completedOnServerQueue.
(WebCore::IDBTransaction::createObjectStore): Mark as write operation to track last write operation.
(WebCore::IDBTransaction::renameObjectStore): Ditto.
(WebCore::IDBTransaction::createIndex): Ditto.
(WebCore::IDBTransaction::renameIndex): Ditto.
(WebCore::IDBTransaction::requestDeleteRecord): Ditto.
(WebCore::IDBTransaction::requestClearObjectStore): Ditto.
(WebCore::IDBTransaction::requestPutOrAdd): Ditto.
(WebCore::IDBTransaction::deleteObjectStore): Ditto.
(WebCore::IDBTransaction::deleteIndex): Ditto.
(WebCore::IDBTransaction::iterateCursorOnServer): Only use cached records if the request does not specify target
key. If cursor is iterated successfully with cache, send a message to IDBServer to notify about the progress.
Otherwise, fall back to depend on IDBServer to answer the request.
(WebCore::IDBTransaction::generateOperationID): TransactionOperation ID is unique in a transaction.
* Modules/indexeddb/IDBTransaction.h:

* Modules/indexeddb/IndexedDB.h:
* Modules/indexeddb/client/IDBConnectionProxy.cpp:
(WebCore::IDBClient::IDBConnectionProxy::iterateCursor): Don't track result of TransactionOperation in
IDBConnectionProxy if TransactionOperation does not need a reply from IDBServer.

* Modules/indexeddb/client/TransactionOperation.cpp:
(WebCore::IDBClient::TransactionOperation::TransactionOperation):
* Modules/indexeddb/client/TransactionOperation.h: Add ID to TransactionOperation.
(WebCore::IDBClient::TransactionOperation::operationID const):
(WebCore::IDBClient::TransactionOperation::TransactionOperation):

* Modules/indexeddb/server/IDBBackingStore.h: remove prefetchCursor as it is not used now.
* Modules/indexeddb/server/MemoryIDBBackingStore.h: Ditto. Also this patch only deals with prefetching in
persistent store.
* Modules/indexeddb/server/MemoryIDBBackingStore.h:
* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::createIndex):
(WebCore::IDBServer::SQLiteIDBBackingStore::getAllIndexRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::getIndexRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::iterateCursor): Only prefetch at when request needs to be answered.
(WebCore::IDBServer::SQLiteIDBBackingStore::prefetchCursor): Deleted.
* Modules/indexeddb/server/SQLiteIDBBackingStore.h:

* Modules/indexeddb/server/SQLiteIDBCursor.cpp:
(WebCore::IDBServer::SQLiteIDBCursor::currentData): Provide an option to include prefetched records in result.
(WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged): Reset count to prefetch when there is a
change. This is used with increaseCountToPrefetch to make prefetch adaptive.
(WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged):
(WebCore::IDBServer::SQLiteIDBCursor::prefetchOneRecord):
(WebCore::IDBServer::SQLiteIDBCursor::increaseCountToPrefetch):
(WebCore::IDBServer::SQLiteIDBCursor::prefetch): Count to prefetch is decided by SQLiteCursor now.
(WebCore::IDBServer::SQLiteIDBCursor::internalFetchNextRecord):
(WebCore::IDBServer::SQLiteIDBCursor::currentValue const):
* Modules/indexeddb/server/SQLiteIDBCursor.h:

* Modules/indexeddb/server/UniqueIDBDatabase.cpp:
(WebCore::IDBServer::UniqueIDBDatabase::iterateCursor): Move call to prefetch cursor to SQLiteIDBBackingStore.
(WebCore::IDBServer::UniqueIDBDatabase::prefetchCursor): Deleted.
* Modules/indexeddb/server/UniqueIDBDatabase.h:
* Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp:
(WebCore::IDBServer::UniqueIDBDatabaseTransaction::iterateCursor):

* Modules/indexeddb/shared/IDBCursorRecord.h: Stop using pointer for IDBValue for easier encoding and decoding.
(WebCore::IDBCursorRecord::size const):
(WebCore::IDBCursorRecord::isolatedCopy const):

* Modules/indexeddb/shared/IDBIterateCursorData.cpp:
(WebCore::IDBIterateCursorData::isolatedCopy const):
* Modules/indexeddb/shared/IDBIterateCursorData.h: Add an option to let IDBServer know whether it should answer
the cursor request.
(WebCore::IDBIterateCursorData::encode const):
(WebCore::IDBIterateCursorData::decode):

* WebCore.xcodeproj/project.pbxproj:

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

27 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Headers.cmake
Source/WebCore/Modules/indexeddb/IDBCursor.cpp
Source/WebCore/Modules/indexeddb/IDBCursor.h
Source/WebCore/Modules/indexeddb/IDBGetResult.cpp
Source/WebCore/Modules/indexeddb/IDBGetResult.h
Source/WebCore/Modules/indexeddb/IDBRequest.cpp
Source/WebCore/Modules/indexeddb/IDBRequest.h
Source/WebCore/Modules/indexeddb/IDBTransaction.cpp
Source/WebCore/Modules/indexeddb/IDBTransaction.h
Source/WebCore/Modules/indexeddb/IndexedDB.h
Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp
Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp
Source/WebCore/Modules/indexeddb/client/TransactionOperation.h
Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h
Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h
Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp
Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h
Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h
Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp
Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h
Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp
Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h
Source/WebCore/WebCore.xcodeproj/project.pbxproj

index 200d973..fb02379 100644 (file)
@@ -1,3 +1,126 @@
+2020-02-14  Sihui Liu  <sihui_liu@apple.com>
+
+        IndexedDB: prefetch cursor records on client side
+        https://bugs.webkit.org/show_bug.cgi?id=207602
+        <rdar://problem/58483927>
+
+        Reviewed by Brady Eidson.
+
+        Cache cursor records on client side and use those records for iterate operations.
+
+        This makes cursor continue/advance tests in PerformanceTests/IndexedDB/basics ~3x faster.
+
+        * Headers.cmake:
+
+        * Modules/indexeddb/IDBCursor.cpp:
+        (WebCore::IDBCursor::setGetResult): Record the ID of TransactionOperation that updates cached record.
+        (WebCore::IDBCursor::iterateWithPrefetchedRecords): IDBCursor uses cached records for iterate opertaions if 
+        there is no write operation between last cached records update and current iteration operation.
+        (WebCore::IDBCursor::clearPrefetchedRecords):
+        * Modules/indexeddb/IDBCursor.h:
+
+        * Modules/indexeddb/IDBGetResult.cpp:
+        (WebCore::IDBGetResult::isolatedCopy):
+        * Modules/indexeddb/IDBGetResult.h:
+        (WebCore::IDBGetResult::IDBGetResult):
+        (WebCore::IDBGetResult::prefetchedRecords const):
+        (WebCore::IDBGetResult::encode const):
+        (WebCore::IDBGetResult::decode):
+
+        * Modules/indexeddb/IDBRequest.cpp: Record corresponding TransactionOperation ID in request.
+        (WebCore::IDBRequest::didOpenOrIterateCursor):
+        * Modules/indexeddb/IDBRequest.h:
+        (WebCore::IDBRequest::pendingCursor const):
+        (WebCore::IDBRequest::setTransactionOperationID):
+
+        * Modules/indexeddb/IDBTransaction.cpp: If a cursor iterate request can be handled with cached records,
+        IDBClient does not need to send request to IDBServer and wait for response. But requests before that iterate 
+        request may need to wait server to answer, and spec requires to handle requests in order. Therefore, we now keep
+        all the results in m_transactionOperationResultMap and handle them according to the ordering in 
+        m_transactionOperationsInProgressQueue.
+        (WebCore::IDBTransaction::abortInProgressOperations):
+        (WebCore::IDBTransaction::removeRequest): Because result of a cursor request can be answered sooner, it is 
+        possible that in finishedDispatchEventForRequest, m_currentlyCompletingRequest (which is a cursor request) is 
+        set to nullptr, and then it is set back to the same cursor request in handleOperationsCompletedOnServer right 
+        after. This happens in dispatchEvent of the cursor request, where request would remove itself from request list
+        of transaction at the end.
+        In this case, when request list becomes empty, transaction may commit automatically. But transaction should not 
+        because that request is still valid as m_currentlyCompletingRequest and should not be removed from list.
+        (WebCore::IDBTransaction::scheduleOperation):
+        (WebCore::IDBTransaction::operationCompletedOnServer):
+        (WebCore::IDBTransaction::handleOperationsCompletedOnServer): All requests were sent to IDBServer and the
+        response/result ordering would naturally be the same as request order. Now that results can be created in both 
+        IDBClient and IDBServer, we no longer handle requests using m_completedOnServerQueue.
+        (WebCore::IDBTransaction::createObjectStore): Mark as write operation to track last write operation.
+        (WebCore::IDBTransaction::renameObjectStore): Ditto.
+        (WebCore::IDBTransaction::createIndex): Ditto.
+        (WebCore::IDBTransaction::renameIndex): Ditto.
+        (WebCore::IDBTransaction::requestDeleteRecord): Ditto.
+        (WebCore::IDBTransaction::requestClearObjectStore): Ditto.
+        (WebCore::IDBTransaction::requestPutOrAdd): Ditto.
+        (WebCore::IDBTransaction::deleteObjectStore): Ditto.
+        (WebCore::IDBTransaction::deleteIndex): Ditto.
+        (WebCore::IDBTransaction::iterateCursorOnServer): Only use cached records if the request does not specify target
+        key. If cursor is iterated successfully with cache, send a message to IDBServer to notify about the progress.
+        Otherwise, fall back to depend on IDBServer to answer the request.
+        (WebCore::IDBTransaction::generateOperationID): TransactionOperation ID is unique in a transaction.
+        * Modules/indexeddb/IDBTransaction.h:
+
+        * Modules/indexeddb/IndexedDB.h:
+        * Modules/indexeddb/client/IDBConnectionProxy.cpp: 
+        (WebCore::IDBClient::IDBConnectionProxy::iterateCursor): Don't track result of TransactionOperation in 
+        IDBConnectionProxy if TransactionOperation does not need a reply from IDBServer.
+
+        * Modules/indexeddb/client/TransactionOperation.cpp:
+        (WebCore::IDBClient::TransactionOperation::TransactionOperation):
+        * Modules/indexeddb/client/TransactionOperation.h: Add ID to TransactionOperation.
+        (WebCore::IDBClient::TransactionOperation::operationID const):
+        (WebCore::IDBClient::TransactionOperation::TransactionOperation):
+
+        * Modules/indexeddb/server/IDBBackingStore.h: remove prefetchCursor as it is not used now.
+        * Modules/indexeddb/server/MemoryIDBBackingStore.h: Ditto. Also this patch only deals with prefetching in 
+        persistent store.
+        * Modules/indexeddb/server/MemoryIDBBackingStore.h:
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::createIndex):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getAllIndexRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getIndexRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::iterateCursor): Only prefetch at when request needs to be answered.
+        (WebCore::IDBServer::SQLiteIDBBackingStore::prefetchCursor): Deleted.
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+
+        * Modules/indexeddb/server/SQLiteIDBCursor.cpp:
+        (WebCore::IDBServer::SQLiteIDBCursor::currentData): Provide an option to include prefetched records in result.
+        (WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged): Reset count to prefetch when there is a 
+        change. This is used with increaseCountToPrefetch to make prefetch adaptive.
+        (WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged):
+        (WebCore::IDBServer::SQLiteIDBCursor::prefetchOneRecord):
+        (WebCore::IDBServer::SQLiteIDBCursor::increaseCountToPrefetch):
+        (WebCore::IDBServer::SQLiteIDBCursor::prefetch): Count to prefetch is decided by SQLiteCursor now.
+        (WebCore::IDBServer::SQLiteIDBCursor::internalFetchNextRecord):
+        (WebCore::IDBServer::SQLiteIDBCursor::currentValue const):
+        * Modules/indexeddb/server/SQLiteIDBCursor.h:
+
+        * Modules/indexeddb/server/UniqueIDBDatabase.cpp:
+        (WebCore::IDBServer::UniqueIDBDatabase::iterateCursor): Move call to prefetch cursor to SQLiteIDBBackingStore.
+        (WebCore::IDBServer::UniqueIDBDatabase::prefetchCursor): Deleted.
+        * Modules/indexeddb/server/UniqueIDBDatabase.h:
+        * Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp:
+        (WebCore::IDBServer::UniqueIDBDatabaseTransaction::iterateCursor):
+
+        * Modules/indexeddb/shared/IDBCursorRecord.h: Stop using pointer for IDBValue for easier encoding and decoding.
+        (WebCore::IDBCursorRecord::size const):
+        (WebCore::IDBCursorRecord::isolatedCopy const):
+
+        * Modules/indexeddb/shared/IDBIterateCursorData.cpp:
+        (WebCore::IDBIterateCursorData::isolatedCopy const):
+        * Modules/indexeddb/shared/IDBIterateCursorData.h: Add an option to let IDBServer know whether it should answer
+        the cursor request. 
+        (WebCore::IDBIterateCursorData::encode const):
+        (WebCore::IDBIterateCursorData::decode):
+
+        * WebCore.xcodeproj/project.pbxproj:
+
 2020-02-14  Charles Turner  <cturner@igalia.com>
 
         [GStreamer][EME] Fix warnings in LOG_DISABLED build
index 3329ab0..dc38d6b 100644 (file)
@@ -68,6 +68,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
     Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h
 
     Modules/indexeddb/shared/IDBCursorInfo.h
+    Modules/indexeddb/shared/IDBCursorRecord.h
     Modules/indexeddb/shared/IDBDatabaseInfo.h
     Modules/indexeddb/shared/IDBError.h
     Modules/indexeddb/shared/IDBGetAllRecordsData.h
index 368edc2..65b89b5 100644 (file)
@@ -312,7 +312,7 @@ ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(JSGlobalObject&
     return request;
 }
 
-bool IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
+bool IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult, uint64_t operationID)
 {
     LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data());
     ASSERT(canCurrentThreadAccessThreadLocalData(effectiveObjectStore().transaction().database().originThread()));
@@ -349,6 +349,13 @@ bool IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
         m_keyPath = getResult.keyPath();
     }
 
+    auto prefetchedRecords = getResult.prefetchedRecords();
+    if (!prefetchedRecords.isEmpty()) {
+        for (auto& record : prefetchedRecords)
+            m_prefetchedRecords.append(record);
+        m_prefetchOperationID = operationID;
+    }
+
     m_gotValue = true;
     return true;
 }
@@ -360,6 +367,26 @@ void IDBCursor::clearWrappers()
     m_valueWrapper.clear();
 }
 
+Optional<IDBGetResult> IDBCursor::iterateWithPrefetchedRecords(unsigned count, uint64_t lastWriteOperationID)
+{
+    unsigned step = count > 0 ? count : 1;
+    if (step > m_prefetchedRecords.size() || m_prefetchOperationID <= lastWriteOperationID)
+        return WTF::nullopt;
+
+    while (--step)
+        m_prefetchedRecords.removeFirst();
+
+    auto record = m_prefetchedRecords.takeFirst();
+
+    LOG(IndexedDB, "IDBTransaction::iterateWithPrefetchedRecords consumes %u records", count > 0 ? count : 1);
+    return IDBGetResult(record.key, record.primaryKey, IDBValue(record.value), effectiveObjectStore().keyPath());
+}
+
+void IDBCursor::clearPrefetchedRecords()
+{
+    m_prefetchedRecords.clear();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index 4622e70..7bc8f8c 100644 (file)
@@ -81,10 +81,13 @@ public:
     void clearWrappers();
     IDBRequest* request() { return m_request.get(); }
 
-    bool setGetResult(IDBRequest&, const IDBGetResult&);
+    bool setGetResult(IDBRequest&, const IDBGetResult&, uint64_t operationID);
 
     virtual bool isKeyCursorWithValue() const { return false; }
 
+    Optional<IDBGetResult> iterateWithPrefetchedRecords(unsigned count, uint64_t lastWriteOperationID);
+    void clearPrefetchedRecords();
+
 protected:
     IDBCursor(IDBObjectStore&, const IDBCursorInfo&);
     IDBCursor(IDBIndex&, const IDBCursorInfo&);
@@ -113,6 +116,9 @@ private:
     JSValueInWrappedObject m_keyWrapper;
     JSValueInWrappedObject m_primaryKeyWrapper;
     JSValueInWrappedObject m_valueWrapper;
+
+    Deque<IDBCursorRecord> m_prefetchedRecords;
+    uint64_t m_prefetchOperationID { 0 };
 };
 
 
index 949e813..12ec870 100644 (file)
@@ -55,6 +55,7 @@ void IDBGetResult::isolatedCopy(const IDBGetResult& source, IDBGetResult& destin
     destination.m_primaryKeyData = source.m_primaryKeyData.isolatedCopy();
     destination.m_keyPath = WebCore::isolatedCopy(source.m_keyPath);
     destination.m_isDefined = source.m_isDefined;
+    destination.m_prefetchedRecords = source.m_prefetchedRecords.isolatedCopy();
 }
 
 void IDBGetResult::setValue(IDBValue&& value)
index e87c390..d03d89b 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(INDEXED_DATABASE)
 
+#include "IDBCursorRecord.h"
 #include "IDBKey.h"
 #include "IDBKeyData.h"
 #include "IDBKeyPath.h"
@@ -76,6 +77,15 @@ public:
     {
     }
 
+    IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, IDBValue&& value, const Optional<IDBKeyPath>& keyPath, Vector<IDBCursorRecord>&& prefetechedRecords)
+        : m_value(WTFMove(value))
+        , m_keyData(keyData)
+        , m_primaryKeyData(primaryKeyData)
+        , m_keyPath(keyPath)
+        , m_prefetchedRecords(WTFMove(prefetechedRecords))
+    {
+    }
+
     enum IsolatedCopyTag { IsolatedCopy };
     IDBGetResult(const IDBGetResult&, IsolatedCopyTag);
 
@@ -87,6 +97,7 @@ public:
     const IDBKeyData& keyData() const { return m_keyData; }
     const IDBKeyData& primaryKeyData() const { return m_primaryKeyData; }
     const Optional<IDBKeyPath>& keyPath() const { return m_keyPath; }
+    const Vector<IDBCursorRecord>& prefetchedRecords() const { return m_prefetchedRecords; }
     bool isDefined() const { return m_isDefined; }
 
     template<class Encoder> void encode(Encoder&) const;
@@ -101,13 +112,14 @@ private:
     IDBKeyData m_keyData;
     IDBKeyData m_primaryKeyData;
     Optional<IDBKeyPath> m_keyPath;
+    Vector<IDBCursorRecord> m_prefetchedRecords;
     bool m_isDefined { true };
 };
 
 template<class Encoder>
 void IDBGetResult::encode(Encoder& encoder) const
 {
-    encoder << m_keyData << m_primaryKeyData << m_keyPath << m_isDefined << m_value;
+    encoder << m_keyData << m_primaryKeyData << m_keyPath << m_isDefined << m_value << m_prefetchedRecords;
 }
 
 template<class Decoder>
@@ -137,6 +149,12 @@ bool IDBGetResult::decode(Decoder& decoder, IDBGetResult& result)
         return false;
     result.m_value = WTFMove(*value);
 
+    Optional<Vector<IDBCursorRecord>> prefetchedRecords;
+    decoder >> prefetchedRecords;
+    if (!prefetchedRecords)
+        return false;
+    result.m_prefetchedRecords = WTFMove(*prefetchedRecords);
+
     return true;
 }
 
index 30f6e5c..3d02119 100644 (file)
@@ -499,7 +499,7 @@ void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData)
     m_resultWrapper = { };
 
     if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) {
-        if (m_pendingCursor->setGetResult(*this, resultData.getResult()) && m_cursorWrapper)
+        if (m_pendingCursor->setGetResult(*this, resultData.getResult(), m_currentTransactionOperationID) && m_cursorWrapper)
             m_resultWrapper = m_cursorWrapper;
         if (resultData.getResult().isDefined())
             m_result = m_pendingCursor;
index 278501b..2866134 100644 (file)
@@ -118,7 +118,7 @@ public:
     void willIterateCursor(IDBCursor&);
     void didOpenOrIterateCursor(const IDBResultData&);
 
-    const IDBCursor* pendingCursor() const { return m_pendingCursor.get(); }
+    IDBCursor* pendingCursor() const { return m_pendingCursor ? m_pendingCursor.get() : nullptr; }
 
     void setSource(IDBCursor&);
     void setVersionChangeTransaction(IDBTransaction&);
@@ -127,6 +127,8 @@ public:
 
     bool hasPendingActivity() const final;
 
+    void setTransactionOperationID(uint64_t transactionOperationID) { m_currentTransactionOperationID = transactionOperationID; }
+
 protected:
     IDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&);
 
@@ -191,6 +193,8 @@ private:
 
     bool m_dispatchingEvent { false };
     bool m_hasUncaughtException { false };
+
+    uint64_t m_currentTransactionOperationID { 0 };
 };
 
 } // namespace WebCore
index 47f2cb0..8ddf207 100644 (file)
@@ -272,15 +272,7 @@ void IDBTransaction::abortInProgressOperations(const IDBError& error)
         operation->doComplete(IDBResultData::error(operation->identifier(), error));
     }
 
-    Vector<RefPtr<IDBClient::TransactionOperation>> completedOnServerAbortVector;
-    completedOnServerAbortVector.reserveInitialCapacity(m_completedOnServerQueue.size());
-    while (!m_completedOnServerQueue.isEmpty())
-        completedOnServerAbortVector.uncheckedAppend(m_completedOnServerQueue.takeFirst().first);
-
-    for (auto& operation : completedOnServerAbortVector) {
-        m_currentlyCompletingRequest = nullptr;
-        operation->doComplete(IDBResultData::error(operation->identifier(), error));
-    }
+    m_transactionOperationResultMap.clear();
 
     m_currentlyCompletingRequest = nullptr;
     connectionProxy().forgetActiveOperations(inProgressAbortVector);
@@ -376,16 +368,22 @@ void IDBTransaction::addRequest(IDBRequest& request)
 void IDBTransaction::removeRequest(IDBRequest& request)
 {
     ASSERT(canCurrentThreadAccessThreadLocalData(m_database->originThread()));
+    if (m_currentlyCompletingRequest == &request)
+        return;
+
     m_openRequests.remove(&request);
 
     autoCommit();
 }
 
-void IDBTransaction::scheduleOperation(Ref<IDBClient::TransactionOperation>&& operation)
+void IDBTransaction::scheduleOperation(Ref<IDBClient::TransactionOperation>&& operation, IsWriteOperation isWriteOperation)
 {
     ASSERT(!m_transactionOperationMap.contains(operation->identifier()));
     ASSERT(canCurrentThreadAccessThreadLocalData(m_database->originThread()));
 
+    if (isWriteOperation == IsWriteOperation::Yes)
+        m_lastWriteOperationID = operation->operationID();
+
     auto identifier = operation->identifier();
     m_pendingTransactionOperationQueue.append(operation.copyRef());
     m_transactionOperationMap.set(identifier, WTFMove(operation));
@@ -398,7 +396,10 @@ void IDBTransaction::operationCompletedOnServer(const IDBResultData& data, IDBCl
     ASSERT(canCurrentThreadAccessThreadLocalData(m_database->originThread()));
     ASSERT(canCurrentThreadAccessThreadLocalData(operation.originThread()));
 
-    m_completedOnServerQueue.append({ &operation, data });
+    if (!m_transactionOperationMap.contains(operation.identifier()))
+        return;
+
+    m_transactionOperationResultMap.set(&operation, IDBResultData(data));
 
     if (!m_currentlyCompletingRequest)
         handleOperationsCompletedOnServer();
@@ -406,12 +407,15 @@ void IDBTransaction::operationCompletedOnServer(const IDBResultData& data, IDBCl
 
 void IDBTransaction::handleOperationsCompletedOnServer()
 {
-    LOG(IndexedDB, "IDBTransaction::completedOperationTimerFired (%p)", this);
+    LOG(IndexedDB, "IDBTransaction::handleOperationsCompletedOnServer");
     ASSERT(canCurrentThreadAccessThreadLocalData(m_database->originThread()));
 
-    while (!m_completedOnServerQueue.isEmpty() && !m_currentlyCompletingRequest) {
-        auto iterator = m_completedOnServerQueue.takeFirst();
-        iterator.first->doComplete(iterator.second);
+    while (!m_transactionOperationsInProgressQueue.isEmpty() && !m_currentlyCompletingRequest) {
+        RefPtr<IDBClient::TransactionOperation> currentOperation = m_transactionOperationsInProgressQueue.first();
+        if (!m_transactionOperationResultMap.contains(currentOperation))
+            return;
+
+        currentOperation->doComplete(m_transactionOperationResultMap.take(currentOperation));
     }
 }
 
@@ -622,7 +626,7 @@ Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo&
         protectedThis->didCreateObjectStoreOnServer(result);
     }, [protectedThis = makeRef(*this), info = info.isolatedCopy()] (auto& operation) {
         protectedThis->createObjectStoreOnServer(operation, info);
-    }));
+    }), IsWriteOperation::Yes);
 
     return *rawObjectStore;
 }
@@ -664,7 +668,7 @@ void IDBTransaction::renameObjectStore(IDBObjectStore& objectStore, const String
         protectedThis->didRenameObjectStoreOnServer(result);
     }, [protectedThis = makeRef(*this), objectStoreIdentifier, newName = newName.isolatedCopy()] (auto& operation) {
         protectedThis->renameObjectStoreOnServer(operation, objectStoreIdentifier, newName);
-    }));
+    }), IsWriteOperation::Yes);
 
     m_referencedObjectStores.set(newName, m_referencedObjectStores.take(objectStore.info().name()));
 }
@@ -699,7 +703,7 @@ std::unique_ptr<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStor
         protectedThis->didCreateIndexOnServer(result);
     }, [protectedThis = makeRef(*this), info = info.isolatedCopy()] (auto& operation) {
         protectedThis->createIndexOnServer(operation, info);
-    }));
+    }), IsWriteOperation::Yes);
 
     return makeUnique<IDBIndex>(*scriptExecutionContext(), info, objectStore);
 }
@@ -753,7 +757,7 @@ void IDBTransaction::renameIndex(IDBIndex& index, const String& newName)
         protectedThis->didRenameIndexOnServer(result);
     }, [protectedThis = makeRef(*this), objectStoreIdentifier, indexIdentifier, newName = newName.isolatedCopy()] (auto& operation) {
         protectedThis->renameIndexOnServer(operation, objectStoreIdentifier, indexIdentifier, newName);
-    }));
+    }), IsWriteOperation::Yes);
 }
 
 void IDBTransaction::renameIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName)
@@ -852,7 +856,23 @@ void IDBTransaction::iterateCursorOnServer(IDBClient::TransactionOperation& oper
 {
     LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer");
     ASSERT(canCurrentThreadAccessThreadLocalData(m_database->originThread()));
+    ASSERT(operation.idbRequest());
 
+    auto* cursor = operation.idbRequest()->pendingCursor();
+    ASSERT(cursor);
+
+    if (data.keyData.isNull() && data.primaryKeyData.isNull()) {
+        if (auto getResult = cursor->iterateWithPrefetchedRecords(data.count, m_lastWriteOperationID)) {
+            auto result = IDBResultData::iterateCursorSuccess(operation.identifier(), getResult.value());
+            m_database->connectionProxy().iterateCursor(operation, { data.keyData, data.primaryKeyData, data.count, IndexedDB::CursorIterateOption::DoNotReply });
+            operationCompletedOnServer(result, operation);
+            return;
+        }
+    }
+
+    cursor->clearPrefetchedRecords();
+
+    ASSERT(data.option == IndexedDB::CursorIterateOption::Reply);
     m_database->connectionProxy().iterateCursor(operation, data);
 }
 
@@ -1126,7 +1146,7 @@ Ref<IDBRequest> IDBTransaction::requestDeleteRecord(JSGlobalObject& state, IDBOb
         protectedThis->didDeleteRecordOnServer(request.get(), result);
     }, [protectedThis = makeRef(*this), range = range.isolatedCopy()] (auto& operation) {
         protectedThis->deleteRecordOnServer(operation, range);
-    }));
+    }), IsWriteOperation::Yes);
     return request;
 }
 
@@ -1165,7 +1185,7 @@ Ref<IDBRequest> IDBTransaction::requestClearObjectStore(JSGlobalObject& state, I
         protectedThis->didClearObjectStoreOnServer(request.get(), result);
     }, [protectedThis = makeRef(*this), objectStoreIdentifier] (auto& operation) {
         protectedThis->clearObjectStoreOnServer(operation, objectStoreIdentifier);
-    }));
+    }), IsWriteOperation::Yes);
 
     return request;
 }
@@ -1205,7 +1225,7 @@ Ref<IDBRequest> IDBTransaction::requestPutOrAdd(JSGlobalObject& state, IDBObject
         protectedThis->didPutOrAddOnServer(request.get(), result);
     }, [protectedThis = makeRef(*this), key, value = makeRef(value), overwriteMode] (auto& operation) {
         protectedThis->putOrAddOnServer(operation, key.get(), value.ptr(), overwriteMode);
-    }));
+    }), IsWriteOperation::Yes);
 
     return request;
 }
@@ -1293,7 +1313,7 @@ void IDBTransaction::deleteObjectStore(const String& objectStoreName)
         protectedThis->didDeleteObjectStoreOnServer(result);
     }, [protectedThis = makeRef(*this), objectStoreName = objectStoreName.isolatedCopy()] (auto& operation) {
         protectedThis->deleteObjectStoreOnServer(operation, objectStoreName);
-    }));
+    }), IsWriteOperation::Yes);
 }
 
 void IDBTransaction::deleteObjectStoreOnServer(IDBClient::TransactionOperation& operation, const String& objectStoreName)
@@ -1323,7 +1343,7 @@ void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& i
         protectedThis->didDeleteIndexOnServer(result);
     }, [protectedThis = makeRef(*this), objectStoreIdentifier, indexName = indexName.isolatedCopy()] (auto& operation) {
         protectedThis->deleteIndexOnServer(operation, objectStoreIdentifier, indexName);
-    }));
+    }), IsWriteOperation::Yes);
 }
 
 void IDBTransaction::deleteIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName)
@@ -1474,6 +1494,12 @@ void IDBTransaction::autoCommit()
     commit();
 }
 
+uint64_t IDBTransaction::generateOperationID()
+{
+    static std::atomic<uint64_t> currentOperationID(1);
+    return currentOperationID += 1;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index 358b01b..d41773c 100644 (file)
@@ -71,6 +71,8 @@ public:
     static Ref<IDBTransaction> create(IDBDatabase&, const IDBTransactionInfo&);
     static Ref<IDBTransaction> create(IDBDatabase&, const IDBTransactionInfo&, IDBOpenDBRequest&);
 
+    static uint64_t generateOperationID();
+
     WEBCORE_EXPORT ~IDBTransaction() final;
 
     // IDBTransaction IDL
@@ -165,7 +167,8 @@ private:
     void finishAbortOrCommit();
     void abortInProgressOperations(const IDBError&);
 
-    void scheduleOperation(Ref<IDBClient::TransactionOperation>&&);
+    enum class IsWriteOperation : bool { No, Yes };
+    void scheduleOperation(Ref<IDBClient::TransactionOperation>&&, IsWriteOperation = IsWriteOperation::No);
     void handleOperationsCompletedOnServer();
     void handlePendingOperations();
     void autoCommit();
@@ -244,8 +247,8 @@ private:
 
     Deque<RefPtr<IDBClient::TransactionOperation>> m_pendingTransactionOperationQueue;
     Deque<IDBClient::TransactionOperation*> m_transactionOperationsInProgressQueue;
-    Deque<std::pair<RefPtr<IDBClient::TransactionOperation>, IDBResultData>> m_completedOnServerQueue;
     Deque<RefPtr<IDBClient::TransactionOperation>> m_abortQueue;
+    HashMap<RefPtr<IDBClient::TransactionOperation>, IDBResultData> m_transactionOperationResultMap;
 
     HashMap<IDBResourceIdentifier, RefPtr<IDBClient::TransactionOperation>> m_transactionOperationMap;
 
@@ -258,6 +261,8 @@ private:
 
     bool m_contextStopped { false };
     bool m_didDispatchAbortOrCommit { false };
+
+    uint64_t m_lastWriteOperationID { 0 };
 };
 
 class TransactionActivator {
index 509bbf2..bf308ad 100644 (file)
@@ -106,6 +106,11 @@ enum class GetAllType {
 
 enum class ConnectionClosedOnBehalfOfServer : bool { No, Yes };
 
+enum class CursorIterateOption {
+    DoNotReply,
+    Reply,
+};
+
 } // namespace IndexedDB
 
 } // namespace WebCore
index 4ec447e..956dfae 100644 (file)
@@ -238,7 +238,8 @@ void IDBConnectionProxy::openCursor(TransactionOperation& operation, const IDBCu
 void IDBConnectionProxy::iterateCursor(TransactionOperation& operation, const IDBIterateCursorData& data)
 {
     const IDBRequestData requestData { operation };
-    saveOperation(operation);
+    if (data.option != IndexedDB::CursorIterateOption::DoNotReply)
+        saveOperation(operation);
 
     callConnectionOnMainThread(&IDBConnectionToServer::iterateCursor, requestData, data);
 }
index 8e1de06..aa5906f 100644 (file)
@@ -44,6 +44,7 @@ TransactionOperation::TransactionOperation(IDBTransaction& transaction, IDBReque
     if (auto* cursor = request.pendingCursor())
         m_cursorIdentifier = makeUnique<IDBResourceIdentifier>(cursor->info().identifier());
 
+    request.setTransactionOperationID(m_operationID);
     m_idbRequest = &request;
 }
 
index e2f148a..7ab5755 100644 (file)
@@ -112,10 +112,13 @@ public:
     bool nextRequestCanGoToServer() const { return m_nextRequestCanGoToServer && m_idbRequest; }
     void setNextRequestCanGoToServer(bool nextRequestCanGoToServer) { m_nextRequestCanGoToServer = nextRequestCanGoToServer; }
 
+    uint64_t operationID() const { return m_operationID; }
+
 protected:
     TransactionOperation(IDBTransaction& transaction)
         : m_transaction(transaction)
         , m_identifier(transaction.connectionProxy())
+        , m_operationID(transaction.generateOperationID())
     {
     }
 
@@ -142,6 +145,8 @@ private:
     RefPtr<IDBRequest> m_idbRequest;
     bool m_nextRequestCanGoToServer { true };
     bool m_didComplete { false };
+
+    uint64_t m_operationID { 0 };
 };
 
 class TransactionOperationImpl final : public TransactionOperation {
index c42640b..a7df7a1 100644 (file)
@@ -85,7 +85,6 @@ public:
     virtual IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) = 0;
     virtual IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) = 0;
     virtual IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) = 0;
-    virtual bool prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier) = 0;
 
     virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) = 0;
     virtual void deleteBackingStore() = 0;
index 378d2a5..74dc950 100644 (file)
@@ -74,7 +74,6 @@ private:
     IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) final;
     IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final;
     IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final;
-    bool prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&) final { return false; }
 
     IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final;
     void deleteBackingStore() final;
index 3fa102d..573bec7 100644 (file)
@@ -1366,8 +1366,8 @@ IDBError SQLiteIDBBackingStore::createIndex(const IDBResourceIdentifier& transac
 
     while (!cursor->currentKey().isNull()) {
         auto& key = cursor->currentKey();
-        auto* value = cursor->currentValue();
-        ThreadSafeDataBuffer valueBuffer = value ? value->data() : ThreadSafeDataBuffer();
+        auto value = cursor->currentValue();
+        ThreadSafeDataBuffer valueBuffer = value.data();
 
         ASSERT(cursor->currentRecordRowID());
 
@@ -2354,7 +2354,7 @@ IDBError SQLiteIDBBackingStore::getAllIndexRecords(const IDBResourceIdentifier&
         IDBKeyData keyCopy = cursor->currentPrimaryKey();
         result.addKey(WTFMove(keyCopy));
         if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values)
-            result.addValue(cursor->currentValue() ? *cursor->currentValue() : IDBValue());
+            result.addValue(IDBValue(cursor->currentValue()));
 
         ++currentCount;
         cursor->advance(1);
@@ -2401,7 +2401,7 @@ IDBError SQLiteIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& tran
         else {
             auto* objectStoreInfo = infoForObjectStore(objectStoreID);
             ASSERT(objectStoreInfo);
-            getResult = { cursor->currentPrimaryKey(), cursor->currentPrimaryKey(), cursor->currentValue() ? *cursor->currentValue() : IDBValue(), objectStoreInfo->keyPath() };
+            getResult = { cursor->currentPrimaryKey(), cursor->currentPrimaryKey(), IDBValue(cursor->currentValue()), objectStoreInfo->keyPath() };
         }
     }
 
@@ -2721,26 +2721,18 @@ IDBError SQLiteIDBBackingStore::iterateCursor(const IDBResourceIdentifier& trans
         }
     }
 
-    auto* objectStoreInfo = infoForObjectStore(cursor->objectStoreID());
-    ASSERT(objectStoreInfo);
-    cursor->currentData(result, objectStoreInfo->keyPath());
-    return IDBError { };
-}
-
-bool SQLiteIDBBackingStore::prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
-{
-    LOG(IndexedDB, "SQLiteIDBBackingStore::prefetchCursor");
-
-    ASSERT(m_sqliteDB);
-    ASSERT(m_sqliteDB->isOpen());
+    if (data.option == IndexedDB::CursorIterateOption::Reply) {
+        auto* objectStoreInfo = infoForObjectStore(cursor->objectStoreID());
+        ASSERT(objectStoreInfo);
 
-    auto* cursor = m_cursors.get(cursorIdentifier);
-    if (!cursor || !cursor->transaction() || !cursor->transaction()->inProgress())
-        return false;
+        bool shouldPrefetch = key.isNull() && primaryKey.isNull();
+        if (shouldPrefetch)
+            cursor->prefetch();
 
-    ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
+        cursor->currentData(result, objectStoreInfo->keyPath(), shouldPrefetch ? SQLiteIDBCursor::ShouldIncludePrefetchedRecords::Yes : SQLiteIDBCursor::ShouldIncludePrefetchedRecords::No);
+    }
 
-    return cursor->prefetch();
+    return IDBError { };
 }
 
 IDBObjectStoreInfo* SQLiteIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
index ce29d2c..4940c15 100644 (file)
@@ -80,7 +80,6 @@ public:
     IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) final;
     IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final;
     IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final;
-    bool prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&) final;
 
     IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final;
     void deleteBackingStore() final;
index b377173..e19aa13 100644 (file)
@@ -42,8 +42,8 @@
 namespace WebCore {
 namespace IDBServer {
 
-static const size_t prefetchLimit = 128;
-static const size_t prefetchSizeLimit = 8 * MB;
+static const size_t prefetchLimit = 256;
+static const size_t prefetchSizeLimit = 1 * MB;
 
 std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
 {
@@ -102,7 +102,7 @@ SQLiteIDBCursor::~SQLiteIDBCursor()
         m_transaction->closeCursor(*this);
 }
 
-void SQLiteIDBCursor::currentData(IDBGetResult& result, const Optional<IDBKeyPath>& keyPath)
+void SQLiteIDBCursor::currentData(IDBGetResult& result, const Optional<IDBKeyPath>& keyPath, ShouldIncludePrefetchedRecords shouldIncludePrefetchedRecords)
 {
     ASSERT(!m_fetchedRecords.isEmpty());
 
@@ -113,7 +113,25 @@ void SQLiteIDBCursor::currentData(IDBGetResult& result, const Optional<IDBKeyPat
         return;
     }
 
-    result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue(), keyPath};
+    if (shouldIncludePrefetchedRecords == ShouldIncludePrefetchedRecords::No) {
+        result = { currentRecord.record.key, currentRecord.record.primaryKey, IDBValue(currentRecord.record.value), keyPath };
+        return;
+    }
+
+    Vector<IDBCursorRecord> prefetchedRecords;
+    prefetchedRecords.reserveCapacity(m_fetchedRecords.size());
+    for (auto& record : m_fetchedRecords) {
+        if (record.isTerminalRecord())
+            break;
+
+        prefetchedRecords.append(record.record);
+    }
+
+    // First record will be returned as current record.
+    if (!prefetchedRecords.isEmpty())
+        prefetchedRecords.remove(0);
+
+    result = { currentRecord.record.key, currentRecord.record.primaryKey, IDBValue(currentRecord.record.value), keyPath, WTFMove(prefetchedRecords) };
 }
 
 static String buildPreIndexStatement(bool isDirectionNext)
@@ -263,6 +281,8 @@ void SQLiteIDBCursor::objectStoreRecordsChanged()
     // We also need to throw away any fetched records as they may no longer be valid.
     m_fetchedRecords.clear();
     m_fetchedRecordsSize = 0;
+
+    m_prefetchCount = 0;
 }
 
 void SQLiteIDBCursor::resetAndRebindStatement()
@@ -359,17 +379,32 @@ bool SQLiteIDBCursor::resetAndRebindPreIndexStatementIfNecessary()
     return true;
 }
 
-bool SQLiteIDBCursor::prefetch()
+bool SQLiteIDBCursor::prefetchOneRecord()
 {
-    LOG(IndexedDB, "SQLiteIDBCursor::prefetch() - Cursor already has %zu fetched records", m_fetchedRecords.size());
+    LOG(IndexedDB, "SQLiteIDBCursor::prefetchOneRecord() - Cursor already has %zu fetched records", m_fetchedRecords.size());
 
     if (m_fetchedRecordsSize >= prefetchSizeLimit || m_fetchedRecords.isEmpty() || m_fetchedRecords.size() >= prefetchLimit || m_fetchedRecords.last().isTerminalRecord())
         return false;
 
     m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
-    fetch();
 
-    return m_fetchedRecords.size() < prefetchLimit && m_fetchedRecordsSize < prefetchSizeLimit;
+    return fetch() && m_fetchedRecords.size() < prefetchLimit && m_fetchedRecordsSize < prefetchSizeLimit;
+}
+
+void SQLiteIDBCursor::increaseCountToPrefetch()
+{
+    m_prefetchCount = m_prefetchCount ? m_prefetchCount * 2 : 1;
+}
+
+bool SQLiteIDBCursor::prefetch()
+{
+    for (unsigned i = 0; i < m_prefetchCount; ++i) {
+        if (!prefetchOneRecord())
+            return false;
+    }
+
+    increaseCountToPrefetch();
+    return true;
 }
 
 bool SQLiteIDBCursor::advance(uint64_t count)
@@ -488,7 +523,7 @@ SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCurs
     ASSERT(!m_fetchedRecords.isEmpty());
     ASSERT(!m_fetchedRecords.last().isTerminalRecord());
 
-    record.record.value = nullptr;
+    record.record.value = { };
 
     auto& database = m_transaction->sqliteTransaction()->database();
     SQLiteStatement* statement = nullptr;
@@ -546,7 +581,7 @@ SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCurs
         }
 
         if (m_cursorType == IndexedDB::CursorType::KeyAndValue)
-            record.record.value = makeUnique<IDBValue>(ThreadSafeDataBuffer::create(WTFMove(keyData)), blobURLs, blobFilePaths);
+            record.record.value = { ThreadSafeDataBuffer::create(WTFMove(keyData)), blobURLs, blobFilePaths };
     } else {
         if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.primaryKey)) {
             LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
@@ -572,7 +607,7 @@ SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCurs
 
         if (result == SQLITE_ROW) {
             m_cachedObjectStoreStatement->getColumnBlobAsVector(0, keyData);
-            record.record.value = makeUnique<IDBValue>(ThreadSafeDataBuffer::create(WTFMove(keyData)));
+            record.record.value = { ThreadSafeDataBuffer::create(WTFMove(keyData)) };
         } else if (result == SQLITE_DONE) {
             // This indicates that the record we're trying to retrieve has been removed from the object store.
             // Skip over it.
@@ -645,10 +680,10 @@ const IDBKeyData& SQLiteIDBCursor::currentPrimaryKey() const
     return m_fetchedRecords.first().record.primaryKey;
 }
 
-IDBValue* SQLiteIDBCursor::currentValue() const
+const IDBValue& SQLiteIDBCursor::currentValue() const
 {
     ASSERT(!m_fetchedRecords.isEmpty());
-    return m_fetchedRecords.first().record.value.get();
+    return m_fetchedRecords.first().record.value;
 }
 
 bool SQLiteIDBCursor::didComplete() const
index d81dfb5..fdca80e 100644 (file)
@@ -66,10 +66,11 @@ public:
 
     const IDBKeyData& currentKey() const;
     const IDBKeyData& currentPrimaryKey() const;
-    IDBValue* currentValue() const;
+    const IDBValue& currentValue() const;
 
     bool advance(uint64_t count);
     bool iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey);
+    bool prefetchOneRecord();
     bool prefetch();
 
     bool didComplete() const;
@@ -77,7 +78,8 @@ public:
 
     void objectStoreRecordsChanged();
 
-    void currentData(IDBGetResult&, const Optional<IDBKeyPath>&);
+    enum class ShouldIncludePrefetchedRecords { No, Yes };
+    void currentData(IDBGetResult&, const Optional<IDBKeyPath>&, ShouldIncludePrefetchedRecords = ShouldIncludePrefetchedRecords::No);
 
 private:
     bool establishStatement();
@@ -109,6 +111,8 @@ private:
 
     bool isDirectionNext() const { return m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique; }
 
+    void increaseCountToPrefetch();
+
     SQLiteIDBTransaction* m_transaction;
     IDBResourceIdentifier m_cursorIdentifier;
     int64_t m_objectStoreID;
@@ -133,6 +137,8 @@ private:
     int64_t m_boundID { 0 };
 
     bool m_backingStoreCursor { false };
+
+    unsigned m_prefetchCount { 0 };
 };
 
 } // namespace IDBServer
index a0a0516..af5b318 100644 (file)
@@ -854,22 +854,6 @@ void UniqueIDBDatabase::iterateCursor(const IDBRequestData& requestData, const I
     auto error = m_backingStore->iterateCursor(transactionIdentifier, cursorIdentifier, data, result);
 
     callback(error, result);
-
-    if (error.isNull())
-        prefetchCursor(transactionIdentifier, cursorIdentifier, data.count ? data.count : 1);
-}
-
-void UniqueIDBDatabase::prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, uint64_t step)
-{
-    LOG(IndexedDB, "UniqueIDBDatabase::prefetchCursor");
-
-    ASSERT(!isMainThread());
-
-    uint64_t countToPrefetch = step * 2;
-    while (countToPrefetch --) {
-        if (!m_backingStore->prefetchCursor(transactionIdentifier, cursorIdentifier))
-            return;
-    }
 }
 
 void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
index 06bf6a4..1c68873 100644 (file)
@@ -142,8 +142,6 @@ private:
 
     void clearTransactionsOnConnection(UniqueIDBDatabaseConnection&);
 
-    void prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&, uint64_t step);
-
     IDBServer& m_server;
     IDBDatabaseIdentifier m_identifier;
 
index 0c9bb10..bf55c0d 100644 (file)
@@ -374,9 +374,12 @@ void UniqueIDBDatabaseTransaction::iterateCursor(const IDBRequestData& requestDa
     auto database = m_databaseConnection->database();
     ASSERT(database);
     
-    database->iterateCursor(requestData, data, [this, requestData](auto& error, const IDBGetResult& result) {
+    database->iterateCursor(requestData, data, [this, requestData, option = data.option](auto& error, const IDBGetResult& result) {
         LOG(IndexedDB, "UniqueIDBDatabaseTransaction::iterateCursor (callback)");
 
+        if (option == IndexedDB::CursorIterateOption::DoNotReply)
+            return;
+
         if (error.isNull())
             m_databaseConnection->connectionToClient().didIterateCursor(IDBResultData::iterateCursorSuccess(requestData.requestIdentifier(), result));
         else
index 613604a..9d5ab94 100644 (file)
@@ -35,12 +35,13 @@ namespace WebCore {
 struct IDBCursorRecord {
     IDBKeyData key;
     IDBKeyData primaryKey;
-    std::unique_ptr<IDBValue> value;
+    IDBValue value;
 
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static bool decode(Decoder&, IDBCursorRecord&);
 
-    size_t size() const { return key.size() + primaryKey.size() + (value ? value->size() : 0); }
+    IDBCursorRecord isolatedCopy() const;
+    size_t size() const { return key.size() + primaryKey.size() + value.size(); }
 };
 
 template<class Encoder>
@@ -64,6 +65,11 @@ bool IDBCursorRecord::decode(Decoder& decoder, IDBCursorRecord& record)
     return true;
 }
 
+inline IDBCursorRecord IDBCursorRecord::isolatedCopy() const
+{
+    return { key.isolatedCopy(), primaryKey.isolatedCopy(), value.isolatedCopy() };
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index f1f0bb3..ad9f4a4 100644 (file)
@@ -33,7 +33,7 @@ namespace WebCore {
 
 IDBIterateCursorData IDBIterateCursorData::isolatedCopy() const
 {
-    return { keyData.isolatedCopy(), primaryKeyData.isolatedCopy(), count };
+    return { keyData.isolatedCopy(), primaryKeyData.isolatedCopy(), count, option };
 }
 
 #if !LOG_DISABLED
index 8d315c5..4df6c64 100644 (file)
@@ -35,6 +35,7 @@ struct IDBIterateCursorData {
     IDBKeyData keyData;
     IDBKeyData primaryKeyData;
     unsigned count;
+    IndexedDB::CursorIterateOption option { IndexedDB::CursorIterateOption::Reply };
 
     WEBCORE_EXPORT IDBIterateCursorData isolatedCopy() const;
 
@@ -50,6 +51,7 @@ template<class Encoder>
 void IDBIterateCursorData::encode(Encoder& encoder) const
 {
     encoder << keyData << primaryKeyData << static_cast<uint64_t>(count);
+    encoder.encodeEnum(option);
 }
 
 template<class Decoder>
@@ -73,9 +75,11 @@ bool IDBIterateCursorData::decode(Decoder& decoder, IDBIterateCursorData& iterat
 
     if (count > std::numeric_limits<unsigned>::max())
         return false;
-
     iteratorCursorData.count = static_cast<unsigned>(count);
 
+    if (!decoder.decodeEnum(iteratorCursorData.option))
+        return false;
+
     return true;
 }
 
index 3d021ce..4a36477 100644 (file)
                510D4A34103165EE0049EA54 /* SocketStreamError.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A2E103165EE0049EA54 /* SocketStreamError.h */; settings = {ATTRIBUTES = (Private, ); }; };
                510D4A37103165EE0049EA54 /* SocketStreamHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A31103165EE0049EA54 /* SocketStreamHandle.h */; settings = {ATTRIBUTES = (Private, ); }; };
                510D4A38103165EE0049EA54 /* SocketStreamHandleClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A32103165EE0049EA54 /* SocketStreamHandleClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               5110FCFC1E03641D006F8D0B /* IDBCursorRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 5110FCFB1E0362A5006F8D0B /* IDBCursorRecord.h */; };
+               5110FCFC1E03641D006F8D0B /* IDBCursorRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 5110FCFB1E0362A5006F8D0B /* IDBCursorRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
                511EC1281C50AACA0032F983 /* IDBSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 511EC1261C50AA570032F983 /* IDBSerialization.h */; };
                511EC12C1C50ABBF0032F983 /* SQLiteIDBTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 511EC12A1C50ABBA0032F983 /* SQLiteIDBTransaction.h */; };
                511EC1301C50ABF50032F983 /* SQLiteIDBCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 511EC12E1C50ABEC0032F983 /* SQLiteIDBCursor.h */; };