IndexedDB 2.0: Prefetch cursor records in the server.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Dec 2016 01:40:37 +0000 (01:40 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Dec 2016 01:40:37 +0000 (01:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166014

Reviewed by Andy Estes.

No new tests (Covered by existing LayoutTests and PerformanceTests).

This patch implements the followng:
1 - After a backing store cursor completes a fetch in the server, it will schedule the next fetch
    even before the client requests one. It will do this up to a limited number of prefetches.
2 - Once a client request to advance the cursor comes in, we'll work our way through prefetched
    records instead of reading anything from disk, which might then cause us to continue prefetch.
3 - If any changes to the object store occur, it will throw away all previously fetched records
    (There's room for future improvement here)

* Modules/indexeddb/server/IDBBackingStore.h:

* Modules/indexeddb/server/IDBServer.cpp:
(WebCore::IDBServer::IDBServer::postDatabaseTask):

* Modules/indexeddb/server/MemoryIDBBackingStore.h:

* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteRange):
(WebCore::IDBServer::SQLiteIDBBackingStore::prefetchCursor):
* Modules/indexeddb/server/SQLiteIDBBackingStore.h:

* Modules/indexeddb/server/SQLiteIDBCursor.cpp:
(WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged):
(WebCore::IDBServer::SQLiteIDBCursor::prefetch):
(WebCore::IDBServer::SQLiteIDBCursor::advance):
* Modules/indexeddb/server/SQLiteIDBCursor.h:

* Modules/indexeddb/server/UniqueIDBDatabase.cpp:
(WebCore::IDBServer::UniqueIDBDatabase::performIterateCursor):
(WebCore::IDBServer::UniqueIDBDatabase::performPrefetchCursor):
(WebCore::IDBServer::UniqueIDBDatabase::postDatabaseTask):
* Modules/indexeddb/server/UniqueIDBDatabase.h:

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

Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
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

index 13482b8..b67f66a 100644 (file)
@@ -1,3 +1,44 @@
+2016-12-18  Brady Eidson  <beidson@apple.com>
+
+        IndexedDB 2.0: Prefetch cursor records in the server.
+        https://bugs.webkit.org/show_bug.cgi?id=166014
+
+        Reviewed by Andy Estes.
+
+        No new tests (Covered by existing LayoutTests and PerformanceTests).
+
+        This patch implements the followng:
+        1 - After a backing store cursor completes a fetch in the server, it will schedule the next fetch 
+            even before the client requests one. It will do this up to a limited number of prefetches.
+        2 - Once a client request to advance the cursor comes in, we'll work our way through prefetched
+            records instead of reading anything from disk, which might then cause us to continue prefetch.
+        3 - If any changes to the object store occur, it will throw away all previously fetched records
+            (There's room for future improvement here)
+
+        * Modules/indexeddb/server/IDBBackingStore.h:
+        
+        * Modules/indexeddb/server/IDBServer.cpp:
+        (WebCore::IDBServer::IDBServer::postDatabaseTask):
+        
+        * Modules/indexeddb/server/MemoryIDBBackingStore.h:
+        
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteRange):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::prefetchCursor):
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+        
+        * Modules/indexeddb/server/SQLiteIDBCursor.cpp:
+        (WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged):
+        (WebCore::IDBServer::SQLiteIDBCursor::prefetch):
+        (WebCore::IDBServer::SQLiteIDBCursor::advance):
+        * Modules/indexeddb/server/SQLiteIDBCursor.h:
+        
+        * Modules/indexeddb/server/UniqueIDBDatabase.cpp:
+        (WebCore::IDBServer::UniqueIDBDatabase::performIterateCursor):
+        (WebCore::IDBServer::UniqueIDBDatabase::performPrefetchCursor):
+        (WebCore::IDBServer::UniqueIDBDatabase::postDatabaseTask):
+        * Modules/indexeddb/server/UniqueIDBDatabase.h:
+
 2016-12-18  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Changing text direction fires input events with null inputTypes and no data
index 490b908..9dde74c 100644 (file)
@@ -91,6 +91,7 @@ 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 08a6af4..8747643 100644 (file)
@@ -481,7 +481,6 @@ void IDBServer::didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint
 
 void IDBServer::postDatabaseTask(CrossThreadTask&& task)
 {
-    ASSERT(isMainThread());
     m_databaseQueue.append(WTFMove(task));
 }
 
index 37715ac..e93a17d 100644 (file)
@@ -70,6 +70,7 @@ 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 { return false; }
 
     IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final;
     void deleteBackingStore() final;
index 58ca934..fa3755e 100644 (file)
@@ -1606,6 +1606,8 @@ IDBError SQLiteIDBBackingStore::deleteRange(const IDBResourceIdentifier& transac
             return error;
         }
 
+        transaction->notifyCursorsOfChanges(objectStoreID);
+
         return { };
     }
 
@@ -2489,6 +2491,22 @@ IDBError SQLiteIDBBackingStore::iterateCursor(const IDBResourceIdentifier& trans
     return { };
 }
 
+bool SQLiteIDBBackingStore::prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
+{
+    LOG(IndexedDB, "SQLiteIDBBackingStore::prefetchCursor");
+
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB->isOpen());
+
+    auto* cursor = m_cursors.get(cursorIdentifier);
+    if (!cursor || !cursor->transaction() || !cursor->transaction()->inProgress())
+        return false;
+
+    ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
+
+    return cursor->prefetch();
+}
+
 IDBObjectStoreInfo* SQLiteIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
 {
     ASSERT(m_databaseInfo);
index 02e6987..257efe8 100644 (file)
@@ -75,6 +75,7 @@ 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 3764e63..78bcf97 100644 (file)
@@ -42,6 +42,8 @@
 namespace WebCore {
 namespace IDBServer {
 
+static const size_t prefetchLimit = 8;
+
 std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
 {
     auto cursor = std::make_unique<SQLiteIDBCursor>(transaction, info);
@@ -235,6 +237,8 @@ void SQLiteIDBCursor::objectStoreRecordsChanged()
         }
     }
 
+    m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
+
     m_fetchedRecords.clear();
 }
 
@@ -291,18 +295,22 @@ bool SQLiteIDBCursor::bindArguments()
     return true;
 }
 
-void SQLiteIDBCursor::prefetch()
+bool SQLiteIDBCursor::prefetch()
 {
-    ASSERT(!m_fetchedRecords.isEmpty());
-    if (m_fetchedRecords.last().errored || m_fetchedRecords.last().completed)
-        return;
+    LOG(IndexedDB, "SQLiteIDBCursor::prefetch() - Cursor already has %zu fetched records", m_fetchedRecords.size());
+
+    if (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;
 }
 
 bool SQLiteIDBCursor::advance(uint64_t count)
 {
+    LOG(IndexedDB, "SQLiteIDBCursor::advance() - Count %" PRIu64 ", %zu fetched records", count, m_fetchedRecords.size());
     ASSERT(count);
 
     if (!m_fetchedRecords.isEmpty() && m_fetchedRecords.first().isTerminalRecord()) {
@@ -314,7 +322,8 @@ bool SQLiteIDBCursor::advance(uint64_t count)
         m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
 
     // Drop already-fetched records up to `count` to see if we've already fetched the record we're looking for.
-    for (size_t i = 0; i < count && !m_fetchedRecords.isEmpty(); ++i) {
+    bool hadCurrentRecord = !m_fetchedRecords.isEmpty();
+    for (; count && !m_fetchedRecords.isEmpty(); --count) {
         if (m_fetchedRecords.first().isTerminalRecord())
             break;
 
@@ -323,11 +332,16 @@ bool SQLiteIDBCursor::advance(uint64_t count)
 
     // If we still have any records left, the first record is our new current record.
     if (!m_fetchedRecords.isEmpty())
-        return !m_fetchedRecords.first().isTerminalRecord();
+        return true;
 
     ASSERT(m_fetchedRecords.isEmpty());
 
-    for (uint64_t i = 0; i < count; ++i) {
+    // If we started out with a current record, we burnt a count on removing it.
+    // Replace that count now.
+    if (hadCurrentRecord)
+        ++count;
+
+    for (; count; --count) {
         if (!m_fetchedRecords.isEmpty()) {
             ASSERT(m_fetchedRecords.size() == 1);
             m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
index 9c4cd90..4a67300 100644 (file)
@@ -69,7 +69,7 @@ public:
 
     bool advance(uint64_t count);
     bool iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey);
-    void prefetch();
+    bool prefetch();
 
     bool didComplete() const;
     bool didError() const;
index 9910157..2722bbc 100644 (file)
@@ -1232,9 +1232,21 @@ void UniqueIDBDatabase::performIterateCursor(uint64_t callbackIdentifier, const
     IDBGetResult result;
     IDBError error = m_backingStore->iterateCursor(transactionIdentifier, cursorIdentifier, data, result);
 
+    if (error.isNull())
+        postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier));
+
     postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformIterateCursor, callbackIdentifier, error, result));
 }
 
+void UniqueIDBDatabase::performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
+{
+    ASSERT(!isMainThread());
+    LOG(IndexedDB, "(db) UniqueIDBDatabase::performPrefetchCursor");
+
+    if (m_backingStore->prefetchCursor(transactionIdentifier, cursorIdentifier))
+        postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier));
+}
+
 void UniqueIDBDatabase::didPerformIterateCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
 {
     ASSERT(isMainThread());
@@ -1685,7 +1697,6 @@ void UniqueIDBDatabase::transactionCompleted(RefPtr<UniqueIDBDatabaseTransaction
 
 void UniqueIDBDatabase::postDatabaseTask(CrossThreadTask&& task)
 {
-    ASSERT(isMainThread());
     m_databaseQueue.append(WTFMove(task));
     ++m_queuedTaskCount;
 
index 86eaba2..02c73fa 100644 (file)
@@ -165,6 +165,8 @@ private:
     void performDeleteRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&);
     void performOpenCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&);
     void performIterateCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&);
+    void performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier);
+
     void performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo&);
     void performUnconditionalDeleteBackingStore();