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
+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
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;
void IDBServer::postDatabaseTask(CrossThreadTask&& task)
{
- ASSERT(isMainThread());
m_databaseQueue.append(WTFMove(task));
}
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;
return error;
}
+ transaction->notifyCursorsOfChanges(objectStoreID);
+
return { };
}
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);
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;
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);
}
}
+ m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
+
m_fetchedRecords.clear();
}
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()) {
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;
// 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;
bool advance(uint64_t count);
bool iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey);
- void prefetch();
+ bool prefetch();
bool didComplete() const;
bool didError() 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());
void UniqueIDBDatabase::postDatabaseTask(CrossThreadTask&& task)
{
- ASSERT(isMainThread());
m_databaseQueue.append(WTFMove(task));
++m_queuedTaskCount;
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();