Подтвердить что ты не робот

Использование библиотеки "kue" с redis-backed в node.js - почему использование redis-памяти увеличивается?

В приложении node.js я использую библиотеку очередей kue, которая поддерживается redis. Когда задание завершено, я удаляю его из очереди. После запуска около 70 000 заданий в ночное время использование памяти redis составляет около 30 МБ. В базе данных осталось 18 неудачных заданий, а длина очереди в настоящее время равна нулю - задания обрабатываются быстрее, чем они находятся в очереди. Redis не используется каким-либо другим способом.

Любые идеи, почему использование памяти redis увеличивается, даже если я удаляю завершенные задания? Кофейный код:

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
4b9b3361

Ответ 1

На самом деле проблема была в более старой версии node. Обновление до цепочки 0.6.x позволило решить проблемы с потреблением памяти.

Ответ 2

Когда у вас проблема с потреблением памяти в системе очередей, и вы на 100% уверены, что все поставленные в очередь элементы были удалены из хранилища и не сидят в очереди исключений/ошибок, то наиболее вероятной причиной является факт, что скорость очередей намного выше, чем скорость декомпрессии.

Redis использует распределитель памяти общего назначения (jemalloc, ptmalloc, tcmalloc и т.д.). Эти распределители не обязательно возвращают память системе. Когда некоторая память освобождается, распределитель имеет тенденцию сохранять ее (для повторного использования для будущего распределения). Это особенно верно, когда многие мелкие объекты распределены случайным образом, что обычно происходит с Redis.

В результате пик потребления памяти в данный момент времени заставит Redis накапливать память и сохранять ее. Эта память не потеряна, она будет использоваться повторно, если произойдет другой пик потребления памяти. Но с системной точки зрения память по-прежнему назначается Redis. Для системы очередей, если вы ставите в очередь элементы быстрее, чем вы можете их деактивировать, у вас будет такой пик в потреблении памяти.

Моим советом было бы помочь вашему приложению получить и зарегистрировать длину очереди с регулярными интервалами времени, чтобы проверить эволюцию количества элементов в очереди (и определить пиковое значение).

Обновлено:

Я проверил несколько вещей с кией, чтобы понять, что он хранит в Redis. На самом деле структура данных довольно сложна (сочетание строк, наборов, zsets и хешей). Если вы посмотрите на Redis, вы найдете следующее:

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)

Я не знаю Coffeescript вообще, поэтому я попытался воспроизвести проблему, используя простой старый 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);

Я запустил этот код, проверив, что осталось в Redis после обработки:

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"

Итак, похоже, что завершенные задания хранятся в q: jobs: complete и q: jobs: X: complete, несмотря на то, что задания были удалены. Я предлагаю вам проверить мощность этих zsets в вашем собственном экземпляре Redis.

Мое объяснение - управление этими zset происходит после того, как исходит "завершенное" событие. Таким образом, задания корректно удаляются, но их идентификаторы вставляются в эти zsets сразу после.

Обходной путь заключается в том, чтобы не полагаться на события для каждого задания, а скорее использовать события в очереди для удаления заданий. Например, можно выполнить следующие изменения:

// 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();
}

После исправления программы содержимое в Redis намного лучше:

redis 127.0.0.1:6379> keys *
1) "q:stats:work-time"
2) "q:ids"
3) "q:job:types"

Возможно, вы используете аналогичную стратегию из Coffescript.

Ответ 3

Рад видеть, что вы исправили свою проблему. В любом случае, в следующий раз, когда у вас возникла проблема с памятью Redis, ваш первый порт вызова должен быть командой "INFO" redis. Эта команда сообщит вам ценную информацию, такую ​​как

Память

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

или

пространство ключей

DB0: клавиши = 282, истекает = 27, avg_ttl = 11335089640

Это очень удобно для понимания состояния вашей памяти и пространства ключей в любой момент.