two-buckets-memcache
Version:
Memcache that trades a simplified expiry strategy for a super low resource consumption
209 lines (129 loc) • 4.17 kB
JavaScript
'use strict';
var uuid = require('uuid/v4');
var Bucket = require('./bucket.js');
var Timeout = require('./timeout.js');
function TwoBucketsMemcache(expireAfter) {
this._expireAfter = expireAfter;
}
TwoBucketsMemcache.prototype.set = function (key, value) {
if (this._destroyed) {
throw new Error('Cache is destroyed');
}
this._createActiveBucketIfNotExisting();
this._activeBucket.set(key, value);
};
TwoBucketsMemcache.prototype.has = function (key) {
return this._activeBucket && this._activeBucket.has(key) || this._retiredBucket && this._retiredBucket.has(key) || false;
};
TwoBucketsMemcache.prototype.get = function (key) {
if (this._activeBucket) {
try {
return this._activeBucket.get(key);
} catch (e) {
if (!this._retiredBucket) {
throw new Error('Cache contains no entry for this key');
}
return this._retiredBucket.get(key);
}
} else {
if (!this._retiredBucket) {
throw new Error('Cache contains no entry for this key');
}
return this._retiredBucket.get(key);
}
};
TwoBucketsMemcache.prototype.remove = function (key) {
if (this._activeBucket) {
this._activeBucket.remove(key);
}
if (this._retiredBucket) {
this._retiredBucket.remove(key);
}
};
TwoBucketsMemcache.prototype.destroy = function () {
if (this._timeout) {
clearTimeout(this._timeout);
delete this._timeout;
}
delete this._activeBucket;
delete this._retiredBucket;
this._destroyed = true;
};
TwoBucketsMemcache.prototype._createActiveBucketIfNotExisting = function () {
if (this._activeBucket) {
return;
}
this._activeBucket = new Bucket();
this._setTimeoutIfNotRunning();
};
TwoBucketsMemcache.prototype._retireBuckets = function () {
delete this._timeout; // ...since it just got fired.
if (this._activeBucket) {
this.notifyPurge();
this._retiredBucket = this._activeBucket; // Also deletes retired bucket
delete this._activeBucket;
this._setTimeoutIfNotRunning();
} else {
this.notifyPurge();
delete this._retiredBucket;
}
};
TwoBucketsMemcache.prototype._setTimeoutIfNotRunning = function (expireAfter) {
if (this._timeout) {
return;
}
if (!expireAfter && expireAfter !== 0) {
expireAfter = this._expireAfter;
}
this._timeout = new Timeout(this._retireBuckets.bind(this), expireAfter);
};
TwoBucketsMemcache.prototype.changeExpireAfter = function (expireAfter) {
if (this._destroyed) {
throw new Error('Cache is destroyed');
}
var spedUp = expireAfter < this._expireAfter;
this._expireAfter = expireAfter;
if (!spedUp || !this._timeout) {
return;
}
var timeElapsed = this._timeout.getTimeElapsed();
var timeRemaining = expireAfter - timeElapsed;
if (timeRemaining < 0) {
timeRemaining = 0;
}
this._timeout.clear();
delete this._timeout;
this._setTimeoutIfNotRunning(timeRemaining);
};
TwoBucketsMemcache.prototype.listenPurge = function (callback) {
if (!this._listenersRetirement) {
this._listenersRetirement = [];
}
var listenerId = uuid();
this._listenersRetirement.push([callback, listenerId]);
return listenerId;
};
TwoBucketsMemcache.prototype.unlistenPurge = function (listenerId) {
if (!this._listenersRetirement) {
return;
}
for ( var i = 0; i < this._listenersRetirement.length; i+=1 ) {
if (this._listenersRetirement[i][1] === listenerId) {
this._listenersRetirement.splice(i, 1);
break;
}
}
};
TwoBucketsMemcache.prototype.notifyPurge = function () {
if (!this._listenersRetirement) {
return;
}
for ( var i = 0; i < this._listenersRetirement.length; i+=1 ) {
try {
this._listenersRetirement[i][0](this._retiredBucket.toArray());
} catch (err) {
// Ignore
}
}
};
module.exports = TwoBucketsMemcache;