Skip to content

Commit

Permalink
add LRU caching to XHRBlobs, providing a big speedup to both BAM and …
Browse files Browse the repository at this point in the history
…BigWig backends. woot!
  • Loading branch information
rbuels committed Oct 10, 2012
1 parent 09bc39c commit a8aaf7f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 23 deletions.
43 changes: 34 additions & 9 deletions src/JBrowse/Model/XHRBlob.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
define( [ 'dojo/_base/declare',
'JBrowse/Model/FileBlob'
'JBrowse/Model/FileBlob',
'JBrowse/Store/LRUCache'
],
function( declare, FileBlob ) {
function( declare, FileBlob, LRUCache ) {
var XHRBlob = declare( FileBlob,
/**
* @lends JBrowse.Model.XHRBlob.prototype
Expand Down Expand Up @@ -32,6 +33,12 @@ var XHRBlob = declare( FileBlob,
this.end = end;
}
this.opts = opts;

this.cache = opts.cache
|| new LRUCache({
fillCallback: dojo.hitch(this, '_fetch')
});
this.opts.cache = this.cache;
},

slice: function(s, l) {
Expand All @@ -49,7 +56,25 @@ var XHRBlob = declare( FileBlob,
return new XHRBlob(this.url, ns, ne, this.opts);
},

fetch: function(callback, attempt, truncatedLength) {
fetch: function( callback ) {
var url = this.url,
end = this.end,
start = this.start;

var request = {
url: url,
end: end,
start: start,
toString: function() {
return url+" (bytes "+start+".."+end+")";
}
};

// note that the cache has `_fetch` configured as its fill callback
this.cache.get( request, callback );
},

_fetch: function( request, callback, attempt, truncatedLength) {
var thisB = this;

attempt = attempt || 1;
Expand All @@ -60,12 +85,12 @@ var XHRBlob = declare( FileBlob,

var req = new XMLHttpRequest();
var length;
req.open('GET', this.url, true);
req.open('GET', request.url, true);
if( req.overrideMimeType )
req.overrideMimeType('text/plain; charset=x-user-defined');
if (this.end) {
req.setRequestHeader('Range', 'bytes=' + this.start + '-' + this.end);
length = this.end - this.start + 1;
if (request.end) {
req.setRequestHeader('Range', 'bytes=' + request.start + '-' + request.end);
length = request.end - request.start + 1;
}
req.responseType = 'arraybuffer';
req.onreadystatechange = function() {
Expand All @@ -88,7 +113,7 @@ var XHRBlob = declare( FileBlob,
try{
var r = req.responseText;
if (length && length != r.length && (!truncatedLength || r.length != truncatedLength)) {
return thisB.fetch( callback, attempt + 1, r.length );
return thisB._fetch( request, callback, attempt + 1, r.length );
} else {
return callback.call( thisB, thisB._stringToBuffer(req.responseText) );
}
Expand All @@ -98,7 +123,7 @@ var XHRBlob = declare( FileBlob,
}
}
} else {
return thisB.fetch(callback, attempt + 1);
return thisB._fetch( request, callback, attempt + 1);
}
}
return null;
Expand Down
58 changes: 44 additions & 14 deletions src/JBrowse/Store/LRUCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ return declare( null,
*/
constructor: function( args ) {
this.fill = args.fillCallback;
this.maxSize = args.maxSize || 100000;
this.maxSize = args.maxSize || 1000000;

this._size = args.sizeFunction || this._size;
this._keyString = args.keyFunction || this._keyString;
Expand All @@ -36,20 +36,34 @@ return declare( null,
this._cacheNewest = null;
},

get: function( inKey ) {
_log: function() {
//console.log.apply( console, arguments );
},

get: function( inKey, callback ) {
var key = this._keyString( inKey );

var record = this._cacheByKey[ key ];
if( !record ) {
// call our fill callback if necessary
this._log( 'cache miss', key );

if( this.fill ) {
var val = this.fill( inKey );
if( val )
return this.set( inKey, val );
this.fill( inKey, dojo.hitch(this, function( value ) {
if( value ) {
this.set( inKey, value );
}
callback( value );
}));
}
return null;
else {
callback( null );
}
return;
}

this._log( 'cache hit', key, record.value );

// take it out of the linked list
if( record.prev )
record.prev.next = record.next;
Expand All @@ -63,14 +77,15 @@ return declare( null,
this._cacheNewest.next = record;
this._cacheNewest = record;

return record.value;
callback( record.value );
},

set: function( inKey, value ) {
var key = this._keyString( inKey );
if( this._cacheByKey[key] ) {
return this.get( inKey );
return;
}
this._log( 'cache fill', key, value );

// make a cache record for it
var record = {
Expand All @@ -83,11 +98,12 @@ return declare( null,
this._prune( record.size );

// put it in the byKey structure
this._cacheByKey[key] = value;
this._cacheByKey[key] = record;

// put it in the doubly-linked list
record.prev = this._cacheNewest;
this._cacheNewest.next = record;
if( this._cacheNewest )
this._cacheNewest.next = record;
this._cacheNewest = record;
if( ! this._cacheOldest )
this._cacheOldest = record;
Expand All @@ -96,7 +112,7 @@ return declare( null,
this.size += record.size;
this.itemCount++;

return value;
return;
},

_keyString: function( key ) {
Expand All @@ -110,9 +126,23 @@ return declare( null,
},

_size: function( value ) {
var type = typeof key;
if( typeof value == 'object' ) {
throw 'not implemented';
var type = typeof value;
if( type == 'object' ) {
var sizeType = typeof value.size;
if( sizeType == 'number' ) {
return sizeType;
}
else if( sizeType == 'function' ) {
return value.size();
} else {
var sum = 0;
for( var k in value ) {
if( value.hasOwnProperty( k ) ) {
sum += this._size( value[k] );
}
}
}
return sum;
} else {
return 1;
}
Expand Down

0 comments on commit a8aaf7f

Please sign in to comment.