Как мусор памяти собирается в движке приложения (python) при итерации по результатам БД

У меня есть некоторый код, который перебирает сущности БД и выполняет задачу - см. Ниже.

На движке приложения я получаю Exceeded soft private memory limit ошибка, да и вообще проверка memory_usage().current() подтверждает проблему. Смотрите ниже для вывода из оператора регистрации. Кажется, что каждый раз при получении пакета foos память увеличивается.

У меня вопрос: почему память не собирается мусором? Я ожидаю, что в каждой итерации циклов (while петля, и for цикл, соответственно) повторное использование имени foos и foo приведет к объектам, к которым foos а также foo используемый для указания будет "разыменованным" (то есть станет недоступным) и, следовательно, получит право на сборку мусора, а затем будет собираться мусором, когда память будет загружена. Но, видимо, этого не происходит.

from google.appengine.api.runtime import memory_usage

batch_size = 10
dict_of_results = {}
results = 0
cursor = None

while True:
  foos = models.Foo.all().filter('status =', 6)
  if cursor:
     foos.with_cursor(cursor)

  for foo in foos.run(batch_size = batch_size):

     logging.debug('on result #{} used memory of {}'.format(results, memory_usage().current()))
     results +=1

     bar  = some_module.get_bar(foo)

     if bar:
        try:
           dict_of_results[bar.baz] += 1
        except KeyError:
           dict_of_results[bar.baz] = 1


     if results >= batch_size:
        cursor = foos.cursor()
        break

  else:
     break   

и в some_module.py

def get_bar(foo):

  for bar in foo.bars:
    if bar.status == 10:
       return bar

  return None  

Вывод logging.debug (сокращенный)

on result #1 used memory of 43
on result #2 used memory of 43
.....
on result #20 used memory of 43
on result #21 used memory of 49
.....
on result #32 used memory of 49
on result #33 used memory of 54
.....
on result #44 used memory of 54
on result #45 used memory of 59
.....
on result #55 used memory of 59
.....
.....
.....

on result #597 used memory of 284.3
Exceeded soft private memory limit of 256 MB with 313 MB after servicing 1 requests total

2 ответа

Решение

Похоже, что ваше пакетное решение вступает в конфликт с пакетной обработкой базы данных, что приводит к большому количеству дополнительных партий.

Когда ты бежишь query.run(batch_size=batch_size), db выполнит запрос до завершения всего лимита. Когда вы достигнете конца партии, db возьмет следующую партию. Однако сразу после того, как db сделает это, вы выходите из цикла и начинаете снова. Это означает, что партии 1 -> n будут существовать в памяти дважды. Один раз для получения последних запросов, один раз для получения следующих запросов.

Если вы хотите перебрать все ваши сущности, просто позвольте db обрабатывать пакет:

foos = models.Foo.all().filter('status =', 6)
for foo in foos.run(batch_size = batch_size):
  results +=1
  bar  = some_module.get_bar(foo)
  if bar:
    try:
      dict_of_results[bar.baz] += 1
    except KeyError:
      dict_of_results[bar.baz] = 1

Или, если вы хотите обрабатывать пакет самостоятельно, убедитесь, что db не выполняет пакетирование:

while True:
  foo_query = models.Foo.all().filter('status =', 6)
  if cursor:
    foo_query.with_cursor(cursor)
  foos = foo_query.fetch(limit=batch_size)
  if not foos:
    break

  cursor = foos.cursor()

Вы можете смотреть не в ту сторону.

Взгляните на эти вопросы и ответы, чтобы узнать о подходах к проверке сборки мусора и о возможных альтернативных объяснениях: использование памяти для запросов к БД Google App Engine

Другие вопросы по тегам