Cuando se tiene un problema de consumo de memoria con un sistema de colas, y que esté 100% seguro de que todos los elementos en cola se han eliminado de la tienda y no sentarse en una cola de excepción/error, entonces la causa más probable es el hecho de que la tasa de espera es mucho más alta que la tasa de eliminación de secuencias.
Redis utiliza un asignador de memoria de propósito general (jemalloc, ptmalloc, tcmalloc, etc.). Estos asignadores no necesariamente devuelven la memoria al sistema. Cuando se libera algo de memoria, el asignador tiende a mantenerlo (para reutilizarlo para una asignación futura). Esto es especialmente cierto cuando se asignan aleatoriamente muchos objetos pequeños, que suele ser el caso de Redis.
La consecuencia es un pico de consumo de memoria en un momento determinado hará que Redis acumule memoria y la conserve. Esta memoria no se pierde, se reutilizará si se produce otro pico de consumo de memoria. Pero desde el punto de vista del sistema, la memoria aún está asignada a Redis. Para un sistema de colas, si coloca los artículos en cola más rápido de lo que puede dequearlos, tendrá un pico en el consumo de memoria.
Mi consejo sería instrumentar su aplicación para buscar y registrar la longitud de la cola en intervalos de tiempo regulares para verificar la evolución del número de elementos en la cola (e identificar el valor pico).
Actualizado:
He probado algunas cosas con Kue para entender lo almacena en Redis. En realidad, la estructura de datos es bastante compleja (una combinación de cadenas, conjuntos, zsets y hash). Si nos fijamos en Redis, se encuentra el siguiente:
q:job:nnn (hash, job definition and properties)
q:search:object:nnn (set, metaphone tokens associated to job nnn)
q:search:word:XXXXX (set, reverse index to support job full-text indexing)
q:jobs:inactive (zset, all the unprocessed jobs)
q:jobs:X:inactive (zset, all the unprocessed jobs of job type X)
q:jobs:active (zset, all the on-going jobs)
q:jobs:X:active (zset, all the on-going jobs of job type X)
q:jobs:complete (zset, all the completed jobs)
q:jobs:X:complete (zset, all the completed jobs of job type X)
q:jobs:failed (zset, all the failed jobs)
q:jobs:X:failed (zset, all the failed jobs of job type X)
q:jobs:delayed (zset, all the delayed jobs)
q:jobs:X:delayed (zset, all the delayed jobs of job type X)
q:job:types (set, all the job types)
q:jobs (zset, all the jobs)
q:stats:work-time (string, work time statistic)
q:ids (string, job id sequence)
no sé CoffeeScript en absoluto, por lo que trató de reproducir el problema utilizando el viejo y simple Javascript:
var kue = require('kue'),
jobs = kue.createQueue();
jobs.process('email', function(job,done) {
console.log('Processing email '+JSON.stringify(job))
done();
});
function create_email(i) {
var j = jobs.create('email', {
title: 'This is email '+i
, to: 'didier'
, template: 'Bla bla bla'
});
j.on('complete', function() {
console.log('complete email job #%d', j.id);
j.remove(function(err){
if (err) throw err;
console.log('removed completed job #%d', j.id);
});
});
j.save();
}
for (i=0; i<5; ++i)
{
create_email(i);
}
kue.app.listen(8080);
me encontré con este código, comprobando lo que quedaba en Redis después del procesamiento:
redis 127.0.0.1:6379> keys *
1) "q:ids"
2) "q:jobs:complete"
3) "q:jobs:email:complete"
4) "q:stats:work-time"
5) "q:job:types"
redis 127.0.0.1:6379> zrange q:jobs:complete 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
puestos de trabajo por lo que parece completarse se mantienen en q: empleos: completas e q: puestos de trabajo: X: completa a pesar de los trabajos se han eliminado. Te sugiero que compruebes la cardinalidad de estos zsets en tu propia instancia de Redis.
Mi explicación es la gestión de estos zset ocurre después de se emite el evento 'completado'. Por lo tanto, los trabajos se eliminan correctamente, pero sus identificadores se insertan en esos zsets justo después.
Una solución alternativa es evitar confiar en los eventos por trabajo, sino utilizar los eventos por cola para eliminar los trabajos.Por ejemplo, las siguientes modificaciones se pueden hacer:
// added this
jobs.on('job complete', function(id) {
console.log('Job complete '+id)
kue.Job.get(id, function(err, job) {
if (err) return;
job.remove(function(err){
if (err) throw err;
console.log('removed completed job #%d', job.id);
});
});
});
// updated that
function create_email(i) {
var j = jobs.create('email', {
title: 'This is email '+i
, to: 'didier'
, template: 'Bla bla bla'
});
j.save();
}
Después de fijar el programa, el contenido en Redis es mucho mejor:
redis 127.0.0.1:6379> keys *
1) "q:stats:work-time"
2) "q:ids"
3) "q:job:types"
es probable que pueda utilizar una estrategia similar de coffeescript.
Hola Didier, gracias por esta respuesta, pero no creo que identifique el problema. Debería haber mencionado que la longitud de la cola es cero. Es fácil controlar el estado de la cola con esta biblioteca kue; incluye un servidor de administración incorporado y una interfaz. Editaré mi pregunta para agregar esta información. – mainsocial
He actualizado mi respuesta en consecuencia. –
Didier - gracias por su investigación. Independientemente llegué a la misma conclusión. De hecho, hubo algunos errores en el código kue que pude solucionar. Revisé las correcciones de mi tenedor e hice una solicitud de extracción. – mainsocial