commit 4d851b34a0aa8b743a08cb64cc00efcfbf2cb9e5
parent 3102e96a538277a8797d7029c46258738ddfecaa
Author: Jan Varga <Jan.Varga@gmail.com>
Date: Fri, 28 Nov 2025 11:35:14 +0000
Bug 1988590 - QM: Clear non-persisted zero-usage origins; r=dom-storage-reviewers,jari
This patch introduces the actual clearing of non-persisted origins that have
zero quota-charged usage during final steps of temporary storage initialization.
Key aspects:
- Honors the new pref
dom.quotaManager.temporaryStorage.clearNonPersistedZeroUsageOrigins.
- Excludes recently used origins.
- Uses debug-only assertions to catch accidental clearing of unrelated origins.
- Supports batching to avoid large I/O spikes when many origins need removal.
Telemetry shows a large number of zero-sized origin directories in some cases,
especially on Desktop. This cleanup should help reduce storage bloat and
improve initialization performance, particularly for long-lived profiles with
thousands of unused origins.
Testing: The clearing is exercised in test_temporaryStorageCleanup.js added in
a follow-up patch for this bug.
Differential Revision: https://phabricator.services.mozilla.com/D266867
Diffstat:
4 files changed, 56 insertions(+), 14 deletions(-)
diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp
@@ -8077,9 +8077,41 @@ void QuotaManager::ClearOrigins(
void QuotaManager::CleanupTemporaryStorage() {
AssertIsOnIOThread();
- // XXX Maybe clear non-persistent zero usage origins here. Ideally the
- // clearing would be done asynchronously by storage maintenance service once
- // available.
+ if (StaticPrefs::
+ dom_quotaManager_temporaryStorage_clearNonPersistedZeroUsageOrigins()) {
+ // XXX Ideally the clearing would be done asynchronously by storage
+ // maintenance service once available.
+
+ // We hardcode a 7-day cutoff for "recently used" origins. Even if such
+ // origins have zero usage, skipping them avoids the performance penalty
+ // of repeatedly recreating origin directories and metadata files. The
+ // value is fixed for now to keep things simple, but could be made
+ // configurable in the future if needed.
+ static_assert(aDefaultCutoffAccessTime == kSecPerWeek * PR_USEC_PER_SEC);
+
+ // Calculate cutoff time (one week ago). PR_Now() returns microseconds
+ // since epoch, so this cannot realistically overflow.
+ const int64_t cutoffTime = PR_Now() - aDefaultCutoffAccessTime;
+
+#ifdef DEBUG
+ // Verify that origins being cleared meet our criteria:
+ // non-persisted, zero usage, and outside cutoff window
+ auto checker = [&self = *this, cutoffTime](const auto& doomedOriginInfo) {
+ MutexAutoLock lock(self.mQuotaMutex);
+ MOZ_ASSERT(!doomedOriginInfo->LockedPersisted());
+ MOZ_ASSERT(doomedOriginInfo->LockedUsage() == 0);
+ MOZ_ASSERT(doomedOriginInfo->LockedAccessTime() < cutoffTime);
+ };
+#else
+ auto checker = [](const auto&) {};
+#endif
+
+ const size_t maxOriginsToClear = StaticPrefs::
+ dom_quotaManager_temporaryStorage_maxOriginsToClearDuringCleanup();
+
+ ClearOrigins(GetOriginInfosWithZeroUsage(Some(cutoffTime)),
+ std::move(checker), Some(maxOriginsToClear));
+ }
// Evicting origins that exceed their group limit also affects the global
// temporary storage usage, so these steps have to be taken sequentially.
diff --git a/dom/quota/Constants.h b/dom/quota/Constants.h
@@ -8,6 +8,7 @@
#define DOM_QUOTA_CONSTANTS_H_
#include "nsLiteralString.h"
+#include "prtime.h"
// The name of the file that we use to load/save the last access time of an
// origin.
@@ -19,6 +20,18 @@
namespace mozilla::dom::quota {
+// Seconds in one day (24 * 60 * 60). Used for time based calculations across
+// quota manager code.
+constexpr int64_t kSecPerDay = 86400;
+
+// Seconds in one week.
+constexpr int64_t kSecPerWeek = 7 * kSecPerDay;
+
+// Default cutoff access time (in microseconds) used for temporary storage
+// cleanup. Corresponds to "one week ago" and is used as the threshold when
+// clearing non persisted zero usage origins.
+constexpr int64_t aDefaultCutoffAccessTime = kSecPerWeek * PR_USEC_PER_SEC;
+
const char kChromeOrigin[] = "chrome";
constexpr auto kSQLiteSuffix = u".sqlite"_ns;
diff --git a/dom/quota/Date.h b/dom/quota/Date.h
@@ -8,16 +8,11 @@
#define DOM_QUOTA_DATE_H_
#include "mozilla/CheckedInt.h"
+#include "mozilla/dom/quota/Constants.h"
#include "prtime.h"
namespace mozilla::dom::quota {
-namespace {
-
-const int64_t kSecPerDay = 86400;
-
-}
-
/**
* A lightweight utility class representing a date as the number of days since
* the Unix epoch (1970-01-01 UTC).
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
@@ -4083,11 +4083,13 @@
# temporary storage initialization?
# When enabled, the QuotaManager will automatically clear non-persisted
# origins that have zero quota-charged usage during final steps of temporary
-# storage initialization. This removes directories and files belonging to
-# origins that do not store any real data but still consume resources through
-# bookkeeping structures on disk. The feature helps reduce storage bloat and
-# improve initialization performance, especially on long-lived profiles with a
-# large number of unused origins.
+# storage initialization. Recently used origins (within the last week) are
+# excluded, and clearing is performed in batches to avoid large I/O spikes
+# when many origins need to be removed. This removes directories and files
+# belonging to origins that do not store any real data but still consume
+# resources through bookkeeping structures on disk. The feature helps reduce
+# storage bloat and improve initialization performance, especially on
+# long-lived profiles with a large number of unused origins.
- name: dom.quotaManager.temporaryStorage.clearNonPersistedZeroUsageOrigins
type: RelaxedAtomicBool
value: false