Bug 572223 - too much cookies.sqlite io. Part 6: fresh connection for sync reads. r=sdwilsh
authorDan Witte <dwitte@mozilla.com>
Thu, 29 Jul 2010 13:43:34 -0700
changeset 48375 6d0d4c0c889042b6618dab048349e51099f2c5e7
parent 48374 e0d1de68b67684c1573ca42528621c36b06be68e
child 48376 3d786e54c7db4ed553a8232b99ab7ba682d233d2
push idunknown
push userunknown
push dateunknown
reviewerssdwilsh
bugs572223
milestone2.0b3pre
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 572223 - too much cookies.sqlite io. Part 6: fresh connection for sync reads. r=sdwilsh
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -638,16 +638,19 @@ nsCookieService::Init()
     prefBranch->AddObserver(kPrefCookieBehavior,     this, PR_TRUE);
     prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, PR_TRUE);
     prefBranch->AddObserver(kPrefMaxCookiesPerHost,  this, PR_TRUE);
     prefBranch->AddObserver(kPrefCookiePurgeAge,     this, PR_TRUE);
     prefBranch->AddObserver(kPrefThirdPartySession,  this, PR_TRUE);
     PrefChanged(prefBranch);
   }
 
+  mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // failure here is non-fatal (we can run fine without
   // persistent storage - e.g. if there's no profile)
   rv = InitDB();
   if (NS_FAILED(rv))
     COOKIE_LOGSTRING(PR_LOG_WARNING, ("Init(): InitDB() gave error %x", rv));
 
   mObserverService = mozilla::services::GetObserverService();
   if (mObserverService) {
@@ -716,24 +719,20 @@ nsCookieService::TryInitDB(PRBool aDelet
   cookieFile->AppendNative(NS_LITERAL_CSTRING(kCookieFileName));
 
   // remove an existing db, if we've been told to (i.e. it's corrupt)
   if (aDeleteExistingDB) {
     rv = cookieFile->Remove(PR_FALSE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<mozIStorageService> storage = do_GetService("@mozilla.org/storage/service;1");
-  if (!storage)
-    return NS_ERROR_UNEXPECTED;
-
   // open a connection to the cookie database, and only cache our connection
   // and statements upon success. The connection is opened shared such that
   // the main and background threads can operate on the db concurrently.
-  rv = storage->OpenDatabase(cookieFile, getter_AddRefs(mDBState->dbConn));
+  rv = mStorageService->OpenDatabase(cookieFile, getter_AddRefs(mDBState->dbConn));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool tableExists = PR_FALSE;
   mDBState->dbConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"), &tableExists);
   if (!tableExists) {
       rv = CreateTable();
       NS_ENSURE_SUCCESS(rv, rv);
 
@@ -966,18 +965,20 @@ nsCookieService::CloseDB()
   // finalize our statements and close the db connection for the default state.
   // since we own these objects, nulling the pointers is sufficient here.
   mDefaultDBState.stmtInsert = nsnull;
   mDefaultDBState.stmtDelete = nsnull;
   mDefaultDBState.stmtUpdate = nsnull;
   if (mDefaultDBState.dbConn) {
     // Cancel any pending read. No further results will be received by our
     // read listener.
-    if (mDefaultDBState.pendingRead)
+    if (mDefaultDBState.pendingRead) {
       CancelAsyncRead(PR_TRUE);
+      mDefaultDBState.syncConn = nsnull;
+    }
 
     mDefaultDBState.dbConn->AsyncClose(mCloseListener);
     mDefaultDBState.dbConn = nsnull;
   }
 }
 
 nsCookieService::~nsCookieService()
 {
@@ -1483,16 +1484,17 @@ nsCookieService::AsyncReadComplete()
       continue;
 
     AddCookieToList(tuple.baseDomain, tuple.cookie, NULL, PR_FALSE);
   }
 
   mDefaultDBState.stmtReadDomain = nsnull;
   mDefaultDBState.pendingRead = nsnull;
   mDefaultDBState.readListener = nsnull;
+  mDefaultDBState.syncConn = nsnull;
   mDefaultDBState.hostArray.Clear();
   mDefaultDBState.readSet.Clear();
 
   mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
 
   COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read",
                                   mDefaultDBState.cookieCount));
 }
@@ -1516,16 +1518,33 @@ nsCookieService::CancelAsyncRead(PRBool 
   mDefaultDBState.hostArray.Clear();
 
   // Only clear the 'readSet' table if we no longer need to know what set of
   // data is already accounted for.
   if (aPurgeReadSet)
     mDefaultDBState.readSet.Clear();
 }
 
+mozIStorageConnection*
+nsCookieService::GetSyncDBConn()
+{
+  NS_ASSERTION(!mDefaultDBState.syncConn, "already have sync db connection");
+
+  // Start a new connection for sync reads to reduce contention with the
+  // background thread.
+  nsCOMPtr<nsIFile> cookieFile;
+  mDefaultDBState.dbConn->GetDatabaseFile(getter_AddRefs(cookieFile));
+  NS_ASSERTION(cookieFile, "no cookie file on connection");
+
+  mStorageService->OpenDatabase(cookieFile,
+    getter_AddRefs(mDefaultDBState.syncConn));
+  NS_ASSERTION(mDefaultDBState.syncConn, "can't open sync db connection");
+  return mDefaultDBState.syncConn;
+}
+
 void
 nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
 {
   NS_ASSERTION(!mDBState->dbConn || mDBState == &mDefaultDBState,
     "not in default db state");
 
   // Fast path 1: nothing to read, or we've already finished reading.
   if (NS_LIKELY(!mDBState->dbConn || !mDefaultDBState.pendingRead))
@@ -1533,18 +1552,21 @@ nsCookieService::EnsureReadDomain(const 
 
   // Fast path 2: already read in this particular domain.
   if (NS_LIKELY(mDefaultDBState.readSet.GetEntry(aBaseDomain)))
     return;
 
   // Read in the data synchronously.
   nsresult rv;
   if (!mDefaultDBState.stmtReadDomain) {
+    if (!GetSyncDBConn())
+      return;
+
     // Cache the statement, since it's likely to be used again.
-    rv = mDefaultDBState.dbConn->CreateStatement(NS_LITERAL_CSTRING(
+    rv = mDefaultDBState.syncConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT "
         "id, "
         "name, "
         "value, "
         "host, "
         "path, "
         "expiry, "
         "lastAccessed, "
@@ -1553,16 +1575,18 @@ nsCookieService::EnsureReadDomain(const 
       "FROM moz_cookies "
       "WHERE baseDomain = ?1"),
       getter_AddRefs(mDefaultDBState.stmtReadDomain));
 
     // XXX Ignore corruption for now. See bug 547031.
     if (NS_FAILED(rv)) return;
   }
 
+  NS_ASSERTION(mDefaultDBState.syncConn, "should have a sync db connection");
+
   mozStorageStatementScoper scoper(mDefaultDBState.stmtReadDomain);
 
   rv = mDefaultDBState.stmtReadDomain->BindUTF8StringByIndex(0, aBaseDomain);
   NS_ASSERT_SUCCESS(rv);
 
   PRBool hasResult;
   PRUint32 readCount = 0;
   nsCString name, value, host, path;
@@ -1595,19 +1619,22 @@ nsCookieService::EnsureReadComplete()
 
   // Fast path 1: nothing to read, or we've already finished reading.
   if (NS_LIKELY(!mDBState->dbConn || !mDefaultDBState.pendingRead))
     return;
 
   // Cancel the pending read, so we don't get any more results.
   CancelAsyncRead(PR_FALSE);
 
+  if (!mDefaultDBState.syncConn && !GetSyncDBConn())
+    return;
+
   // Read in the data synchronously.
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDefaultDBState.dbConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = mDefaultDBState.syncConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "id, "
       "name, "
       "value, "
       "host, "
       "path, "
       "expiry, "
       "lastAccessed, "
@@ -1635,16 +1662,17 @@ nsCookieService::EnsureReadComplete()
     if (mDefaultDBState.readSet.GetEntry(baseDomain))
       continue;
 
     nsCookie* newCookie = GetCookieFromRow(stmt);
     AddCookieToList(baseDomain, newCookie, NULL, PR_FALSE);
     ++readCount;
   }
 
+  mDefaultDBState.syncConn = nsnull;
   mDefaultDBState.readSet.Clear();
 
   mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
 
   COOKIE_LOGSTRING(PR_LOG_DEBUG,
     ("EnsureReadComplete(): %ld cookies read", readCount));
 }
 
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -60,16 +60,17 @@
 
 class nsICookiePermission;
 class nsIEffectiveTLDService;
 class nsIIDNService;
 class nsIPrefBranch;
 class nsIObserverService;
 class nsIURI;
 class nsIChannel;
+class mozIStorageService;
 class mozIStorageStatementCallback;
 class mozIStorageCompletionCallback;
 class ReadCookieDBListener;
 
 struct nsCookieAttributes;
 struct nsListIter;
 struct nsEnumerationData;
 
@@ -155,16 +156,17 @@ struct DBState
   PRInt64                         cookieOldestTime;
   nsCOMPtr<mozIStorageConnection> dbConn;
   nsCOMPtr<mozIStorageStatement>  stmtInsert;
   nsCOMPtr<mozIStorageStatement>  stmtDelete;
   nsCOMPtr<mozIStorageStatement>  stmtUpdate;
 
   // Various parts representing asynchronous read state. These are useful
   // while the background read is taking place.
+  nsCOMPtr<mozIStorageConnection>       syncConn;
   nsCOMPtr<mozIStorageStatement>        stmtReadDomain;
   nsCOMPtr<mozIStoragePendingStatement> pendingRead;
   // The asynchronous read listener. This is a weak ref (storage has ownership)
   // since it may need to outlive the DBState's database connection.
   ReadCookieDBListener*                 readListener;
   // An array of (baseDomain, cookie) tuples representing data read in
   // asynchronously. This is merged into hostTable once read is complete.
   nsTArray<CookieDomainTuple>           hostArray;
@@ -215,16 +217,17 @@ class nsCookieService : public nsICookie
     nsresult                      InitDB();
     nsresult                      TryInitDB(PRBool aDeleteExistingDB);
     nsresult                      CreateTable();
     void                          CloseDB();
     nsresult                      Read();
     template<class T> nsCookie*   GetCookieFromRow(T &aRow);
     void                          AsyncReadComplete();
     void                          CancelAsyncRead(PRBool aPurgeReadSet);
+    mozIStorageConnection*        GetSyncDBConn();
     void                          EnsureReadDomain(const nsCString &aBaseDomain);
     void                          EnsureReadComplete();
     nsresult                      NormalizeHost(nsCString &aHost);
     nsresult                      GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aRequireHostMatch);
     nsresult                      GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
     void                          GetCookieInternal(nsIURI *aHostURI, nsIURI *aOriginatingURI, PRBool aHttpBound, nsCString &aCookie);
     void                          SetCookieStringInternal(nsIURI *aHostURI, nsIURI *aOriginatingURI, const nsCString &aCookieHeader, const nsCString &aServerTime, PRBool aFromHttp);
     PRBool                        SetCookieInternal(nsIURI *aHostURI, const nsCString& aBaseDomain, PRBool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
@@ -248,16 +251,17 @@ class nsCookieService : public nsICookie
     void                          NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);
 
   protected:
     // cached members.
     nsCOMPtr<nsIObserverService>     mObserverService;
     nsCOMPtr<nsICookiePermission>    mPermissionService;
     nsCOMPtr<nsIEffectiveTLDService> mTLDService;
     nsCOMPtr<nsIIDNService>          mIDNService;
+    nsCOMPtr<mozIStorageService>     mStorageService;
 
     // we have two separate DB states: one for normal browsing and one for
     // private browsing, switching between them as appropriate. this state
     // encapsulates both the in-memory table and the on-disk DB.
     // note that the private states' dbConn should always be null - we never
     // want to be dealing with the on-disk DB when in private browsing.
     DBState                      *mDBState;
     DBState                       mDefaultDBState;