LCOV - code coverage report
Current view: top level - storage - VacuumManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 158 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 24 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/DebugOnly.h"
       8             : 
       9             : #include "VacuumManager.h"
      10             : 
      11             : #include "mozilla/Services.h"
      12             : #include "mozilla/Preferences.h"
      13             : #include "nsIObserverService.h"
      14             : #include "nsIFile.h"
      15             : #include "nsThreadUtils.h"
      16             : #include "mozilla/Logging.h"
      17             : #include "prtime.h"
      18             : 
      19             : #include "mozStorageConnection.h"
      20             : #include "mozIStorageStatement.h"
      21             : #include "mozIStorageAsyncStatement.h"
      22             : #include "mozIStoragePendingStatement.h"
      23             : #include "mozIStorageError.h"
      24             : #include "mozStorageHelper.h"
      25             : #include "nsXULAppAPI.h"
      26             : 
      27             : #define OBSERVER_TOPIC_IDLE_DAILY "idle-daily"
      28             : #define OBSERVER_TOPIC_XPCOM_SHUTDOWN "xpcom-shutdown"
      29             : 
      30             : // Used to notify begin and end of a heavy IO task.
      31             : #define OBSERVER_TOPIC_HEAVY_IO "heavy-io-task"
      32             : #define OBSERVER_DATA_VACUUM_BEGIN u"vacuum-begin"
      33             : #define OBSERVER_DATA_VACUUM_END u"vacuum-end"
      34             : 
      35             : // This preferences root will contain last vacuum timestamps (in seconds) for
      36             : // each database.  The database filename is used as a key.
      37             : #define PREF_VACUUM_BRANCH "storage.vacuum.last."
      38             : 
      39             : // Time between subsequent vacuum calls for a certain database.
      40             : #define VACUUM_INTERVAL_SECONDS 30 * 86400 // 30 days.
      41             : 
      42             : extern mozilla::LazyLogModule gStorageLog;
      43             : 
      44             : namespace mozilla {
      45             : namespace storage {
      46             : 
      47             : namespace {
      48             : 
      49             : ////////////////////////////////////////////////////////////////////////////////
      50             : //// BaseCallback
      51             : 
      52             : class BaseCallback : public mozIStorageStatementCallback
      53             : {
      54             : public:
      55             :   NS_DECL_ISUPPORTS
      56             :   NS_DECL_MOZISTORAGESTATEMENTCALLBACK
      57           0 :   BaseCallback() {}
      58             : protected:
      59           0 :   virtual ~BaseCallback() {}
      60             : };
      61             : 
      62             : NS_IMETHODIMP
      63           0 : BaseCallback::HandleError(mozIStorageError *aError)
      64             : {
      65             : #ifdef DEBUG
      66             :   int32_t result;
      67           0 :   nsresult rv = aError->GetResult(&result);
      68           0 :   NS_ENSURE_SUCCESS(rv, rv);
      69           0 :   nsAutoCString message;
      70           0 :   rv = aError->GetMessage(message);
      71           0 :   NS_ENSURE_SUCCESS(rv, rv);
      72             : 
      73           0 :   nsAutoCString warnMsg;
      74           0 :   warnMsg.AppendLiteral("An error occured during async execution: ");
      75           0 :   warnMsg.AppendInt(result);
      76           0 :   warnMsg.Append(' ');
      77           0 :   warnMsg.Append(message);
      78           0 :   NS_WARNING(warnMsg.get());
      79             : #endif
      80           0 :   return NS_OK;
      81             : }
      82             : 
      83             : NS_IMETHODIMP
      84           0 : BaseCallback::HandleResult(mozIStorageResultSet *aResultSet)
      85             : {
      86             :   // We could get results from PRAGMA statements, but we don't mind them.
      87           0 :   return NS_OK;
      88             : }
      89             : 
      90             : NS_IMETHODIMP
      91           0 : BaseCallback::HandleCompletion(uint16_t aReason)
      92             : {
      93             :   // By default BaseCallback will just be silent on completion.
      94           0 :   return NS_OK;
      95             : }
      96             : 
      97           0 : NS_IMPL_ISUPPORTS(
      98             :   BaseCallback
      99             : , mozIStorageStatementCallback
     100             : )
     101             : 
     102             : ////////////////////////////////////////////////////////////////////////////////
     103             : //// Vacuumer declaration.
     104             : 
     105           0 : class Vacuumer : public BaseCallback
     106             : {
     107             : public:
     108             :   NS_DECL_MOZISTORAGESTATEMENTCALLBACK
     109             : 
     110             :   explicit Vacuumer(mozIStorageVacuumParticipant *aParticipant);
     111             : 
     112             :   bool execute();
     113             :   nsresult notifyCompletion(bool aSucceeded);
     114             : 
     115             : private:
     116             :   nsCOMPtr<mozIStorageVacuumParticipant> mParticipant;
     117             :   nsCString mDBFilename;
     118             :   nsCOMPtr<mozIStorageConnection> mDBConn;
     119             : };
     120             : 
     121             : ////////////////////////////////////////////////////////////////////////////////
     122             : //// Vacuumer implementation.
     123             : 
     124           0 : Vacuumer::Vacuumer(mozIStorageVacuumParticipant *aParticipant)
     125           0 :   : mParticipant(aParticipant)
     126             : {
     127           0 : }
     128             : 
     129             : bool
     130           0 : Vacuumer::execute()
     131             : {
     132           0 :   MOZ_ASSERT(NS_IsMainThread(), "Must be running on the main thread!");
     133             : 
     134             :   // Get the connection and check its validity.
     135           0 :   nsresult rv = mParticipant->GetDatabaseConnection(getter_AddRefs(mDBConn));
     136           0 :   NS_ENSURE_SUCCESS(rv, false);
     137           0 :   bool ready = false;
     138           0 :   if (!mDBConn || NS_FAILED(mDBConn->GetConnectionReady(&ready)) || !ready) {
     139           0 :     NS_WARNING("Unable to get a connection to vacuum database");
     140           0 :     return false;
     141             :   }
     142             : 
     143             :   // Ask for the expected page size.  Vacuum can change the page size, unless
     144             :   // the database is using WAL journaling.
     145             :   // TODO Bug 634374: figure out a strategy to fix page size with WAL.
     146           0 :   int32_t expectedPageSize = 0;
     147           0 :   rv = mParticipant->GetExpectedDatabasePageSize(&expectedPageSize);
     148           0 :   if (NS_FAILED(rv) || !Service::pageSizeIsValid(expectedPageSize)) {
     149           0 :     NS_WARNING("Invalid page size requested for database, will use default ");
     150           0 :     NS_WARNING(mDBFilename.get());
     151           0 :     expectedPageSize = Service::getDefaultPageSize();
     152             :   }
     153             : 
     154             :   // Get the database filename.  Last vacuum time is stored under this name
     155             :   // in PREF_VACUUM_BRANCH.
     156           0 :   nsCOMPtr<nsIFile> databaseFile;
     157           0 :   mDBConn->GetDatabaseFile(getter_AddRefs(databaseFile));
     158           0 :   if (!databaseFile) {
     159           0 :     NS_WARNING("Trying to vacuum a in-memory database!");
     160           0 :     return false;
     161             :   }
     162           0 :   nsAutoString databaseFilename;
     163           0 :   rv = databaseFile->GetLeafName(databaseFilename);
     164           0 :   NS_ENSURE_SUCCESS(rv, false);
     165           0 :   mDBFilename = NS_ConvertUTF16toUTF8(databaseFilename);
     166           0 :   MOZ_ASSERT(!mDBFilename.IsEmpty(), "Database filename cannot be empty");
     167             : 
     168             :   // Check interval from last vacuum.
     169           0 :   int32_t now = static_cast<int32_t>(PR_Now() / PR_USEC_PER_SEC);
     170             :   int32_t lastVacuum;
     171           0 :   nsAutoCString prefName(PREF_VACUUM_BRANCH);
     172           0 :   prefName += mDBFilename;
     173           0 :   rv = Preferences::GetInt(prefName.get(), &lastVacuum);
     174           0 :   if (NS_SUCCEEDED(rv) && (now - lastVacuum) < VACUUM_INTERVAL_SECONDS) {
     175             :     // This database was vacuumed recently, skip it.
     176           0 :     return false;
     177             :   }
     178             : 
     179             :   // Notify that we are about to start vacuuming.  The participant can opt-out
     180             :   // if it cannot handle a vacuum at this time, and then we'll move to the next
     181             :   // one.
     182           0 :   bool vacuumGranted = false;
     183           0 :   rv = mParticipant->OnBeginVacuum(&vacuumGranted);
     184           0 :   NS_ENSURE_SUCCESS(rv, false);
     185           0 :   if (!vacuumGranted) {
     186           0 :     return false;
     187             :   }
     188             : 
     189             :   // Notify a heavy IO task is about to start.
     190           0 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     191           0 :   if (os) {
     192             :     rv =
     193           0 :       os->NotifyObservers(nullptr, OBSERVER_TOPIC_HEAVY_IO,
     194           0 :                           OBSERVER_DATA_VACUUM_BEGIN);
     195           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to notify");
     196             :   }
     197             : 
     198             :   // Execute the statements separately, since the pragma may conflict with the
     199             :   // vacuum, if they are executed in the same transaction.
     200           0 :   nsCOMPtr<mozIStorageAsyncStatement> pageSizeStmt;
     201             :   nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
     202           0 :                               "PRAGMA page_size = ");
     203           0 :   pageSizeQuery.AppendInt(expectedPageSize);
     204           0 :   rv = mDBConn->CreateAsyncStatement(pageSizeQuery,
     205           0 :                                      getter_AddRefs(pageSizeStmt));
     206           0 :   NS_ENSURE_SUCCESS(rv, false);
     207           0 :   RefPtr<BaseCallback> callback = new BaseCallback();
     208           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
     209           0 :   rv = pageSizeStmt->ExecuteAsync(callback, getter_AddRefs(ps));
     210           0 :   NS_ENSURE_SUCCESS(rv, false);
     211             : 
     212           0 :   nsCOMPtr<mozIStorageAsyncStatement> stmt;
     213           0 :   rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     214             :     "VACUUM"
     215           0 :   ), getter_AddRefs(stmt));
     216           0 :   NS_ENSURE_SUCCESS(rv, false);
     217           0 :   rv = stmt->ExecuteAsync(this, getter_AddRefs(ps));
     218           0 :   NS_ENSURE_SUCCESS(rv, false);
     219             : 
     220           0 :   return true;
     221             : }
     222             : 
     223             : ////////////////////////////////////////////////////////////////////////////////
     224             : //// mozIStorageStatementCallback
     225             : 
     226             : NS_IMETHODIMP
     227           0 : Vacuumer::HandleError(mozIStorageError *aError)
     228             : {
     229             :   int32_t result;
     230             :   nsresult rv;
     231           0 :   nsAutoCString message;
     232             : 
     233             : #ifdef DEBUG
     234           0 :   rv = aError->GetResult(&result);
     235           0 :   NS_ENSURE_SUCCESS(rv, rv);
     236           0 :   rv = aError->GetMessage(message);
     237           0 :   NS_ENSURE_SUCCESS(rv, rv);
     238             : 
     239           0 :   nsAutoCString warnMsg;
     240           0 :   warnMsg.AppendLiteral("Unable to vacuum database: ");
     241           0 :   warnMsg.Append(mDBFilename);
     242           0 :   warnMsg.AppendLiteral(" - ");
     243           0 :   warnMsg.AppendInt(result);
     244           0 :   warnMsg.Append(' ');
     245           0 :   warnMsg.Append(message);
     246           0 :   NS_WARNING(warnMsg.get());
     247             : #endif
     248             : 
     249           0 :   if (MOZ_LOG_TEST(gStorageLog, LogLevel::Error)) {
     250           0 :     rv = aError->GetResult(&result);
     251           0 :     NS_ENSURE_SUCCESS(rv, rv);
     252           0 :     rv = aError->GetMessage(message);
     253           0 :     NS_ENSURE_SUCCESS(rv, rv);
     254           0 :     MOZ_LOG(gStorageLog, LogLevel::Error,
     255             :            ("Vacuum failed with error: %d '%s'. Database was: '%s'",
     256             :             result, message.get(), mDBFilename.get()));
     257             :   }
     258           0 :   return NS_OK;
     259             : }
     260             : 
     261             : NS_IMETHODIMP
     262           0 : Vacuumer::HandleResult(mozIStorageResultSet *aResultSet)
     263             : {
     264           0 :   NS_NOTREACHED("Got a resultset from a vacuum?");
     265           0 :   return NS_OK;
     266             : }
     267             : 
     268             : NS_IMETHODIMP
     269           0 : Vacuumer::HandleCompletion(uint16_t aReason)
     270             : {
     271           0 :   if (aReason == REASON_FINISHED) {
     272             :     // Update last vacuum time.
     273           0 :     int32_t now = static_cast<int32_t>(PR_Now() / PR_USEC_PER_SEC);
     274           0 :     MOZ_ASSERT(!mDBFilename.IsEmpty(), "Database filename cannot be empty");
     275           0 :     nsAutoCString prefName(PREF_VACUUM_BRANCH);
     276           0 :     prefName += mDBFilename;
     277           0 :     DebugOnly<nsresult> rv = Preferences::SetInt(prefName.get(), now);
     278           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to set a preference");
     279             :   }
     280             : 
     281           0 :   notifyCompletion(aReason == REASON_FINISHED);
     282             : 
     283           0 :   return NS_OK;
     284             : }
     285             : 
     286             : nsresult
     287           0 : Vacuumer::notifyCompletion(bool aSucceeded)
     288             : {
     289           0 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     290           0 :   if (os) {
     291           0 :     os->NotifyObservers(nullptr, OBSERVER_TOPIC_HEAVY_IO,
     292           0 :                         OBSERVER_DATA_VACUUM_END);
     293             :   }
     294             : 
     295           0 :   nsresult rv = mParticipant->OnEndVacuum(aSucceeded);
     296           0 :   NS_ENSURE_SUCCESS(rv, rv);
     297             : 
     298           0 :   return NS_OK;
     299             : }
     300             : 
     301             : } // namespace
     302             : 
     303             : ////////////////////////////////////////////////////////////////////////////////
     304             : //// VacuumManager
     305             : 
     306           0 : NS_IMPL_ISUPPORTS(
     307             :   VacuumManager
     308             : , nsIObserver
     309             : )
     310             : 
     311             : VacuumManager *
     312             : VacuumManager::gVacuumManager = nullptr;
     313             : 
     314             : VacuumManager *
     315           0 : VacuumManager::getSingleton()
     316             : {
     317             :   //Don't allocate it in the child Process.
     318           0 :   if (!XRE_IsParentProcess()) {
     319           0 :     return nullptr;
     320             :   }
     321             : 
     322           0 :   if (gVacuumManager) {
     323           0 :     NS_ADDREF(gVacuumManager);
     324           0 :     return gVacuumManager;
     325             :   }
     326           0 :   gVacuumManager = new VacuumManager();
     327           0 :   if (gVacuumManager) {
     328           0 :     NS_ADDREF(gVacuumManager);
     329             :   }
     330           0 :   return gVacuumManager;
     331             : }
     332             : 
     333           0 : VacuumManager::VacuumManager()
     334           0 :   : mParticipants("vacuum-participant")
     335             : {
     336           0 :   MOZ_ASSERT(!gVacuumManager,
     337             :              "Attempting to create two instances of the service!");
     338           0 :   gVacuumManager = this;
     339           0 : }
     340             : 
     341           0 : VacuumManager::~VacuumManager()
     342             : {
     343             :   // Remove the static reference to the service.  Check to make sure its us
     344             :   // in case somebody creates an extra instance of the service.
     345           0 :   MOZ_ASSERT(gVacuumManager == this,
     346             :              "Deleting a non-singleton instance of the service");
     347           0 :   if (gVacuumManager == this) {
     348           0 :     gVacuumManager = nullptr;
     349             :   }
     350           0 : }
     351             : 
     352             : ////////////////////////////////////////////////////////////////////////////////
     353             : //// nsIObserver
     354             : 
     355             : NS_IMETHODIMP
     356           0 : VacuumManager::Observe(nsISupports *aSubject,
     357             :                        const char *aTopic,
     358             :                        const char16_t *aData)
     359             : {
     360           0 :   if (strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY) == 0) {
     361             :     // Try to run vacuum on all registered entries.  Will stop at the first
     362             :     // successful one.
     363           0 :     nsCOMArray<mozIStorageVacuumParticipant> entries;
     364           0 :     mParticipants.GetEntries(entries);
     365             :     // If there are more entries than what a month can contain, we could end up
     366             :     // skipping some, since we run daily.  So we use a starting index.
     367             :     static const char* kPrefName = PREF_VACUUM_BRANCH "index";
     368           0 :     int32_t startIndex = Preferences::GetInt(kPrefName, 0);
     369           0 :     if (startIndex >= entries.Count()) {
     370           0 :       startIndex = 0;
     371             :     }
     372             :     int32_t index;
     373           0 :     for (index = startIndex; index < entries.Count(); ++index) {
     374           0 :       RefPtr<Vacuumer> vacuum = new Vacuumer(entries[index]);
     375             :       // Only vacuum one database per day.
     376           0 :       if (vacuum->execute()) {
     377           0 :         break;
     378             :       }
     379             :     }
     380           0 :     DebugOnly<nsresult> rv = Preferences::SetInt(kPrefName, index);
     381           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to set a preference");
     382             :   }
     383             : 
     384           0 :   return NS_OK;
     385             : }
     386             : 
     387             : } // namespace storage
     388             : } // namespace mozilla

Generated by: LCOV version 1.13