Bug 1286798 - Part 46: Add a pref for database shadowing; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:49:43 +0100
changeset 508044 6710732a8d0752472bc82d8fd2faba8f58e7f545
parent 508043 fc0bce55c20d8acc8e099d0055978d9c167b3ed2
child 508045 b9badcc1709de40910e1c13b7041d3d4b85d2683
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 46: Add a pref for database shadowing; r=asuth
dom/localstorage/ActorsParent.cpp
modules/libpref/init/all.js
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -114,18 +114,20 @@ static_assert(kSQLiteGrowthIncrement >= 
 #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 kDefaultShadowWrites = true;
 const uint32_t kDefaultSnapshotPrefill = 4096;
 const char kDefaultQuotaPref[] = "dom.storage.default_quota";
+const char kShadowWritesPref[] = "dom.storage.shadow_writes";
 const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
 
 const uint32_t kPreparedDatastoreTimeoutMs = 20000;
 
 #define LS_ARCHIVE_FILE_NAME "ls-archive.sqlite"
 #define WEB_APPS_STORE_FILE_NAME "webappsstore.sqlite"
 
 // Shadow database Write Ahead Log's maximum size is 512KB
@@ -936,34 +938,34 @@ public:
 
     return mClearInfo || !mWriteInfos.IsEmpty();
   }
 
   void
   ApplyWrites(nsTArray<LSItemInfo>& aOrderedItems);
 
   nsresult
-  PerformWrites(Connection* aConnection);
+  PerformWrites(Connection* aConnection, bool aShadowWrites);
 };
 
 class WriteOptimizer::WriteInfo
 {
 public:
   enum Type {
     AddItem = 0,
     UpdateItem,
     RemoveItem,
     Clear
   };
 
   virtual Type
   GetType() = 0;
 
   virtual nsresult
-  Perform(Connection* aConnection) = 0;
+  Perform(Connection* aConnection, bool aShadowWrites) = 0;
 
   virtual ~WriteInfo() = default;
 };
 
 class WriteOptimizer::AddItemInfo
   : public WriteInfo
 {
   nsString mKey;
@@ -991,17 +993,17 @@ public:
 private:
   Type
   GetType() override
   {
     return AddItem;
   }
 
   nsresult
-  Perform(Connection* aConnection) override;
+  Perform(Connection* aConnection, bool aShadowWrites) override;
 };
 
 class WriteOptimizer::UpdateItemInfo final
   : public AddItemInfo
 {
 public:
   UpdateItemInfo(const nsAString& aKey,
                  const nsAString& aValue)
@@ -1035,17 +1037,17 @@ public:
 private:
   Type
   GetType() override
   {
     return RemoveItem;
   }
 
   nsresult
-  Perform(Connection* aConnection) override;
+  Perform(Connection* aConnection, bool aShadowWrites) override;
 };
 
 class WriteOptimizer::ClearInfo final
   : public WriteInfo
 {
 public:
   ClearInfo()
   { }
@@ -1053,17 +1055,17 @@ public:
 private:
   Type
   GetType() override
   {
     return Clear;
   }
 
   nsresult
-  Perform(Connection* aConnection) override;
+  Perform(Connection* aConnection, bool aShadowWrites) override;
 };
 
 class DatastoreOperationBase
   : public Runnable
 {
   nsCOMPtr<nsIEventTarget> mOwningEventTarget;
   nsresult mResultCode;
   Atomic<bool> mMayProceedOnNonOwningThread;
@@ -1341,23 +1343,21 @@ private:
   CachedStatement(const CachedStatement&) = delete;
   CachedStatement& operator=(const CachedStatement&) = delete;
 };
 
 class Connection::FlushOp final
   : public ConnectionDatastoreOperationBase
 {
   WriteOptimizer mWriteOptimizer;
+  bool mShadowWrites;
 
 public:
   FlushOp(Connection* aConnection,
-          WriteOptimizer&& aWriteOptimizer)
-    : ConnectionDatastoreOperationBase(aConnection)
-    , mWriteOptimizer(std::move(aWriteOptimizer))
-  { }
+          WriteOptimizer&& aWriteOptimizer);
 
 private:
   nsresult
   DoDatastoreWork() override;
 };
 
 class Connection::CloseOp final
   : public ConnectionDatastoreOperationBase
@@ -2572,16 +2572,17 @@ typedef nsRefPtrHashtable<nsUint64HashKe
 StaticAutoPtr<PreparedObserverHashtable> gPreparedObsevers;
 
 typedef nsClassHashtable<nsCStringHashKey, nsTArray<Observer*>>
   ObserverHashtable;
 
 StaticAutoPtr<ObserverHashtable> gObservers;
 
 Atomic<uint32_t, Relaxed> gOriginLimitKB(kDefaultOriginLimitKB);
+Atomic<bool> gShadowWrites(kDefaultShadowWrites);
 Atomic<int32_t, Relaxed> gSnapshotPrefill(kDefaultSnapshotPrefill);
 
 typedef nsDataHashtable<nsCStringHashKey, int64_t> UsageHashtable;
 
 // Can only be touched on the Quota Manager I/O thread.
 StaticAutoPtr<UsageHashtable> gUsages;
 
 typedef nsTHashtable<nsCStringHashKey> ArchivedOriginHashtable;
@@ -2725,16 +2726,26 @@ GetUsage(mozIStorageConnection* aConnect
     return rv;
   }
 
   *aUsage = usage;
   return NS_OK;
 }
 
 void
+ShadowWritesPrefChangedCallback(const char* aPrefName, void* aClosure)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!strcmp(aPrefName, kShadowWritesPref));
+  MOZ_ASSERT(!aClosure);
+
+  gShadowWrites = Preferences::GetBool(aPrefName, kDefaultShadowWrites);
+}
+
+void
 SnapshotPrefillPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!strcmp(aPrefName, kSnapshotPrefillPref));
   MOZ_ASSERT(!aClosure);
 
   int32_t snapshotPrefill =
     Preferences::GetInt(aPrefName, kDefaultSnapshotPrefill);
@@ -3184,43 +3195,43 @@ WriteOptimizer::ApplyWrites(nsTArray<LSI
     itemInfo->key() = addItemInfo->GetKey();
     itemInfo->value() = addItemInfo->GetValue();
   }
 
   mWriteInfos.Clear();
 }
 
 nsresult
-WriteOptimizer::PerformWrites(Connection* aConnection)
+WriteOptimizer::PerformWrites(Connection* aConnection, bool aShadowWrites)
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(aConnection);
 
   nsresult rv;
 
   if (mClearInfo) {
-    rv = mClearInfo->Perform(aConnection);
+    rv = mClearInfo->Perform(aConnection, aShadowWrites);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   for (auto iter = mWriteInfos.ConstIter(); !iter.Done(); iter.Next()) {
-    rv = iter.Data()->Perform(aConnection);
+    rv = iter.Data()->Perform(aConnection, aShadowWrites);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 WriteOptimizer::
-AddItemInfo::Perform(Connection* aConnection)
+AddItemInfo::Perform(Connection* aConnection, bool aShadowWrites)
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(aConnection);
 
   Connection::CachedStatement stmt;
   nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
     "INSERT OR REPLACE INTO data (key, value) "
     "VALUES(:key, :value)"),
@@ -3239,16 +3250,20 @@ AddItemInfo::Perform(Connection* aConnec
     return rv;
   }
 
   rv = stmt->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (!aShadowWrites) {
+    return NS_OK;
+  }
+
   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
     "INSERT OR REPLACE INTO shadow.webappsstore2 "
       "(originAttributes, originKey, scope, key, value) "
       "VALUES (:originAttributes, :originKey, :scope, :key, :value) "),
     &stmt);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -3284,17 +3299,17 @@ AddItemInfo::Perform(Connection* aConnec
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 WriteOptimizer::
-RemoveItemInfo::Perform(Connection* aConnection)
+RemoveItemInfo::Perform(Connection* aConnection, bool aShadowWrites)
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(aConnection);
 
   Connection::CachedStatement stmt;
   nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
     "DELETE FROM data "
       "WHERE key = :key;"),
@@ -3308,16 +3323,20 @@ RemoveItemInfo::Perform(Connection* aCon
     return rv;
   }
 
   rv = stmt->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (!aShadowWrites) {
+    return NS_OK;
+  }
+
   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
     "DELETE FROM shadow.webappsstore2 "
       "WHERE originAttributes = :originAttributes "
       "AND originKey = :originKey "
       "AND key = :key;"),
     &stmt);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -3338,17 +3357,17 @@ RemoveItemInfo::Perform(Connection* aCon
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 WriteOptimizer::
-ClearInfo::Perform(Connection* aConnection)
+ClearInfo::Perform(Connection* aConnection, bool aShadowWrites)
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(aConnection);
 
   Connection::CachedStatement stmt;
   nsresult rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
     "DELETE FROM data;"),
     &stmt);
@@ -3356,16 +3375,20 @@ ClearInfo::Perform(Connection* aConnecti
     return rv;
   }
 
   rv = stmt->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (!aShadowWrites) {
+    return NS_OK;
+  }
+
   rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
     "DELETE FROM shadow.webappsstore2 "
       "WHERE originAttributes = :originAttributes "
       "AND originKey = :originKey;"),
     &stmt);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -3774,66 +3797,80 @@ CachedStatement::Assign(Connection* aCon
 
   mStatement = aStatement;
 
   if (mStatement) {
     mScoper.emplace(mStatement);
   }
 }
 
+Connection::
+FlushOp::FlushOp(Connection* aConnection,
+                 WriteOptimizer&& aWriteOptimizer)
+  : ConnectionDatastoreOperationBase(aConnection)
+  , mWriteOptimizer(std::move(aWriteOptimizer))
+  , mShadowWrites(gShadowWrites)
+{
+}
+
 nsresult
 Connection::
 FlushOp::DoDatastoreWork()
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mConnection);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<mozIStorageConnection> storageConnection =
     mConnection->StorageConnection();
   MOZ_ASSERT(storageConnection);
 
-  nsresult rv = AttachShadowDatabase(quotaManager->GetBasePath(),
-                                     storageConnection);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  nsresult rv;
+
+  if (mShadowWrites) {
+    rv = AttachShadowDatabase(quotaManager->GetBasePath(), storageConnection);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   CachedStatement stmt;
   rv = mConnection->GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"),
                                        &stmt);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = stmt->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = mWriteOptimizer.PerformWrites(mConnection);
+  rv = mWriteOptimizer.PerformWrites(mConnection, mShadowWrites);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = mConnection->GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = stmt->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = DetachShadowDatabase(storageConnection);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  if (mShadowWrites) {
+    rv = DetachShadowDatabase(storageConnection);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 Connection::
 CloseOp::DoDatastoreWork()
@@ -6974,16 +7011,19 @@ QuotaClient::RegisterObservers(nsIEventT
     }
 
     if (NS_FAILED(Preferences::AddAtomicUintVarCache(&gOriginLimitKB,
                                                      kDefaultQuotaPref,
                                                      kDefaultOriginLimitKB))) {
       NS_WARNING("Unable to respond to default quota pref changes!");
     }
 
+    Preferences::RegisterCallbackAndCall(ShadowWritesPrefChangedCallback,
+                                         kShadowWritesPref);
+
     Preferences::RegisterCallbackAndCall(SnapshotPrefillPrefChangedCallback,
                                          kSnapshotPrefillPref);
 
     sObserversRegistered = true;
   }
 
   return NS_OK;
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1287,16 +1287,17 @@ pref("dom.serviceWorkers.disable_open_cl
 pref("dom.storage.enabled", true);
 // Whether or not LSNG (Next Generation Local Storage) is enabled.
 #ifdef NIGHTLY_BUILD
 pref("dom.storage.next_gen", true);
 #else
 pref("dom.storage.next_gen", false);
 #endif
 pref("dom.storage.default_quota",      5120);
+pref("dom.storage.shadow_writes", true);
 pref("dom.storage.snapshot_prefill", 16384);
 pref("dom.storage.snapshot_reusing", true);
 pref("dom.storage.testing", false);
 
 pref("dom.send_after_paint_to_content", false);
 
 // Timeout clamp in ms for timeouts we clamp
 pref("dom.min_timeout_value", 4);