Bug 1173439 P5 Cache should index on a hash instead of the url itself. r=ehsan

This commit is contained in:
Ben Kelly 2015-06-16 17:39:05 -07:00
parent 7674db423c
commit 96563d1764

120
dom/cache/DBSchema.cpp vendored
View File

@ -170,6 +170,7 @@ static nsresult DeleteEntries(mozIStorageConnection* aConn,
nsTArray<IdCount>& aDeletedSecurityIdListOut,
uint32_t aPos=0, int32_t aLen=-1);
static nsresult InsertSecurityInfo(mozIStorageConnection* aConn,
nsICryptoHash* aCrypto,
const nsACString& aData, int32_t *aIdOut);
static nsresult DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId,
int32_t aCount);
@ -199,6 +200,8 @@ static nsresult CreateAndBindKeyStatement(mozIStorageConnection* aConn,
const char* aQueryFormat,
const nsAString& aKey,
mozIStorageStatement** aStateOut);
static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn,
nsACString& aOut);
} // anonymous namespace
nsresult
@ -262,7 +265,9 @@ CreateSchema(mozIStorageConnection* aConn)
"id INTEGER NOT NULL PRIMARY KEY, "
"request_method TEXT NOT NULL, "
"request_url_no_query TEXT NOT NULL, "
"request_url_no_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash
"request_url_query TEXT NOT NULL, "
"request_url_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash
"request_referrer TEXT NOT NULL, "
"request_headers_guard INTEGER NOT NULL, "
"request_mode INTEGER NOT NULL, "
@ -286,10 +291,18 @@ CreateSchema(mozIStorageConnection* aConn)
));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// TODO: see if we can remove these indices on TEXT columns (bug 1110458)
// Create an index to support the QueryCache() matching algorithm. This
// needs to quickly find entries in a given Cache that match the request
// URL. The url query is separated in order to support the ignoreSearch
// option. Finally, we index hashes of the URL values instead of the
// actual strings to avoid excessive disk bloat. The index will duplicate
// the contents of the columsn in the index. The hash index will prune
// the vast majority of values from the query result so that normal
// scanning only has to be done on a few values to find an exact URL match.
rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE INDEX entries_request_url_no_query_index "
"ON entries (request_url_no_query);"
"CREATE INDEX entries_request_match_index "
"ON entries (cache_id, request_url_no_query_hash, "
"request_url_query_hash);"
));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@ -930,9 +943,15 @@ QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
"LEFT OUTER JOIN response_headers ON entries.id=response_headers.entry_id "
"AND response_headers.name='vary' "
"WHERE entries.cache_id=:cache_id "
"AND entries.request_url_no_query=:url_no_query "
"AND entries.request_url_no_query_hash=:url_no_query_hash "
);
if (!aParams.ignoreSearch()) {
query.AppendLiteral("AND entries.request_url_query_hash=:url_query_hash ");
}
query.AppendLiteral("AND entries.request_url_no_query=:url_no_query ");
if (!aParams.ignoreSearch()) {
query.AppendLiteral("AND entries.request_url_query=:url_query ");
}
@ -946,6 +965,28 @@ QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsICryptoHash> crypto =
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString urlWithoutQueryHash;
rv = HashCString(crypto, aRequest.urlWithoutQuery(), urlWithoutQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("url_no_query_hash"),
urlWithoutQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (!aParams.ignoreSearch()) {
nsAutoCString urlQueryHash;
rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("url_query_hash"),
urlQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url_no_query"),
aRequest.urlWithoutQuery());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@ -1216,10 +1257,11 @@ DeleteEntries(mozIStorageConnection* aConn,
}
nsresult
InsertSecurityInfo(mozIStorageConnection* aConn, const nsACString& aData,
int32_t *aIdOut)
InsertSecurityInfo(mozIStorageConnection* aConn, nsICryptoHash* aCrypto,
const nsACString& aData, int32_t *aIdOut)
{
MOZ_ASSERT(aConn);
MOZ_ASSERT(aCrypto);
MOZ_ASSERT(aIdOut);
MOZ_ASSERT(!aData.IsEmpty());
@ -1227,24 +1269,10 @@ InsertSecurityInfo(mozIStorageConnection* aConn, const nsACString& aData,
// the full blob would be quite expensive. Instead, we index a small
// hash value. Calculate this hash as the first 8 bytes of the SHA1 of
// the full data.
nsresult rv;
nsCOMPtr<nsICryptoHash> crypto =
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
nsAutoCString hash;
nsresult rv = HashCString(aCrypto, aData, hash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = crypto->Init(nsICryptoHash::SHA1);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = crypto->Update(reinterpret_cast<const uint8_t*>(aData.BeginReading()),
aData.Length());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString fullHash;
rv = crypto->Finish(false /* based64 result */, fullHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsDependentCSubstring hash(fullHash, 0, 8);
// Next, search for an existing entry for this blob by comparing the hash
// value first and then the full data. SQLite is smart enough to use
// the index on the hash to search the table before doing the expensive
@ -1414,10 +1442,14 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
MOZ_ASSERT(aConn);
nsresult rv = NS_OK;
int32_t securityId = -1;
nsCOMPtr<nsICryptoHash> crypto =
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
int32_t securityId = -1;
if (!aResponse.channelInfo().securityInfo().IsEmpty()) {
rv = InsertSecurityInfo(aConn,
rv = InsertSecurityInfo(aConn, crypto,
aResponse.channelInfo().securityInfo(),
&securityId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@ -1428,7 +1460,9 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
"INSERT INTO entries ("
"request_method, "
"request_url_no_query, "
"request_url_no_query_hash, "
"request_url_query, "
"request_url_query_hash, "
"request_referrer, "
"request_headers_guard, "
"request_mode, "
@ -1449,7 +1483,9 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
") VALUES ("
":request_method, "
":request_url_no_query, "
":request_url_no_query_hash, "
":request_url_query, "
":request_url_query_hash, "
":request_referrer, "
":request_headers_guard, "
":request_mode, "
@ -1479,10 +1515,26 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
aRequest.urlWithoutQuery());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString urlWithoutQueryHash;
rv = HashCString(crypto, aRequest.urlWithoutQuery(), urlWithoutQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindUTF8StringAsBlobByName(
NS_LITERAL_CSTRING("request_url_no_query_hash"), urlWithoutQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_query"),
aRequest.urlQuery());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString urlQueryHash;
rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindUTF8StringAsBlobByName(
NS_LITERAL_CSTRING("request_url_query_hash"), urlQueryHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindStringByName(NS_LITERAL_CSTRING("request_referrer"),
aRequest.referrer());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@ -1945,6 +1997,26 @@ CreateAndBindKeyStatement(mozIStorageConnection* aConn,
return rv;
}
nsresult
HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut)
{
MOZ_ASSERT(aCrypto);
nsresult rv = aCrypto->Init(nsICryptoHash::SHA1);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = aCrypto->Update(reinterpret_cast<const uint8_t*>(aIn.BeginReading()),
aIn.Length());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString fullHash;
rv = aCrypto->Finish(false /* based64 result */, fullHash);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
aOut = Substring(fullHash, 0, 8);
return rv;
}
} // anonymouns namespace
nsresult