Bug 1286798 - Part 45: Delay flushing to disk using a timer; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:49:40 +0100
changeset 508043 fc0bce55c20d8acc8e099d0055978d9c167b3ed2
parent 508042 c29b8b8fbf4d209d5e98cde786a6fe58c2dce5a5
child 508044 6710732a8d0752472bc82d8fd2faba8f58e7f545
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1286798
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -109,16 +109,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;
@@ -902,16 +904,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);
@@ -921,16 +924,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
@@ -1195,27 +1206,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
@@ -1286,16 +1299,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;
@@ -1315,24 +1337,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;
 };
@@ -3109,16 +3131,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--) {
@@ -3469,31 +3493,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);
 
@@ -3502,16 +3528,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)
@@ -3562,20 +3597,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()
@@ -3640,16 +3674,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);
 }
 
@@ -3692,17 +3776,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 =