2017-01-01 2 views
7

У меня есть задача сельдерея на Heroku, которая подключается к внешнему API и извлекает некоторые данные, хранит в базе данных и повторяется несколько сотен раз. Очень быстро (после ~ 10 циклов) Heroku начинает предупреждать о высоком использовании памяти. Есть идеи?Задача Django Celery на Heroku вызывает высокий уровень использования памяти

tasks.py

@app.task 
def retrieve_details(): 
    for p in PObj.objects.filter(some_condition=True): 
     p.fetch() 

models.py

def fetch(self): 
    v_data = self.service.getV(**dict(
     Number=self.v.number 
    )) 
    response = self.map_response(v_data) 

    for key in ["some_key","some_other_key",]: 
     setattr(self.v, key, response.get(key)) 

    self.v.save() 

Heroky журналы

2017-01-01 10:26:25.634 
132 <45>1 2017-01-01T10:26:25.457411+00:00 heroku run.5891 - - Error R14 (Memory quota exceeded) 

Go to the log: https://api.heroku.com/myapps/[email protected]/addons/logentries 

You are receiving this email because your Logentries alarm "Memory quota exceeded" 
has been triggered. 

In context: 
2017-01-01 10:26:25.568 131 <45>1 2017-01-01T10:26:25.457354+00:00 heroku run.5891 - - Process running mem=595M(116.2%) 
2017-01-01 10:26:25.634 132 <45>1 2017-01-01T10:26:25.457411+00:00 heroku run.5891 - - Error R14 (Memory quota exceeded) 
+0

Не могли бы вы выслать тела методов для методов, называемых 'fetch'? – 2ps

ответ

1

Чтобы развернуть настоящие мысли rdegges, вот две стратегии, которые я использовал в прошлом при работе с celery/python, чтобы уменьшить объем памяти: (1) отлаживать подзадачи, чтобы каждый процесс обрабатывал ровно один объект и/или (2) использовать генераторы.

  1. удар от подзадач, что каждый процесс в точности один объект:

    @app.task 
    def retrieve_details(): 
        qs = PObj.objects.filter(some_condition=True) 
        for p in qs.values_list('id', flat=True): 
         do_fetch.delay(p) 
    
    @app.task 
    def do_fetch(n_id): 
        p = PObj.objects.get(id=n_id) 
        p.fetch() 
    

    Теперь вы можете настроить сельдерей так, что он убивает процессы после обработки N количества PObj (задачи), чтобы сохранить память след низкое использования --max-tasks-per-child.

  2. Использование генераторов: Вы также можете попробовать это с помощью генераторов, так что вы можете (теоретически) выбросить PObj после вызова выборки

    def ps_of_interest(chunk=10): 
        n = chunk 
        start = 0 
        while n == chunk: 
         some_ps = list(PObj.objects.filter(some_condition=True)[start:start + n]) 
         n = len(some_ps) 
         start += chunk 
         for p in some_ps: 
          yield p 
    
    @app.task 
    def retrieve_details(): 
        for p in ps_of_interest(): 
         p.fetch() 
    

За мои деньги, я бы с опцией # 1.

6

Ты BASICA lly загружает кучу данных в словарь Python в памяти. Это вызовет много накладных расходов на память, особенно если вы захватываете много объектов из локальной базы данных.

Вам действительно нужно хранить все эти объекты в словаре?

То, что большинство людей делать вещи, как это:

  • Получить один объект в то время, из базы данных.
  • Обработайте этот предмет (выполните любую логику, в которой вы нуждаетесь).
  • Повторите.

Таким образом, вы сохраняете только один объект в памяти в любой момент времени, тем самым значительно уменьшая объем памяти.

Если бы я был вами, я бы искал способы переместить мою логику в запрос базы данных или просто обработать каждый элемент по отдельности.