- Revision
- 278786
- Author
- cdu...@apple.com
- Date
- 2021-06-11 15:56:18 -0700 (Fri, 11 Jun 2021)
Log Message
[WK2] Batch local storage database writes using transactions
https://bugs.webkit.org/show_bug.cgi?id=226938
Reviewed by Geoff Garen.
Batch local storage writes using transactions, to improve performance
and reduce disk writes. In this patch, we use a simple time-based
approach where we batch transactions happening in the same 500ms
period.
* NetworkProcess/WebStorage/LocalStorageDatabase.cpp:
(WebKit::LocalStorageDatabase::create):
(WebKit::LocalStorageDatabase::LocalStorageDatabase):
(WebKit::LocalStorageDatabase::startTransactionIfNecessary):
(WebKit::LocalStorageDatabase::removeItem):
(WebKit::LocalStorageDatabase::setItem):
(WebKit::LocalStorageDatabase::clear):
(WebKit::LocalStorageDatabase::close):
* NetworkProcess/WebStorage/LocalStorageDatabase.h:
* NetworkProcess/WebStorage/StorageArea.cpp:
(WebKit::StorageArea::ensureDatabase const):
Modified Paths
Diff
Modified: trunk/Source/WebKit/ChangeLog (278785 => 278786)
--- trunk/Source/WebKit/ChangeLog 2021-06-11 22:37:40 UTC (rev 278785)
+++ trunk/Source/WebKit/ChangeLog 2021-06-11 22:56:18 UTC (rev 278786)
@@ -1,3 +1,27 @@
+2021-06-11 Chris Dumez <cdu...@apple.com>
+
+ [WK2] Batch local storage database writes using transactions
+ https://bugs.webkit.org/show_bug.cgi?id=226938
+
+ Reviewed by Geoff Garen.
+
+ Batch local storage writes using transactions, to improve performance
+ and reduce disk writes. In this patch, we use a simple time-based
+ approach where we batch transactions happening in the same 500ms
+ period.
+
+ * NetworkProcess/WebStorage/LocalStorageDatabase.cpp:
+ (WebKit::LocalStorageDatabase::create):
+ (WebKit::LocalStorageDatabase::LocalStorageDatabase):
+ (WebKit::LocalStorageDatabase::startTransactionIfNecessary):
+ (WebKit::LocalStorageDatabase::removeItem):
+ (WebKit::LocalStorageDatabase::setItem):
+ (WebKit::LocalStorageDatabase::clear):
+ (WebKit::LocalStorageDatabase::close):
+ * NetworkProcess/WebStorage/LocalStorageDatabase.h:
+ * NetworkProcess/WebStorage/StorageArea.cpp:
+ (WebKit::StorageArea::ensureDatabase const):
+
2021-06-11 Alex Christensen <achristen...@webkit.org>
Don't include certificate info in WebURLSchemeTask::didReceiveResponse
Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp (278785 => 278786)
--- trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp 2021-06-11 22:37:40 UTC (rev 278785)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp 2021-06-11 22:56:18 UTC (rev 278786)
@@ -40,14 +40,16 @@
constexpr auto getItemsQueryString { "SELECT key, value FROM ItemTable"_s };
constexpr unsigned maximumSizeForValuesKeptInMemory { 1024 }; // 1 KB
+constexpr Seconds transactionDuration { 500_ms };
-Ref<LocalStorageDatabase> LocalStorageDatabase::create(String&& databasePath, unsigned quotaInBytes)
+Ref<LocalStorageDatabase> LocalStorageDatabase::create(Ref<WorkQueue>&& workQueue, String&& databasePath, unsigned quotaInBytes)
{
- return adoptRef(*new LocalStorageDatabase(WTFMove(databasePath), quotaInBytes));
+ return adoptRef(*new LocalStorageDatabase(WTFMove(workQueue), WTFMove(databasePath), quotaInBytes));
}
-LocalStorageDatabase::LocalStorageDatabase(String&& databasePath, unsigned quotaInBytes)
- : m_databasePath(WTFMove(databasePath))
+LocalStorageDatabase::LocalStorageDatabase(Ref<WorkQueue>&& workQueue, String&& databasePath, unsigned quotaInBytes)
+ : m_workQueue(WTFMove(workQueue))
+ , m_databasePath(WTFMove(databasePath))
, m_quotaInBytes(quotaInBytes)
{
ASSERT(!RunLoop::isMain());
@@ -100,6 +102,21 @@
return true;
}
+void LocalStorageDatabase::startTransactionIfNecessary()
+{
+ if (!m_transaction)
+ m_transaction = makeUnique<SQLiteTransaction>(m_database);
+
+ if (m_transaction->inProgress())
+ return;
+
+ m_transaction->begin();
+ m_workQueue->dispatchAfter(transactionDuration, [weakThis = makeWeakPtr(*this)] {
+ if (weakThis)
+ weakThis->m_transaction->commit();
+ });
+}
+
bool LocalStorageDatabase::migrateItemTableIfNeeded()
{
ASSERT(!RunLoop::isMain());
@@ -184,6 +201,7 @@
if (!m_database.isOpen())
return;
+ startTransactionIfNecessary();
oldValue = item(key);
if (oldValue.isNull())
return;
@@ -248,6 +266,7 @@
if (!m_database.isOpen())
return;
+ startTransactionIfNecessary();
oldValue = item(key);
auto insertStatement = scopedStatement(m_insertStatement, "INSERT INTO ItemTable VALUES (?, ?)"_s);
@@ -280,6 +299,7 @@
if (m_items && m_items->isEmpty())
return false;
+ startTransactionIfNecessary();
auto clearStatement = scopedStatement(m_clearStatement, "DELETE FROM ItemTable"_s);
if (!clearStatement) {
LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database");
@@ -300,6 +320,12 @@
return m_database.lastChanges() > 0;
}
+void LocalStorageDatabase::flushToDisk()
+{
+ if (m_transaction)
+ m_transaction->commit();
+}
+
void LocalStorageDatabase::close()
{
ASSERT(!RunLoop::isMain());
@@ -315,6 +341,8 @@
m_getItemsStatement = nullptr;
m_deleteItemStatement = nullptr;
m_items = std::nullopt;
+ if (auto transaction = std::exchange(m_transaction, nullptr))
+ transaction->commit();
if (m_database.isOpen())
m_database.close();
Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h (278785 => 278786)
--- trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h 2021-06-11 22:37:40 UTC (rev 278785)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h 2021-06-11 22:56:18 UTC (rev 278786)
@@ -27,9 +27,12 @@
#include <WebCore/SQLiteDatabase.h>
#include <wtf/HashMap.h>
+#include <wtf/WeakPtr.h>
+#include <wtf/WorkQueue.h>
namespace WebCore {
class SQLiteStatementAutoResetScope;
+class SQLiteTransaction;
struct SecurityOriginData;
}
@@ -36,9 +39,9 @@
namespace WebKit {
-class LocalStorageDatabase : public RefCounted<LocalStorageDatabase> {
+class LocalStorageDatabase : public RefCounted<LocalStorageDatabase>, public CanMakeWeakPtr<LocalStorageDatabase> {
public:
- static Ref<LocalStorageDatabase> create(String&& databasePath, unsigned quotaInBytes);
+ static Ref<LocalStorageDatabase> create(Ref<WorkQueue>&&, String&& databasePath, unsigned quotaInBytes);
~LocalStorageDatabase();
HashMap<String, String> items() const;
@@ -51,14 +54,16 @@
// Will block until all pending changes have been written to disk.
void close();
+ void flushToDisk();
void handleLowMemoryWarning();
private:
- LocalStorageDatabase(String&& databasePath, unsigned quotaInBytes);
+ LocalStorageDatabase(Ref<WorkQueue>&&, String&& databasePath, unsigned quotaInBytes);
enum class ShouldCreateDatabase : bool { No, Yes };
bool openDatabase(ShouldCreateDatabase);
+ void startTransactionIfNecessary();
bool migrateItemTableIfNeeded();
bool databaseIsEmpty() const;
@@ -66,8 +71,10 @@
WebCore::SQLiteStatementAutoResetScope scopedStatement(std::unique_ptr<WebCore::SQLiteStatement>&, ASCIILiteral query) const;
+ Ref<WorkQueue> m_workQueue;
String m_databasePath;
mutable WebCore::SQLiteDatabase m_database;
+ std::unique_ptr<WebCore::SQLiteTransaction> m_transaction;
const unsigned m_quotaInBytes { 0 };
bool m_isClosed { false };
Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp (278785 => 278786)
--- trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp 2021-06-11 22:37:40 UTC (rev 278785)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp 2021-06-11 22:56:18 UTC (rev 278786)
@@ -175,7 +175,7 @@
// We open the database here even if we've already imported our items to ensure that the database is open if we need to write to it.
if (!m_localStorageDatabase) {
auto* localStorageDatabaseTracker = m_localStorageNamespace->storageManager()->localStorageDatabaseTracker();
- m_localStorageDatabase = LocalStorageDatabase::create(localStorageDatabaseTracker->databasePath(m_securityOrigin), m_quotaInBytes);
+ m_localStorageDatabase = LocalStorageDatabase::create(m_queue.copyRef(), localStorageDatabaseTracker->databasePath(m_securityOrigin), m_quotaInBytes);
m_localStorageDatabase->openIfExisting();
}
return *m_localStorageDatabase;
@@ -199,6 +199,12 @@
m_localStorageDatabase->close();
}
+void StorageArea::syncToDatabase()
+{
+ if (m_localStorageDatabase)
+ m_localStorageDatabase->flushToDisk();
+}
+
void StorageArea::handleLowMemoryWarning()
{
if (m_localStorageDatabase)
Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h (278785 => 278786)
--- trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h 2021-06-11 22:37:40 UTC (rev 278785)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h 2021-06-11 22:56:18 UTC (rev 278786)
@@ -71,6 +71,7 @@
void close();
+ void syncToDatabase();
void handleLowMemoryWarning();
private:
Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp (278785 => 278786)
--- trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp 2021-06-11 22:37:40 UTC (rev 278785)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp 2021-06-11 22:56:18 UTC (rev 278786)
@@ -155,7 +155,16 @@
{
ASSERT(RunLoop::isMain());
- m_queue->dispatchSync([] { });
+ BinarySemaphore semaphore;
+ m_queue->dispatch([this, &semaphore] {
+ for (const auto& storageArea : m_storageAreas.values()) {
+ ASSERT(storageArea);
+ if (storageArea)
+ storageArea->syncToDatabase();
+ }
+ semaphore.signal();
+ });
+ semaphore.wait();
}
void StorageManagerSet::suspend(CompletionHandler<void()>&& completionHandler)