Bug 1286798 - Part 46: Add a pref for database shadowing; r=asuth draft
authorJan Varga <jan.varga@gmail.com>
Wed, 24 Oct 2018 06:59:09 +0200
changeset 481724 93b458d08f0881bf310e7b063e6c8bdc9ccd2e2a
parent 481723 2d1ec32bf081c00c73f70dd7741338eeb2c9e5ef
child 481725 cf3015b13f989acc8dd401d057b2458fe25067f9
push id10
push userbugmail@asutherland.org
push dateSun, 18 Nov 2018 18:57:42 +0000
reviewersasuth
bugs1286798
milestone65.0a1
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
@@ -113,18 +113,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
@@ -917,34 +919,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;
@@ -972,17 +974,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)
@@ -1016,17 +1018,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()
   { }
@@ -1034,17 +1036,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;
@@ -1322,23 +1324,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
@@ -2553,16 +2553,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;
@@ -2706,16 +2707,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);
@@ -3169,43 +3180,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)"),
@@ -3224,16 +3235,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;
   }
@@ -3269,17 +3284,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;"),
@@ -3293,16 +3308,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;
@@ -3323,17 +3342,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);
@@ -3341,16 +3360,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;
   }
@@ -3759,66 +3782,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()
@@ -6959,16 +6996,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
@@ -1266,16 +1266,17 @@ pref("dom.serviceWorkers.disable_open_cl
 
 pref("dom.storage.enabled", true);
 #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);