Bug 1286798 - Part 45: Delay flushing to disk using a timer; r=asuth draft
authorJan Varga <jan.varga@gmail.com>
Wed, 24 Oct 2018 06:59:09 +0200
changeset 481723 2d1ec32bf081c00c73f70dd7741338eeb2c9e5ef
parent 481722 7fe2c3512187460b4deed2618f56553e717bd4bd
child 481724 93b458d08f0881bf310e7b063e6c8bdc9ccd2e2a
push id10
push userbugmail@asutherland.org
push dateSun, 18 Nov 2018 18:57:42 +0000
reviewersasuth
bugs1286798
milestone65.0a1
Bug 1286798 - Part 45: Delay flushing to disk using a timer; r=asuth This improves performance even more by grouping database operations from multiple checkpoints and possibly from multiple processes.
dom/localstorage/ActorsParent.cpp
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -108,16 +108,18 @@ const uint32_t kSQLiteGrowthIncrement = 
 static_assert(kSQLiteGrowthIncrement >= 0 &&
               kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
               kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
               "Must be 0 (disabled) or a positive multiple of the page size!");
 
 #define DATA_FILE_NAME "data.sqlite"
 #define JOURNAL_FILE_NAME "data.sqlite-journal"
 
+const uint32_t kFlushTimeoutMs = 5000;
+
 const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
 
 const uint32_t kDefaultOriginLimitKB = 5 * 1024;
 const uint32_t kDefaultSnapshotPrefill = 4096;
 const char kDefaultQuotaPref[] = "dom.storage.default_quota";
 const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
 
 const uint32_t kPreparedDatastoreTimeoutMs = 20000;
@@ -883,16 +885,17 @@ class WriteOptimizer final
 
 public:
   WriteOptimizer()
   { }
 
   WriteOptimizer(WriteOptimizer&& aWriteOptimizer)
     : mClearInfo(std::move(aWriteOptimizer.mClearInfo))
   {
+    AssertIsOnBackgroundThread();
     MOZ_ASSERT(&aWriteOptimizer != this);
 
     mWriteInfos.SwapElements(aWriteOptimizer.mWriteInfos);
   }
 
   void
   AddItem(const nsString& aKey,
           const nsString& aValue);
@@ -902,16 +905,24 @@ public:
              const nsString& aValue);
 
   void
   RemoveItem(const nsString& aKey);
 
   void
   Clear();
 
+  bool
+  HasWrites() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mClearInfo || !mWriteInfos.IsEmpty();
+  }
+
   void
   ApplyWrites(nsTArray<LSItemInfo>& aOrderedItems);
 
   nsresult
   PerformWrites(Connection* aConnection);
 };
 
 class WriteOptimizer::WriteInfo
@@ -1176,27 +1187,29 @@ private:
 class Connection final
 {
   friend class ConnectionThread;
 
 public:
   class CachedStatement;
 
 private:
-  class EndUpdateBatchOp;
+  class FlushOp;
   class CloseOp;
 
   RefPtr<ConnectionThread> mConnectionThread;
+  nsCOMPtr<nsITimer> mFlushTimer;
   nsCOMPtr<mozIStorageConnection> mStorageConnection;
   nsAutoPtr<ArchivedOriginInfo> mArchivedOriginInfo;
   nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
     mCachedStatements;
   WriteOptimizer mWriteOptimizer;
   const nsCString mOrigin;
   const nsString mFilePath;
+  bool mFlushScheduled;
 #ifdef DEBUG
   bool mInUpdateBatch;
 #endif
 
 public:
   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Connection)
 
   void
@@ -1267,16 +1280,25 @@ public:
 private:
   // Only created by ConnectionThread.
   Connection(ConnectionThread* aConnectionThread,
              const nsACString& aOrigin,
              const nsAString& aFilePath,
              nsAutoPtr<ArchivedOriginInfo>&& aArchivedOriginInfo);
 
   ~Connection();
+
+  void
+  ScheduleFlush();
+
+  void
+  Flush();
+
+  static void
+  FlushTimerCallback(nsITimer* aTimer, void* aClosure);
 };
 
 class Connection::CachedStatement final
 {
   friend class Connection;
 
   nsCOMPtr<mozIStorageStatement> mStatement;
   Maybe<mozStorageStatementScoper> mScoper;
@@ -1296,24 +1318,24 @@ private:
   Assign(Connection* aConnection,
          already_AddRefed<mozIStorageStatement> aStatement);
 
   // No funny business allowed.
   CachedStatement(const CachedStatement&) = delete;
   CachedStatement& operator=(const CachedStatement&) = delete;
 };
 
-class Connection::EndUpdateBatchOp final
+class Connection::FlushOp final
   : public ConnectionDatastoreOperationBase
 {
   WriteOptimizer mWriteOptimizer;
 
 public:
-  EndUpdateBatchOp(Connection* aConnection,
-                   WriteOptimizer&& aWriteOptimizer)
+  FlushOp(Connection* aConnection,
+          WriteOptimizer&& aWriteOptimizer)
     : ConnectionDatastoreOperationBase(aConnection)
     , mWriteOptimizer(std::move(aWriteOptimizer))
   { }
 
 private:
   nsresult
   DoDatastoreWork() override;
 };
@@ -3094,16 +3116,18 @@ WriteOptimizer::Clear()
   if (!mClearInfo) {
     mClearInfo = new ClearInfo();
   }
 }
 
 void
 WriteOptimizer::ApplyWrites(nsTArray<LSItemInfo>& aOrderedItems)
 {
+  AssertIsOnBackgroundThread();
+
   if (mClearInfo) {
     aOrderedItems.Clear();
     mClearInfo = nullptr;
   }
 
   for (int32_t index = aOrderedItems.Length() - 1;
        index >= 0;
        index--) {
@@ -3454,31 +3478,33 @@ ConnectionDatastoreOperationBase::Run()
 Connection::Connection(ConnectionThread* aConnectionThread,
                        const nsACString& aOrigin,
                        const nsAString& aFilePath,
                        nsAutoPtr<ArchivedOriginInfo>&& aArchivedOriginInfo)
   : mConnectionThread(aConnectionThread)
   , mArchivedOriginInfo(std::move(aArchivedOriginInfo))
   , mOrigin(aOrigin)
   , mFilePath(aFilePath)
+  , mFlushScheduled(false)
 #ifdef DEBUG
   , mInUpdateBatch(false)
 #endif
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!aOrigin.IsEmpty());
   MOZ_ASSERT(!aFilePath.IsEmpty());
 }
 
 Connection::~Connection()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mStorageConnection);
   MOZ_ASSERT(!mCachedStatements.Count());
   MOZ_ASSERT(!mInUpdateBatch);
+  MOZ_ASSERT(!mFlushScheduled);
 }
 
 void
 Connection::Dispatch(ConnectionDatastoreOperationBase* aOp)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mConnectionThread);
 
@@ -3487,16 +3513,25 @@ Connection::Dispatch(ConnectionDatastore
 }
 
 void
 Connection::Close(nsIRunnable* aCallback)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aCallback);
 
+  if (mFlushScheduled) {
+    MOZ_ASSERT(mFlushTimer);
+    MOZ_ALWAYS_SUCCEEDS(mFlushTimer->Cancel());
+
+    Flush();
+
+    mFlushTimer = nullptr;
+  }
+
   RefPtr<CloseOp> op = new CloseOp(this, aCallback);
 
   Dispatch(op);
 }
 
 void
 Connection::AddItem(const nsString& aKey,
                     const nsString& aValue)
@@ -3547,20 +3582,19 @@ Connection::BeginUpdateBatch()
 }
 
 void
 Connection::EndUpdateBatch()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mInUpdateBatch);
 
-  RefPtr<EndUpdateBatchOp> op =
-    new EndUpdateBatchOp(this, std::move(mWriteOptimizer));
-
-  Dispatch(op);
+  if (mWriteOptimizer.HasWrites() && !mFlushScheduled)  {
+    ScheduleFlush();
+  }
 
 #ifdef DEBUG
   mInUpdateBatch = false;
 #endif
 }
 
 nsresult
 Connection::EnsureStorageConnection()
@@ -3625,16 +3659,66 @@ Connection::GetCachedStatement(const nsA
 
     mCachedStatements.Put(aQuery, stmt);
   }
 
   aCachedStatement->Assign(this, stmt.forget());
   return NS_OK;
 }
 
+void
+Connection::ScheduleFlush()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mWriteOptimizer.HasWrites());
+  MOZ_ASSERT(!mFlushScheduled);
+
+  if (!mFlushTimer) {
+    mFlushTimer = NS_NewTimer();
+    MOZ_ASSERT(mFlushTimer);
+  }
+
+  MOZ_ALWAYS_SUCCEEDS(
+    mFlushTimer->InitWithNamedFuncCallback(FlushTimerCallback,
+                                           this,
+                                           kFlushTimeoutMs,
+                                           nsITimer::TYPE_ONE_SHOT,
+                                           "Connection::FlushTimerCallback"));
+
+  mFlushScheduled = true;
+}
+
+void
+Connection::Flush()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFlushScheduled);
+
+  if (mWriteOptimizer.HasWrites()) {
+    RefPtr<FlushOp> op = new FlushOp(this, std::move(mWriteOptimizer));
+
+    Dispatch(op);
+  }
+
+  mFlushScheduled = false;
+}
+
+// static
+void
+Connection::FlushTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+
+  auto* self = static_cast<Connection*>(aClosure);
+  MOZ_ASSERT(self);
+  MOZ_ASSERT(self->mFlushScheduled);
+
+  self->Flush();
+}
+
 Connection::
 CachedStatement::CachedStatement()
 {
   AssertIsOnConnectionThread();
 
   MOZ_COUNT_CTOR(Connection::CachedStatement);
 }
 
@@ -3677,17 +3761,17 @@ CachedStatement::Assign(Connection* aCon
 
   if (mStatement) {
     mScoper.emplace(mStatement);
   }
 }
 
 nsresult
 Connection::
-EndUpdateBatchOp::DoDatastoreWork()
+FlushOp::DoDatastoreWork()
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mConnection);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<mozIStorageConnection> storageConnection =