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
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;