2012-01-16 21 views
13

En una aplicación node.js estoy usando la biblioteca de cola kue, que está respaldada por redis. Cuando un trabajo está completo lo retiro de la cola. Después de ejecutar aproximadamente 70,000 trabajos durante la noche, el uso de la memoria redis es de aproximadamente 30MB. Todavía hay 18 trabajos fallidos en la base de datos, y la longitud de la cola actualmente es cero: los trabajos se procesan más rápidamente de lo que están haciendo cola. Redis no está siendo utilizado de ninguna otra manera.Usando la biblioteca "kue" redis-backed en node.js - ¿por qué mi uso de memoria redis sigue aumentando?

¿Alguna idea de por qué el uso de la memoria redis sigue aumentando a pesar de que estoy eliminando los trabajos completados? código CoffeeScript:

gaemodel.update = (params) -> 
    job = jobs.create "gaemodel-update", params 
    job.attempts 2 
    job.save() 
    job.on "complete", -> 
    job.remove (err) -> 
     throw err if err 
     console.log 'completed job #%d', job.id 

Respuesta

0

De hecho, el problema era con una versión anterior del nodo. Actualización a los problemas de consumo de memoria resueltos de la cadena 0.6.x.

19

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.

+0

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

+0

He actualizado mi respuesta en consecuencia. –

+0

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

2

Me alegro de verte arregló tu problema. En cualquier caso, la próxima vez que tenga un problema de memoria con Redis, su primer puerto de llamada debería ser el comando "INFO" de redis. Este comando le dirá información valiosa, como

memoria

used_memory: 3223928 used_memory_human: 3.07M used_memory_rss: 1916928 used_memory_peak: 3512536 used_memory_peak_human: 3.35m used_memory_lua: 37888 mem_fragmentation_ratio: 0,59

O

Keyspace

db0: keys = 282, caduca = 27, avg_ttl = 11335089640

Lo cual es muy útil para entender el estado de la memoria y el espacio de teclas en cualquier momento.

Cuestiones relacionadas