Commit e13e3de6 authored by yawano's avatar yawano Committed by Commit bot

Make re-scanning of devices much faster by caching content hashcodes.

This CL is took over from issue 1112573005 at patchset 30001 (http://crrev.com/1112573005#ps30001)

BUG=476688
TEST=*ImportController*

Review URL: https://codereview.chromium.org/1126043005

Cr-Commit-Position: refs/heads/master@{#329377}
parent 25a9e6e7
......@@ -20,6 +20,13 @@ importer.DriveDuplicateFinder = function(tracker) {
/** @private {Promise<string>} */
this.driveIdPromise_ = null;
/**
* An bounded cache of most recently calculated file content hashcodes.
* @private {!LRUCache<!Promise<string>>}
*/
this.hashCache_ = new LRUCache(
importer.DriveDuplicateFinder.MAX_CACHED_HASHCODES_);
};
/**
......@@ -45,42 +52,57 @@ importer.DriveDuplicateFinder.HASH_EVENT_THRESHOLD_ = 5000;
/** @private @const {number} */
importer.DriveDuplicateFinder.SEARCH_EVENT_THRESHOLD_ = 1000;
/** @private @const {number} */
importer.DriveDuplicateFinder.MAX_CACHED_HASHCODES_ = 10000;
/**
* Computes the content hash for the given file entry.
* @param {!FileEntry} entry
* @return {!Promise<string>} The computed hash.
* @private
*/
importer.DriveDuplicateFinder.prototype.computeHash_ = function(entry) {
return new Promise(
/** @this {importer.DriveDuplicateFinder} */
function(resolve, reject) {
var startTime = new Date().getTime();
chrome.fileManagerPrivate.computeChecksum(
entry.toURL(),
/**
* @param {string} result The content hash.
* @this {importer.DriveDuplicateFinder}
*/
function(result) {
var elapsedTime = new Date().getTime() - startTime;
// Send the timing to GA only if it is sorta exceptionally long.
// A one second, CPU intensive operation, is pretty long.
if (elapsedTime >=
importer.DriveDuplicateFinder.HASH_EVENT_THRESHOLD_) {
console.info(
'Content hash computation took ' + elapsedTime + ' ms.');
this.tracker_.sendTiming(
metrics.Categories.ACQUISITION,
metrics.timing.Variables.COMPUTE_HASH,
elapsedTime);
}
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result);
}
}.bind(this));
}.bind(this));
return importer.createMetadataHashcode(entry).then(function(hashcode) {
// Cache key is the concatination of metadata hashcode and URL.
var cacheKey = hashcode + '|' + entry.toURL();
if (this.hashCache_.hasKey(cacheKey)) {
return this.hashCache_.get(cacheKey);
}
var hashPromise = new Promise(
/** @this {importer.DriveDuplicateFinder} */
function(resolve, reject) {
var startTime = new Date().getTime();
chrome.fileManagerPrivate.computeChecksum(
entry.toURL(),
/**
* @param {string} result The content hash.
* @this {importer.DriveDuplicateFinder}
*/
function(result) {
var elapsedTime = new Date().getTime() - startTime;
// Send the timing to GA only if it is sorta exceptionally long.
// A one second, CPU intensive operation, is pretty long.
if (elapsedTime >=
importer.DriveDuplicateFinder.HASH_EVENT_THRESHOLD_) {
console.info(
'Content hash computation took ' + elapsedTime + ' ms.');
this.tracker_.sendTiming(
metrics.Categories.ACQUISITION,
metrics.timing.Variables.COMPUTE_HASH,
elapsedTime);
}
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result);
}
}.bind(this));
}.bind(this));
this.hashCache_.put(cacheKey, hashPromise);
return hashPromise;
}.bind(this));
};
/**
......@@ -91,8 +113,9 @@ importer.DriveDuplicateFinder.prototype.computeHash_ = function(entry) {
* @private
*/
importer.DriveDuplicateFinder.prototype.findByHash_ = function(hash) {
return this.getDriveId_()
.then(this.searchFilesByHash_.bind(this, hash));
return /** @type {!Promise<Array<string>>} */ (
this.getDriveId_()
.then(this.searchFilesByHash_.bind(this, hash)));
};
/**
......@@ -119,7 +142,8 @@ importer.DriveDuplicateFinder.prototype.getDriveId_ = function() {
* A promise-based wrapper for chrome.fileManagerPrivate.searchFilesByHashes.
* @param {string} hash The content hash to search for.
* @param {string} volumeId The volume to search.
* @return <!Promise<Array<string>>> A list of file URLs.
* @return {!Promise<Array<string>>} A list of file URLs.
* @private
*/
importer.DriveDuplicateFinder.prototype.searchFilesByHash_ =
function(hash, volumeId) {
......
......@@ -13,6 +13,7 @@
<script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
<script src="../../common/js/async_util.js"></script>
<script src="../../common/js/lru_cache.js"></script>
<script src="../../common/js/mock_entry.js"></script>
<script src="../../common/js/test_tracker.js"></script>
<script src="../../common/js/unittest_util.js"></script>
......
......@@ -121,7 +121,9 @@ importer.DefaultMediaScanner.prototype.scanDirectory = function(directory) {
.then(
/** @this {importer.DefaultMediaScanner} */
function() {
console.info(scan.name + ': Finished.');
console.info(
scan.name + ': Finished directory scan. Details: ' +
JSON.stringify(scan.getStatistics()));
this.notify_(importer.ScanEvent.FINALIZED, scan);
}.bind(this));
......@@ -155,7 +157,9 @@ importer.DefaultMediaScanner.prototype.scanFiles = function(entries) {
.then(
/** @this {importer.DefaultMediaScanner} */
function() {
console.info(scan.name + ': Finished.');
console.info(
scan.name + ': Finished file-selection scan. Details: ' +
JSON.stringify(scan.getStatistics()));
this.notify_(importer.ScanEvent.FINALIZED, scan);
}.bind(this));
......
......@@ -126,6 +126,15 @@ LRUCache.prototype.peek = function(key) {
return node.value;
};
/**
* Returns true if the cache contains the key.
* @param {string} key
* @return {boolean}
*/
LRUCache.prototype.hasKey = function(key) {
return key in this.nodes_;
};
/**
* Saves an item in this cache. The saved item will be the most recently used
* item and won't be evicted soon. If an item with the same key already exists
......
......@@ -181,6 +181,7 @@
"chrome://resources/js/analytics.js",
"common/js/async_util.js",
"common/js/file_type.js",
"common/js/lru_cache.js",
"common/js/metrics_base.js",
"common/js/metrics_events.js",
"common/js/metrics.js",
......
......@@ -28,6 +28,7 @@
<include name="IDR_FILE_MANAGER_METRICS_BASE_JS" file="file_manager/common/js/metrics_base.js" flattenhtml="false" type="BINDATA" />
<include name="IDR_FILE_MANAGER_METRICS_JS" file="file_manager/common/js/metrics.js" flattenhtml="false" type="BINDATA" />
<include name="IDR_FILE_MANAGER_METRICS_EVENTS_JS" file="file_manager/common/js/metrics_events.js" flattenhtml="false" type="BINDATA" />
<include name="IDR_FILE_MANAGER_LRU_CACHE_JS" file="file_manager/common/js/lru_cache.js" flattenhtml="false" type="BINDATA" />
<!-- Scripts working in background page. -->
<include name="IDR_FILE_MANAGER_DEVICE_APP_WINDOW_WRAPPER_JS" file="file_manager/background/js/app_window_wrapper.js" flattenhtml="false" type="BINDATA" />
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment