Classe Javascript de Estratégia de Cache Básica

Recentemente, fui solicitado a armazenar em cache alguns dados usando memcached em node.js. Tudo foi fácil de configurar e o cache estava funcionando muito bem com o cluster de servidores memcached em que estava desenvolvendo. E como parte do teste do aplicativo, forçamos o desligamento de uma única instância do memcached no cluster para ver como o aplicativo se adapta. Infelizmente, observamos tempos de resposta inaceitavelmente negativos para o futuro. Mais tarde, descobri que havia um bug na biblioteca do memcached que estávamos usando. Isso já foi corrigido, mas mesmo depois que o bug foi resolvido, estávamos vendo algumas das solicitações em cache que foram forçadas a fechar penduradas e aguardando uma resposta. Determinamos que provavelmente era devido ao fato de o soquete TCP nunca receber a mensagem FIN do servidor memcached,

De qualquer forma, precisávamos de uma estratégia que protegesse o cliente de tempos de resposta elevados. Chega minha classe BasicCachingStrategy. Ele nos permite definir uma API padrão para caching get / set simples com a imposição de um tempo limite.

O código é documentado e provavelmente fará um trabalho melhor explicando o que faz do que eu. 😉

  • Ao substituir essa classe, você sempre deve substituir os métodos de protótipo gete set.
  • Ao usar esta classe, chame timeout_gete timeout_set. Isso impõe um tempo limite nos métodos gete set.
  • Esta classe está pronta para AMD / RequireJS / NodeJS / navegador

https://gist.github.com/mrlannigan/6314235

'use strict';

/**
* Basic Caching Strategy Class file

* @author Julian Lannigan <julian@jlconsulting.co>

* @since 22AUG2013

*/


(function () {
/**
* Timeout Error Class

*
@param {String} msg Timeout error message
* @extends {Error}

*/

function TimeoutError(msg) {
Error.captureStackTrace && Error.captureStackTrace(this, this);
this.message = msg || 'Error';
}
TimeoutError.super_ = Error;
TimeoutError.prototype = Object.create(Error.prototype, {
constructor: {value: TimeoutError, enumerable: false},
name
: {value: 'Timeout Error'}
});

/**
* Basic Caching Strategy

*/

function BasicCachingStrategy() {}

/**
* Default timeout length (ms)

* @type {Number}

*/

BasicCachingStrategy.prototype.timeout = 5000;

/**
* Extension of timeout property as a class property

* @type {Number}

*/

Object.defineProperty(BasicCachingStrategy, 'timeout', {
configurable
: false,
enumerable
: true,
writable
: true,
value
: BasicCachingStrategy.prototype.timeout
});

/**
* Extension of parent class for error class to use if a timeout occurs

* @type {Error}

*/

Object.defineProperty(BasicCachingStrategy, 'TimeoutError', {
configurable
: false,
enumerable
: true,
writable
: true,
value
: TimeoutError
});

/**
* Default `always miss` caching function (should always be overridden)

*
@param {String} key Key for cache
*
@param {Function} callback Callback (err, cached?, cachedValue)
* @return {BasicCachingStrategy}

*/

BasicCachingStrategy.prototype.get = function (key, callback) {
callback
(null, false, null);
return this;
};

/**
* Default `always not cached` caching function (should always be overridden)

*
@param {String} key Key for cache
*
@param {Mixed} value Value to store
*
@param {Function} callback Callback (err, cached?)
* @return {BasicCachingStrategy}

*/

BasicCachingStrategy.prototype.set = function (key, value, callback) {
callback
(null, false);
return this;
};

/**
* Wrapper method for `get` with the addition of a timeout

*

* If you are writing a library to use this object, you should always

* call the timeout version of the applicable function. Override at

* your own risk.

*

*
@param {String} key Key for cache
*
@param {Function} callback Callback (err, cached?, cachedValue)
* @return {BasicCachingStrategy}

*/

BasicCachingStrategy.prototype.timeout_get = function (key, callback) {
var self = this,
timeout
,
called
= false;

timeout
= setTimeout(function () {
called
= true;
callback
(new BasicCachingStrategy.TimeoutError('reached during get'), false, null);
}, this.timeout);

this.get(key, function () {
clearTimeout
(timeout);
if (called) { return; }
callback
.apply(self, arguments);
});

return this;
};

/**
* Wrapper method for `set` with the addition of a timeout

*

* If you are writing a library to use this object, you should always

* call the timeout version of the applicable function. Override at

* your own risk.

*

*
@param {String} key Key for cache
*
@param {Mixed} value Value to store
*
@param {Function} callback Callback (err, cached?)
* @return {BasicCachingStrategy}

*/

BasicCachingStrategy.prototype.timeout_set = function (key, value, callback) {
var self = this,
timeout
,
called
= false;

timeout
= setTimeout(function () {
called
= true;
callback
(new BasicCachingStrategy.TimeoutError('reached during set'), false);
}, this.timeout);

this.set(key, value, function () {
clearTimeout
(timeout);
if (called) { return; }
callback
.apply(self, arguments);
});

return this;
};

// AMD / RequireJS
if (typeof define !== 'undefined' && define.amd) {
define
([], function () {
return BasicCachingStrategy;
});
}
// Node.js
else if (typeof module !== 'undefined' && module.exports) {
module
.exports = BasicCachingStrategy;
}
// included directly via <script> tag
else {
this.BasicCachingStrategy = BasicCachingStrategy;
}

})();