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
get
eset
. - Ao usar esta classe, chame
timeout_get
etimeout_set
. Isso impõe um tempo limite nos métodosget
eset
. - 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;
}
})();